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设备的引用计数。