在Linux 内核中,使用usb_driver 结构体描述一个USB 设备驱动,usb_driver 结构体的定义如代码清单20.11 所示。
代码清单20.11 usb_driver 结构体
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); /* I/O 控制函数 */ int (*suspend) (struct usb_interface *intf, pm_message_t message);/*挂起函数*/ int (*resume) (struct usb_interface *intf); /* 恢复函数 */ int (*reset_resume)(struct usb_interface *intf); void (*pre_reset) (struct usb_interface *intf); void (*post_reset) (struct usb_interface *intf); const struct usb_device_id *id_table;/* usb_device_id 表指针 */ struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; unsigned int soft_unbind:1; };
在编写新的USB 设备驱动时,主要应该完成的工作是probe()和disconnect()函数,即探测和断开函数,它们分别在设备被插入和拔出的时候被调用,用于初始化和释放软硬件资源。对usb_driver 的注册和注销通过这两个函数完成:
int usb_register(struct usb_driver *new_driver)
void usb_deregister(struct usb_driver *driver);
usb_driver 结构体中的id_table 成员描述了这个USB 驱动所支持的USB 设备列表,它指向一个usb_device_id 数组,usb_device_id 结构体用于包含USB 设备的制造商ID、产品ID、产品版本、设备类、接口类等信息及其要匹配标志成员match_flags(标明要与哪些成员匹配,包含DEV_LO、DEV_HI、DEV_CLASS、DEV_SUBCLASS、DEV_PROTOCOL、INT_CLASS、INT_SUBCLASS、INT_PROTOCOL)。可以借助下面一组宏来生成usb_device_id 结构体的实例:
USB_DEVICE(vendor, product)
该宏根据制造商ID 和产品ID 生成一个usb_device_id 结构体的实例,在数组中增加该元素将意味着该驱动可支持匹配制造商ID、产品ID 的设备。
USB_DEVICE_VER(vendor, product, lo, hi)
该宏根据制造商ID、产品ID、产品版本的最小值和最大值生成一个usb_device_id 结构体的实例,在数组中增加该元素将意味着该驱动可支持匹配制造商ID、产品ID 和lo~hi 范围内版本的设备。
USB_DEVICE_INFO(class, subclass, protocol)
该宏用于创建一个匹配设备指定类型的usb_device_id 结构体实例。
USB_INTERFACE_INFO(class, subclass, protocol)
该宏用于创建一个匹配接口指定类型的usb_device_id 结构体实例。
当USB 核心检测到某个设备的属性和某个驱动程序的usb_device_id 结构体所携带的信息一致时,这个驱动程序的probe()函数就被执行。拔掉设备或者卸掉驱动模块后,USB 核心就执行disconnect()函数来响应这个动作。
上述usb_driver 结构体中的函数是USB 设备驱动中USB 相关的部分,而USB 只是一个总线,真正的USB 设备驱动的主体工作仍然是USB 设备本身所属类型的驱动,如字符设备、tty 设备、块设备、输入设备等。因此USB 设备驱动包含其作为总线上挂在设备的驱动和本身所属设备类型的驱动两部分。
与platform_driver 类似,usb_driver 起到了“牵线”的作用,即在probe()里注册相应的字符、tty 等设备,在disconnect()注销相应的字符、tty 等设备,而原先对设备的注册和注销一般直接发生在模块加载和卸载函数中。
尽管USB 本身所属设备驱动的结构与其不挂在USB 总线上时完全相同,但是在访问方式上却发生了很大的变化,例如,对于USB 接口的字符设备而言,尽管仍然是write()、read()、ioctl()这些函数,但是在这些函数中,贯穿始终的是称为URB 的USB 请求块。
如图20.4 所示,在这棵树里,我们把树根比作主机控制器,树叶比作具体的USB 设备,树干和树枝就是USB 总线。树叶本身与树枝通过usb_driver 连接,而树叶本身的驱动(读写、控制)则需要通过其树叶设备本身所属类设备驱动来完成。树根和树叶之间的“通信”依靠在树干和树枝里“流淌”的URB 来完成。
由此可见,usb_driver 本身只是起到了找到USB 设备、管理USB 设备连接和断开的作用,也就是说,它是公司入口处的“打卡机”,可以获得员工(USB 设备)的上/下班情况。树叶和员工一样,可以是研发工程师也可以是销售工程师,而作为USB 设备的树叶可以是字符树叶、网络树叶或块树叶,因此必须实现相应设备类的驱动。