在Linux内核的源码中提供了USB设备驱动的框架代码:usb-skeleton.c
我们自己的驱动程序可借助这个框架代码,做些修改就能生成我们自己的驱动程序。
首先从USB设备与Linux驱动的匹配说起,当一个USB设备连接到Linux USB总线之后,USB主机控制器会使用USB设备的0号端点EPO对USB查询及设置,用以获取USB设备中提供的相关信息,然后根据这些信息,来决定加载那些驱动程序等。其中比较关键的USB设备的信息是:
idVendor 厂商ID这个是需要统一申请的,例如Cypress为0x04B4
idProduct 产品ID这个是厂家自己给产品分配的ID,例如CY68013为0x8613
由这两个信息我们得到一个唯一的设备ID标示,这个ID标示会和驱动程序中通过MODULE_DEVICE_TABLE(usb, xxxx_table)这个宏导出的
idVendor和
idProduct进行匹配,如果匹配成功则加载相应的驱动,并调用驱动的probe函数,进行相关处理如加载固件等。
可以通过USBFS文件系统来查看连接到系统USB总线上的USB设备的各种信息,USBFS文件系统的挂载命令为:
mount -t usbfs none /proc/bus/usb
挂载成功之后可以在/proc/bus/usb/devices文件中查看连接到USB总线上的设备的信息以及与之匹配的驱动的名称等信息
在USB设备驱动中通过USB_DEVICE(VENDOR_ID, PRODUCT_ID)这个宏将指定的
VENDOR_ID和
PRODUCT_ID赋值到struct usb_device_id结构体中的
idVendor和
idProduct元素。
然后在通过MODULE_DEVICE_TABLE这个宏将驱动名称和唯一标示的ID导出,
这个语句创建一个局部变量称为 __mod_usb_device_table, 它指向 struct usb_device_id 的列表. 稍后在内核建立过程中, depmod 程序在所有的模块中寻找 __mod_usb_device_table. 如果找到这个符号, 它将数据拉出模块并且添加到文件 /lib/modules/KERNEL_VERSION/modules.usbmap. 在 depmod 完成后, 所有的被内核中的模块支持的 USB 设备被列出, 带有它们的模块名子, 在那个文件中. 当内核告知热插拔系统有新的 USB 设备已找到, 热插拔系统使用 moudles.usbmap 文件来找到正确的驱动来加载,并把设备的usb_device_id指针作为probe函数的第二个参数传递。
例:CY68013的struct usb_device_id,该结构中的driver_info可以保存一个结构体的指针,用来保存设备的相关信息如加载什么固件,使用那个设置等等
- 28 /* Define these values to match your devices */
- 29 #define CY68013_VENDOR_ID 0x04B4
- 30 #define CY68013_PRODUCT_ID 0x8613
- 31
- 32 #define CY_FIX_FW "cy_fix.hex"
- 33 static struct cy68013_info cy_info = {
- 34 .alt = 1,
- 35 .fwname = "cy_var.hex"
- 36 };
- 37
- 38 /* table of devices that work with this driver */
- 39 static const struct usb_device_id cy68013_table[] = {
- 40 { USB_DEVICE(CY68013_VENDOR_ID, CY68013_PRODUCT_ID),
- 41 .driver_info = (unsigned long)&cy_info },
- 42 { } /* Terminating entry */
- 43 };
- 44 MODULE_DEVICE_TABLE(usb, cy68013_table);