linux usb驱动书籍,好文共享-转载)Linux USB驱动程序基础

Linux USB Driver Basics

Introduction

Drivers are software components that operating systems use to provide

hardware specific services to applications. This webpage attempts to document

the basics of USB drivers on Linux. The goal here is to provide you with a basic

understanding of how USB device drivers on Linux work.

The File Abstraction

Linux, like other Unix derived operating systems, tries to make applications

simpler by providing a common hardware abstraction, the File. Essentially,

interactions with almost all hardware can be abstracted into the same interface

that the operating system provides for manipulating files. Hence, you can "Open"

a driver, "Read" a driver, "Write" to a driver and "Close" a driver. This

abstraction extends all the way into the application, who can use the same

system calls that it uses to open and manipulate files to talk to hardware.

The way this works is that the kernel creates nodes in the file system,

typically located in /dev that represent a particular interface to a driver. To

talk to a particular driver an application will open the /dev entry associated

with that driver. The file descriptor returned by that open is passed to all

future system calls (read, write, select), and is eventually passed to

close.

Besides drivers, Unix also uses this file paradigm for various different

kinds of IPC, and even for socket communications over a network.

It is quite surprising how many completely different kinds of hardware can be

modeled with just 4 operations (open, close, read and write). That said, their

is another very important system call that unix applications developers commonly

use, select(). The select() system call allows applications to poll and

determine whether data could be read from, or written to a file descriptor

without blocking.

Sometimes a piece of hardware provides some functionality that doesn't fit

well into this file centric paradigm. To allow for this, unix applications

typically make use of the ioctl() system call. This call takes a numeric value

that is essentially an identifier for a specific piece of functionality in the

driver.

The Job of the Device Driver

Simply stated, it is the job of the driver to provide functions that the

kernel can use to implement this file programming paradigm. Applications do not

directly call functions in the driver, instead they call functions in libc that

eventually call into the kernel (via the system call interface). The kernel

implementations of these system calls call into your driver.

As you might expect, this means that you're going to be implementating open,

close, read and write inside your driver. To implement select(), the kernel

needs you to implement a method in your driver called poll(). Other than these,

their are only a handfull of other functions that need to be implemented, and

you have a driver!

Loading a Driver

Linux supports essentially two kinds of driver development models: 1)

Compiled into the kernel, 2) Dynamically loadable driver modules. If configured

properly, option 2 has the advantage that drivers can be loaded automatically by

the kernel only when they are actually needed to service an application request.

Option 1 presents a logically cleaner, and slightly faster scenario.

The choice is up to you. That said, we will now cover the basics of loading

and unloading a kernel module into a running kernel.

First, understand that a Linux kernel driver cannot be dynamically linked

against any librarys (not even libc, which many programmers dont even know they

are linking against). The only code external to your driver that your allowed to

make use of are functions that are implemented within the kernel. In fact,

kernel modules are never "Linked", instead they are relocatable object files,

.o's or sometimes .ko's.

The following command is used to install a driver into a running kernel:[td@objective ~]$ insmod driver.ko

The following command is used to show what drivers are currently loaded into

the kernel:[td@objective ~]$ lsmod

Module Size Used by

md5 4033 1

ipv6 263105 22

ipt_REJECT 5441 1

ipt_state 1857 5

ip_conntrack 41369 1 ipt_state

iptable_filter 2881 1

ip_tables 19521 3 ipt_REJECT,ipt_state,iptable_filter

video 15685 0

button 4033 0

battery 9285 0

ac 4805 0

uhci_hcd 35025 0

ehci_hcd 40013 0

hw_random 5973 0

i2c_i801 8653 0

i2c_core 21313 1 i2c_i801

snd_intel8x0 34177 0

snd_ac97_codec 74937 1 snd_intel8x0

snd_pcm_oss 50673 0

snd_mixer_oss 17729 1 snd_pcm_oss

snd_pcm 98889 3 snd_intel8x0,snd_ac97_codec

snd_timer 32837 1 snd_pcm

snd 57285 6 snd_intel8x0,snd_ac97_codec

soundcore 10785 1 snd

snd_page_alloc 9669 2 snd_intel8x0,snd_pcm

e1000 102573 0

dm_snapshot 17413 0

dm_zero 2113 0

dm_mirror 25645 0

ext3 130633 2

jbd 83161 1 ext3

dm_mod 57333 6 dm_snapshot,dm_zero,dm_mirror

ata_piix 9413 0

