http://book.51cto.com/art/200912/169140.htm
《ARM嵌入式Linux系统开发详解》第25章USB驱动开发,本章讲解了Linux内核USB驱动体系结构、USB设备驱动结构等知识,并在最后给出了两个USB设备驱动开发实例。本节为大家介绍USB驱动程序框架。
25.2.4 USB驱动程序框架
Linux内核代码driver/usb/usb-skeleton.c文件是一个标准的USB设备驱动程序。编写一个USB设备的驱动可以参考usb-skeleton.c文件,实际上,可以直接修改该文件驱动新的USB设备。下面以usb-skeleton.c文件为例分析usb-skel设备驱动框架。
1.基本数据结构
usb-skel设备使用自定义结构usb_skel记录设备驱动用到的所有描述符,该结构定义如下:
- struct usb_skel {
- struct usb_device * udev; /* the usb device for
this device */
// USB设备描述符- struct usb_interface * interface; /* the
interface for this device
- */
// USB接口描述符- struct semaphore limit_sem; /* limiting
the number of writes in- progress */
// 互斥信号量- unsigned char * bulk_in_buffer; /* the
buffer to receive data */
// 数据接收缓冲区- size_t bulk_in_size; /* the size
of the receive buffer */
// 数据接收缓冲区大小- __u8 bulk_in_endpointAddr; /* the
address of the bulk in- endpoint */
// 入端点地址- __u8 bulk_out_endpointAddr; /* the
address of the bulk out endpoint */
// 出端点地址- struct kref kref;
- };
usb-skel设备驱动把usb_skel结构存放在了urb结构的context指针里。通过urb,设备的所有操作函数都可以访问到usb_skel结构。其中,limit_sem成员是一个信号量,当多个usb-skel类型的设备存在于系统中的时候,需要控制设备之间的数据同步。
2.驱动程序初始化和注销
与其他所有的Linux设备驱动程序一样,usb-skel驱动使用module_init()宏设置初始化函数,使用module_exit()宏设置注销函数。usb-skel驱动的初始化函数是usb_skel_init()函数,定义如下:
- static int __init usb_skel_init(void)
- {
- int result;
- /* register this driver with the USB subsystem */
- result = usb_register(&skel_driver); // 注册USB设备驱动
- if (result)
- err("usb_register failed. Error number %d", result);
- return result;
- }
usb_skel_init()函数调用内核提供的usb_register()函数注册了一个usb_driver类型的结构变量,该变量定义如下:
- static struct usb_driver skel_driver = {
- .name = "skeleton", // USB设备名称
- .probe = skel_probe, // USB设备初始化函数
- .disconnect = skel_disconnect, // USB设备注销函数
- .id_table = skel_table, // USB设备ID映射表
- };
skel_driver结构变量中,定义了usb-skel设备的名、设备初始化函数、设备注销函数和USB ID映射表。其中usb-skel设备的USB ID映射表定义如下:
- static struct usb_device_id skel_table [] = {
- { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
- { } /* Terminating entry */
- };
skel_table中只有一项,定义了一个默认的usb-skel设备的ID。其中,USB_SKEL_VENDOR_ID是USB设备的厂商ID,USB_SKEL_PRODUCT_ID是USB设备ID。
注销函数的操作比较简单,调用usb_deregister()函数注销usb-skel设备驱动,函数定义如下:
- static void __exit usb_skel_exit(void)
- {
- /* deregister this driver with the USB subsystem */
- usb_deregister(&skel_driver); // 注销USB设备
- }
3.设备初始化
从skel_driver结构可以知道usb-skel设备的初始化函数是skel_probe()函数。设备初始化主要是探测设备类型,分配USB设备用到的urb资源,注册USB设备操作函数等。skel_class结构变量记录了usb-skel设备信息,定义如下:
- static struct usb_class_driver skel_class = {
- .name = "skel%d", // 设备名称
- .fops = &skel_fops, // 设备操作函数
- .minor_base = USB_SKEL_MINOR_BASE,
- };
name变量使用%d通配符表示一个整型变量,当一个usb-skel类型的设备连接到USB总线后会按照子设备编号自动设置设备名称。fops是设备操作函数结构变量,定义如下:
- static struct file_operations skel_fops = {
- .owner = THIS_MODULE,
- .read = skel_read, // 读操作
- .write = skel_write, // 写操作
- .open = skel_open, // 打开操作
- .release = skel_release, // 关闭操作
- };
skel_ops定义了usb-skel设备的操作函数。当在usb-skel设备上发生相关事件时,USB文件系统会调用对应的函数处理。
4.设备注销
skel_disconnect()函数在注销设备的时候被调用,定义如下:
- static void skel_disconnect(struct usb_interface *interface)
- {
- struct usb_skel *dev;
- int minor = interface->minor;
- /* prevent skel_open() from racing skel_disconnect() */
- lock_kernel(); // 在操作之前加锁
- dev = usb_get_intfdata(interface); // 获得USB设备接口描述
- usb_set_intfdata(interface, NULL); // 设置USB设备接口描述无效
- /* give back our minor */
- usb_deregister_dev(interface, &skel_class); // 注销USB设备操作描述
- unlock_kernel(); // 操作完毕解锁
- /* decrement our usage count */
- kref_put(&dev->kref, skel_delete); // 减小引用计数
- info("USB Skeleton #%d now disconnected", minor);
- }
skel_disconnect()函数释放usb-skel设备用到的资源。首先获取USB设备接口描述,之后设置为无效;然后调用usb_deregister_dev()函数注销USB设备的操作描述符,注销操作本身需要加锁;注销设备描述符后,更新内核对usb-skel设备的引用计数。