分类: LINUX
2010-08-31 19:45:58
Linux内核支持两种主要类型的USB驱动程序:宿主(host)系统上的驱动程序和设备(device)上的驱动程序(为了避免混淆,一般称为USB gadget drivers)。
USB驱动程序存在于不同的内核子系统(块设备,网络设备,字符设备等等)和USB硬件控制器之中。USB核心为USB驱动程序提供了一个用于访问和控制USB硬件的接口,而不必考虑系统当前存在的各种不同类型的USB硬件控制器。
USB设备是非常复杂的,详细需要参考相关文档。
在USB设备的逻辑组织中,包含设备、配置、接口和端点4个层次,它们的关系可以简单的描述如下:
(1) 设备通常具有一个或更多的配置
(2) 配置通常具有一个或更多接口
(3) 接口通常具有一个或更多的设置
(4) 接口没有或者具有一个以上的端点。
在linux内核中,使用usb_driver结构体描述一个USB设备驱动,定义如下
/**
* struct usb_driver - identifies USB interface driver to usbcore
* @name: The driver name should be unique among USB drivers,名字应该唯一
* and should normally be the same as the module name.并且应与模块名字一样
* @probe: Called to see if the driver is willing to manage a particular
* interface on a device. If it is, probe returns zero and uses
* usb_set_intfdata() to associate driver-specific data with the
* interface. It may also use usb_set_interface() to specify the
* appropriate altsetting. If unwilling to manage the interface,
* return -ENODEV, if genuine IO errors occured, an appropriate
* negative errno value.
* @disconnect: Called when the interface is no longer accessible, usually
* because its device has been (or is being) disconnected or the
* driver module is being unloaded.
* @ioctl: Used for drivers that want to talk to userspace through
* the "usbfs" filesystem. This lets devices provide ways to
* expose information to user space regardless of where they
* do (or don't) show up otherwise in the filesystem.
* @suspend: Called when the device is going to be suspended by the system.
* @resume: Called when the device is being resumed by the system.
* @reset_resume: Called when the suspended device has been reset instead
* of being resumed.
* @pre_reset: Called by usb_reset_device() when the device
* is about to be reset.
* @post_reset: Called by usb_reset_device() after the device
* has been reset
* @id_table: USB drivers use ID table to support hotplugging.
* Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
* or your driver's probe function will never get called.
* @dynids: used internally to hold the list of dynamically added device
* ids for this driver.
* @drvwrap: Driver-model core structure wrapper.
* @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be
* added to this driver by preventing the sysfs file from being created.
* @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
* for interfaces bound to this driver.
* @soft_unbind: if set to 1, the USB core will not kill URBs and disable
* endpoints before calling the driver's disconnect method.
*
* USB interface drivers must provide a name, probe() and disconnect()
* methods, and an id_table. Other driver fields are optional.
*
* The id_table is used in hotplugging. It holds a set of descriptors,
* and specialized data may be associated with each entry. That table
* is used by both user and kernel mode hotplugging support.
*
* The probe() and disconnect() methods are called in a context where
* they can sleep, but they should avoid abusing the privilege. Most
* work to connect to a device should be done when the device is opened,
* and undone at the last close. The disconnect code needs to address
* concurrency issues with respect to open() and close() methods, as
* well as forcing all pending I/O requests to complete (by unlinking
* them as necessary, and blocking until the unlinks complete).
*/
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int soft_unbind:1;
}
这个结构体,我们最需要关心的只有probe(),disconnect()以及id_table。首先亮相的就是id_table了,还记得usb-skeleton.c这个万能驱动吗?也就是改得它的id_table,使得它能称为万能驱动的。
关于struct usb_device_id *id_table;的定义前,有这么一段注释
/*
* Device table entry for "new style" table-driven USB drivers.
* User mode code can read these tables to choose which modules to load.
* Declare the table as a MODULE_DEVICE_TABLE.
*
* A probe() parameter will point to a matching entry from this table.
* Use the driver_info field for each match to hold information tied
* to that match: device quirks, etc.
*
* Terminate the driver's table with an all-zeroes entry.
* Use the flag values to control which fields are compared.
*/
就是说只有满足了id_tables之后,才能去调用probe函数,它存在的意义就很清楚了,类似于身份证之类。
而probe()和disconnect()函数,即探测和断开函数,它们分别在设备被插入和拔出的时候被调用,用于初始化和释放硬件资源。
USB设备驱动包含其作为总线上挂载设备的驱动和本身所属设备类型的驱动2部分。
尽管USB本身所属设备驱动的结构与其不挂在USB总线上时完全相同,但是在访问方式上却发生了很大变化。与USB设备通信时不再是IO内存和IO端口的访问,而贯穿始终的是称为URB的USB请求块。
USB URB
Urb被用来以一种异步的方式往/从特定的USB设备上的特定USB端点发送/接收数据。它的使用和文件系统异步IO代码中的kiocb结构体以及网络代码中的struct skbuff很类似。USB设备驱动程序可能会为单个端点分配许多urb,也可能对许多不同的端点重用单个的urb,这取决于驱动程序的需要。设备中的每个端点都可以处理一个urb队列,所以多个urb可以在队列为空之前发送到同一个端点。一个urb的典型生命周期如下:
(1) USB设备驱动程序创建
(2) 分配给一个特定USB设备的特定端点
(3) 由USB设备驱动程序提交给USB核心
(4) 由USB核心提交到特定设备的特定USB主控制器驱动程序
(5) 由USB主控制器驱动程序处理,进行一次到USB设备的传送
(6) 当URB结束之后,USB主控制器驱动程序通知USB设备驱动程序
Urb可以在任何时刻被提交该urb的驱动程序取消掉,或者被USB核心取消,如果该设备已从系统中移除。Urb被动态地创建,它包含一个内部引用计数,使得它们可以在最后一个使用者释放它们时自动销毁。
创建和销毁URB
Struct urb 结构体不能在驱动程序中或者另一个结构体中静态地穿件,因为这样会破坏USB核心对urb所使用的引用计数机制。它必须使用usb_alloc_urb函数来创建。原型如下:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
第一个参数,iso_packets,是该urb应该包含的等时数据包的数量。如果不打算创建等时urb,该值应设置为0.第二个参数,mem_flags,和传递给用于从内核分配内存的kmalloc函数的标志有相同的类型。
当一个urb被创建之后,在它被使用之前必须初始化。
USB核心提供下列函数来完成urb的初始化
void usb_fill_[control | int | bulk]_urb(
struct urb *urb, // 须初始化的指针
struct usb_device *usb_dev, // 该urb所发送的目标USB设备
unsigned int pipe, // 目标USB设备的特定端点
unsigned char *setup_packet, // 指向即将被发送到端点的设置数据包的数据
void *transfer_buffer, // 缓存区指针,不能为静态缓存区,必须用kmalloc创建
int buffer_length, // 缓存区大小
usb_complete_t completion_fn,// urb结束后调用的指针
void *context,//指向一个小数据块,以便结束处理例程后面的查找
int interval, // 该urb被调度的间隔 中断专有
);
等时urb必须在驱动程序中“手工地”进行初始化。
提交urb
一旦urb被USB驱动程序正确地创建和初始化之后,就可以提交到USB核心以发送到USB设备了。这是通过调用usb_submit_urb函数来完成的。
int usb_submit_urb(struct urb *urb, int mem_flags);
urb参数是指向即将被发送到设备的urb的指针。mem_flags参数等同于传递给kmalloc调用的同一个参数。
当一个urb被成功地提交到USB核心之后,在接收函数被调用之前不能访问该urb结构体中的任何字段。因为usb_submit_urb函数可以在任何时刻调用(包括从一个中断上下文中),mem_flags变量的内容必须是正确的。其实只有三个有效的值可以被使用,取决于usb_submit_urb何时被调用:
GFP_ATOMIC
只要下列条件成立就应该使用该值:
(1) 调用者是在一个urb结束处理例程、中断处理例程、底半部、tasklet或者定时器回调函数中
(2) 调用者正持有一个自旋锁或读写锁。注意如果持有了信号量,该值就不需要了。
(3) Current->state不是TASK_RUNNING
GFP_NOIO
如果驱动程序处于块IO路径中应该使用该值。在所有存储类型的设备的错误处理路径中也应该使用它。
GFP_KERNEL
该值应该在前述类别之外的所有情况使用。
结束urb:结束回调处理例程
如果调用usb_submit_urb成功,把对urb的控制交给USB核心,该函数返回0;否则,返回负的错误号。如果函数调用成功,当urb结束的时候urb的结束处理例程正好被调用一次。当该函数被调用时,USB核心结束了对URB的处理,此刻对它的控制返回给设备驱动程序。只有三种结束urb和调用结束函数的情形:
(1) urb被成功发送到了设备,设备返回了正确的确认。对于OUT urb而言就是数据被成功发送,对于IN urb而言就是所请求的数据被成功的接收到。如果确实是这样,urb中的status变量被设置为0
(2) 发送数据到设备或者从设备接收数据时发生了某种错误。错误情况由urb结构体中的status变量的错误值来指示。
(3) Urb从USB核心中被解开链接。当驱动程序通过usb_unlink_urb或usb_kill_urb调用告诉USB核心取消一个已经提交的urb时,或者当设备从系统中被移除而一个urb已经提交给它时,会发生这种情况。