libata 46917 1 ata_piix

sd_mod 20289 0

scsi_mod 146313 2 libata,sd_mod

The following command is used to remove a driver from a running kernel:[td@objective ~]$ rmmod driver.ko

Since you never link your driver, you will not know about unresolved symbols

until you actually load your driver into the kernel. It is during the "insmod"

process that the kernel binds all of the calls to kernel functions in your code

to their actual current locations in memory. This process is called "resolving

symbol dependancies".

Understanding the Universal Serial Bus

From the highest level

As I'm sure you know from your own use of USB, the USB bus, besides offering

communications also offers power to devices connected to it. The USB can offer

up to 500mA for the devices connected to it. Devices that need more than this

can be self powered.

Besides this, your probably aware of the fact that the USB can be expanded

with hubs.

How hardware is about to make your life easier

Thankfully, your driver will not have to communicate directly on the

Universal Serial Bus. Instead, your driver will communicate to the USB host

controller, which will communicate on the bus on your behalf. The USB host

controller is a piece of hardware that acts as a focal point for all of the

CPU's interaction with the USB hardware. It hides most of the complexity of

dealing with USB and also protects the USB hardware from potentially badly

written driver software that might otherwise affect the whole bus.

Their are two kinds of host controller hardware commonly in use, UHCI

(Universal Host Controller Interface) and OHCI (Open Host Controller Interface).

The good news is that both of these host controllers present the same interface

to your driver, so you really dont have to care about what kind of host

controller hardware is present. As you might expect, UHCI & OHCI hardware

has its own driver, that thankfully you will not need to touch. It will be the

job of your driver to communicate (indirectly) with the host controller driver,

to configure, read from or write to devices on the USB.

Types of Communications on the USB

USB devices have the full range of different speed, latency and reliability

requirements. Things like mice and keyboard transfer only small amounts of data,

relatively rarely, but they care a lot about latency. No one like a slow

keyboard.

On the other hand, USB webcams often transfer compressed MPEG video, which is

extremely high bandwidth, but because MPEG was designed to be transmitted over

lossy communications channels, it is OK if the occasional packet is lost.

To support these differing requirements, USB supports a number of

communications types.

Control Transfers - Control transfers are used when you need

reliable (the data MUST get their) communications and you are sending a very

small amount of data. Essentially, these transfers are commands, and their are a

few commands that every device is required to support (GET_STATUS,

CLEAR_FEATURE, SET_FEATURE, SET_ADDRESS, GET_DESCRIPTOR, SET_DESCRIPTOR,

GET_CONFIGURATION, SET_CONFIGURATION, GET_INTERFACE, SET_INTERFACE,

SYNCH_FRAME).

Bulk Transfers - Bulk transfers are used when you need

reliable (the data MUST get their) communications and you are sending large

amounts of data.

Interrupt Transfers - Interrupt transfers are simmilar to

bulk transfers except they are configured to occur automatically at some

interval. These types of transfers are useful for devices that stream constant

data of the USB.

Isochronous Transfers - Isochronous transfers are very fast,

and have guaranteed bus bandwidth, but they have no reliability. This transfer

type seems to have been designed with MPEG in mind.

The code that you write is going to have to specify to the host controller

what types of communcations you want to use. Thankfully, an extremely wide range

of device types fit nicely into the above types of data transfers.

The heirarchical view of the USB

If you think about what I have described so far, it should be fairly apparent

that since you will be dealing only indirectly with actual USB devices and

instead be communicating through a host controller, that host controller must

have some kind of data model for the devices that are connected to it on the USB

bus. It must present some description of the devices connected, and their

capabilities.

The capabilites of a USB device are modeled heirarchically by the host

controller. Your driver will communicate to a USB device by making requests to

nodes of this heirarchy. To fully understand the reasoning for all of the layers

of the heirarchy I am about to tell you about please consider how generic a host

controller must be, given how wide ranging the functionality of USB hardware can

be.

When a USB is first plugged into the bus it is detected by the host

controller. The host controller sends it the GET_DESCRIPTOR command and

retrieves what is called the device descriptor. This structure is itself of a

heirarchical nature and describes the features and capabilities of a particular

device.

This "Device Descriptor" has 1 or more "Configuration Descriptors", which in

turn has 1 or more "Interfaces", which in turn has 0 to N "Endpoints". I know

this is a lot to swallow, but its the genericness and flexibility of this

structure that lets USB work for so many different types of devices.

uid-11114210-id-2906991.html

Remember, we're talking about 1 USB device here. Every device you have

connected to your bus has a structure like this. Simple devices may not have

multiple configurations or interfaces. Almost all devices will have at least 1

endpoint however, which means they will also have at least 1 interface and 1

configuration. The reason for this is that the endpoint is typically the part of

this tree that the application interacts with.

The Importance of Endpoints

Endpoints are logically the parts of this tree that you write to or read

from. If your goal is to read the mpeg coming out of a USB webcam, then you will

be reading that data from an Endpoint. We talked a little about communications

types above (remember Control, Bulk, Interrupt and Isochronous?) but a key to

understanding USB is to understand that Endpoints support a particular type of

communication. If you need to do bulk transfers to your device you'll need to

find an endpoint on your device that supports bulk transfers. If you dont find

it, then your out of luck. You cannot create endpoints, what you find represents

the capabilities that this particular USB hardware is presenting to the host

controller on your machine.

Endpoints can also be used as a secondary form of device identification. We

will discuss the primary form of device identification employed shortly, but for

now consider that you could identify a device by the presence of a particular

heirarchy of configurations, interfaces and endpoints. In fact, the presence of

the right number and types of nodes in this heirarchy is arguably the most

important thing you care about. If a device identifies itself to you as being of

a particular kind, that does you no good if the hardware doesn't support the

endpoints you expect.

Coding details

USB Identification

All USB devices have two very important numbers that serve as our primary

form of device identification. These numbers are the devices Vendor

ID and Product ID. The idea is that if a company would

mark all of the USB devices it creates with an id that identifies the company,

it should be really easy to determine if a particular device was made by your

company. Because a company might create many different kinds of USB devices,

their is a second number, the Product ID. This number can be used to identify a

particular product.

When a device is plugged into the USB bus, the kernel query's the installed

drivers to find out which supports this particular piece of hardware.

Essentially, the kernel calls a method in your driver called "probe". This

function is passed the vendor and product id's, as well as a structure used to

check for the availability of particular nodes in the devices USB heirarchy. If

everything checks out, your driver should eventually call usb_register_dev() to

let the kernel know that you do indeed handle this particular piece of

hardware.

How it all begins

Now that you know some of the basic fundamentals, it's time we discuss some

of the details in a little more depth. The first driver code that gets called by

the kernel is your module init routine. Likewise, when your driver is unloaded

(via rmmod), the last function that is called is your modules exit routine. It

is the job of your init function to register a struct with the kernel called

'usb_driver'. This struct has information like your drivers name, (indirectly

through another structure) the vendor and product id's of the usb hardware your

driver services, and a pointer to a couple of functions called probe and

disconnect (which we'll talk about shortly).#define USB_VENDOR_ID 0x22FF

#define USB_PRODUCT_ID_1 0x1111

#define USB_PRODUCT_ID_2 0x2222

static struct usb_device_id generic_usb_id_table [] =

{

{

USB_DEVICE(USB_VENDOR_ID,USB_PRODUCT_ID_1)

},

{

USB_DEVICE(USB_VENDOR_ID,USB_PRODUCT_ID_2)

}

{

}

};

MODULE_DEVICE_TABLE (usb, generic_usb_id_table);

static struct usb_driver generic_usb_driver =

{

.owner = THIS_MODULE,

.name = "generic_usb_driver",

.id_table = generic_usb_id_table,

.probe = generic_usb_probe,

.disconnect = generic_usb_disconnect

};

static int __init generic_usb_driver_init( void )

{

return usb_register( &generic_usb_driver );

}

static void __exit generic_usb_driver_exit( void )

{

usb_deregister( &generic_usb_driver )

}

module_init (generic_usb_driver_init);

module_exit (generic_usb_driver_exit);

Their is a bit of macro magic here at the end, but the basic idea is pretty

simple. We fill out a structure and we implement some standard entry points that

register and unregister that structure. What we have done at this point is told

the kernel what vendor and product id's we handle, and what functions it should

call when devices that match those id's are added or removed from the USB

bus.

Getting Probed by the kernel

When a device is plugged into the USB bus, the USB host controller hardware

generates an interrupt that wakes up the host controller driver. Eventually, the

kernel finds the usb driver associated with the vendor and product id of the new

device. It then calls that drivers probe method. When a device is removed, that

also generates an interrupt and eventually, a call to your drivers disconnect

function.

The drivers probe method is very important. First, consider that most USB

drivers dont really run all the time. They are called by the kernel when they

have work to do (such as reading and writing). Since it is usually possible for

more than 1 kind of the same USB device to be plugged in, drivers cannot use

global data to store information about their state. Instead they must declare

some type of structure, which they allocate and populate an instance of when

they are probe()'d by the kernel and they determine that they really do handle

the hardware in question. Theirfore, it is the job of your probe function to

examine the passed usb_interface structure to make sure it has all of the

endpoints you need to function. Once that determination has been made you should

kmalloc() your instance data structure, populate it (perhaps storing endpoint

addresses or pre-allocating URB's) and return it by calling usb_set_intfdata()

on the passed interface object. This instance data is passed back to you when

the kernel calls into your driver from then on.

Another critical thing you have to do in your probe function is call

usb_register_dev, passing a pointer to an instance of a usb_class_driver struct.

This struct (the second structure down) is populated with a number of important

fields.static struct file_operations generic_fops =

{

.owner = THIS_MODULE,

.read = generic_read,

.write = generic_write,

.poll = generic_poll,

.ioctl = generic_ioctl

};

static struct usb_class_driver generic_class =

{

.name = "generic_%d",

.fops = &generic_fops,

.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,

.minor_base = 42

};

The first structure above contains pointers to all of the functions used to

support the file paradigm for this driver (read, write, poll and ioctl). The

second structure contains a pointer to the first structure. It also contains a

field that is used by the kernel when it creates your device nodes under /dev.

The .name field of the usb_class_driver structure is used as a format string,

combined with a number, to uniquely identify all of the devices on the USB bus

that are serviced by your particular driver.

writestatic ssize_t generic_write( struct file* file,

const char __user* user_buffer,

size_t count,

loff_t* ppos );

Probably the first thing you'll want to do in your write function is access

the instance data for this device that you allocated in probe(). You do this by

looking at the "private_data" field of the struct file* passed to the function.

Basically, an URB is the unit of communication on the USB bus. Writing or

reading from a device on the USB consists of creating and submitting properly

constructed URBs. Given that, the next thing you need to do is call

usb_alloc_urb() to create the urb for this write operation. Next, you need to

create a kernel memory buffer, attach it to the URB, copy the write data from

user space into your kernel buffer, finalize the initialization of the URB and

finally call usb_submit_urb() to send it.char* buf = NULL;

struct urb* urb = usb_alloc_urb( 0, GFP_KERNEL );

buf = usb_buffer_alloc( dev->udev, count, GFP_KERNEL, &urb->transfer_dma );

if( buf )

{

copy_from_user( buf, user_buffer, count );

usb_fill_bulk_urb( urb, dev->udev,

usb_sndbulkpipe(dev->udev,dev->bulk_out_endpointAddr),

buf,

count,

generic_write_bulk_callback,

dev );

usb_submit_urb( urb, GFP_KERNEL );

usb_free_urb( urb );

}

Not the best code in the world, but shows you the steps of what your going to

have to do.

read

Read is a lot simpler than write. Again, the first thing you'll probably want

to do is get your instance data from the private_data field of the passed file*

structure. Once thats done, you can get to the fun stuff:usb_bulk_msg( dev->udev,

usb_recvbulkpipe( dev->udev, dev->bulk_in_endpointAddr),

dev->bulk_in_buffer,

min(dev->bulk_in_size,count),

&count, HZ*2 );

copy_to_user( buffer, dev->bulk_in_buffer, count );

The heart of your read() implementation may be as simple as this.

ioctl

ioctl is the back door. Essentially, ioctl exists because sometimes not every

piece of hardware can operate exactly like a file in all cases. Sometimes, you

just need to request a specific piece of functionality be performed on your

behalf in the driver. You'd also like to be able to pass parameters to such a

function, and be returned some kind of indication of the success of such a

call.static int generic_reader_ioctl( struct inode* inode,

struct file* file,

unsigned in cmd,

unsigned long arg )

{

struct usb_generic_instance_data* dev =

(struct usb_generic_instance_data*)file->private_data;

switch( cmd )

{

case GET_STATE:

return dev->global_state;

break;

default:

break;

}

return -1;

}

The above is a complete ioctl() function that implements a single logical

function, GET_STATE. Any number of additional function can be added here simply

by adding cases to the switch statement.

exit(0);This article is a bit longer than I had hoped it would be. The

goal was a short introduction that would give you a place to start in your

understanding of the Linux USB drivers. I hope I have done at least that much!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值