编写与一个USB
设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到
USB
子系统中,稍后再使用制造商和设备标识来判断是否安装了硬件。当然,这些制造商和设备标识需要我们编写进
USB
驱动程序中。
USB
驱动程序依然遵循设备模型
——
总线、设备、驱动。和
I2C
总线设备驱动编写一样,所有的
USB
驱动程序都必须创建的主要结构体是
struct usb_driver
,它们向
USB
核心代码描述了
USB
驱动程序。但这是个外壳,只是实现设备和总线的挂接,具体的
USB
设备是什么样的,如何实现的,比如一个字符设备,我们还需填写相应的文件操作接口 ,下面我们从外到里进行剖析,学习如何搭建这样的一个
USB
驱动外壳框架:
一、注册USB
驱动程序
Linux
的设备驱动,特别是这种
hotplug
的
USB
设备驱动,会被编译成模块,然后在需要时挂在到内核。所以
USB
驱动和注册与正常的模块注册、卸载是一样的,下面是
USB
驱动的注册与卸载:
[cpp] view plain copy
1. static int __init usb_skel_init(void)
2. {
3. int result;
4. /* register this driver with the USB subsystem */
5. result = usb_register(&skel_driver);
6. if (result)
7. err("usb_register failed. Error number %d", result);
8.
9. return result;
10. }
11.
12. static void __exit usb_skel_exit(void)
13. {
14. /* deregister this driver with the USB subsystem */
15. usb_deregister(&skel_driver);
16. }
17.
18. module_init (usb_skel_init);
19. module_exit (usb_skel_exit);
20. MODULE_LICENSE("GPL");
USB
设备驱动的模块加载函数通用的方法是在
I2C
设备驱动的模块加载函数中使用
usb_register
(
struct *usb_driver
)函数添加
usb_driver
的工作
,
而在模块卸载函数中利用
usb_deregister
(
struct *usb_driver
)做相反的工作。 对比
I2C
设备驱动中的
i2c_add_driver(&i2c_driver)
与
i2c_del_driver(&i2c_driver)
。
struct usb_driver
是
USB
设备驱动
,
我们需要实现其成员函数:
[cpp] view plain copy
1. static struct usb_driver skel_driver = {
2. .owner = THIS_MODULE,
3. .name = "skeleton",
4. .id_table = skel_table,
5. .probe = skel_probe,
6. .disconnect = skel_disconnect,
7. };
从代码看来,usb_driver
需要初始化五个字段:
模块的所有者 THIS_MODULE
模块的名字 skeleton
probe
函数
skel_probe
disconnect
函数
skel_disconnect
id_table
最重要的当然是probe
函数与
disconnect
函数
,
这个在后面详细介绍,先谈一下
id_table
:
id_table
是
struct usb_device_id
类型,包含了一列该驱动程序可以支持的所有不同类型的
USB
设备。如果没有设置该变量,
USB
驱动程序中的探测回调该函数将不会被调用。对比
I2C
中
struct i2c_device_id *id_table
,一个驱动程序可以对应多个设备,
i2c
示例:
[cpp] view plain copy
1. static const struct i2c_device_id mpu6050_id[] = {
2. { "mpu6050", 0},
3. {}
4. };
usb
子系统通过设备的
production ID
和
vendor ID
的组合或者设备的
class
、
subclass
跟
protocol
的组合来识别设备,并调用相关的驱动程序作处理。我们可以看看这个
id_table
到底是什么东西:
[cpp] view plain copy
1. static struct usb_device_id skel_table [] = {
2. { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
3. { } /* Terminating entry */
4. };
5.
6. MODULE_DEVICE_TABLE (usb, skel_table);
MODULE_DEVICE_TABLE
的第一个参数是设备的类型,如果是
USB
设备,那自然是
usb
。后面一个参数是设备表,这个设备表的最后一个元素是空的,用于标识结束。代码定义了
USB_SKEL_VENDOR_ID
是
0xfff0
,
USB_SKEL_PRODUCT_ID
是
0xfff0
,也就是说,当有一个设备接到集线器时,
usb
子系统就会检查这个设备的
vendor ID
和
product ID
,如果它们的值是
0xfff0
时,那么子系统就会调用这个
skeleton
模块作为设备的驱动。
当USB
设备接到
USB
控制器接口时,
usb_core
就检测该设备的一些信息,例如生产厂商
ID
和产品的
ID
,或者是设备所属的
class
、
subclass
跟
protocol
,以便确定应该调用哪一个驱动处理该设备。
我们下面所要做的就是对probe
函数与
disconnect
函数的填充了,但是在对
probe
函数与
disconnect
函数填充之前,有必要先学习三个重要的数据结构,这在我们后面
probe
函数与
disconnect
函数中有很大的作用:
二、USB
驱动程序中重要数据结构
1、usb-skeleton
usb-skeleton
是一个局部结构体,用于与端点进行通信。下面先看一下
Linux
内核源码中的一个
usb-skeleton
(就是
usb
驱动的骨架咯),其定义的设备结构体就叫做
usb-skel
:
[cpp] view plain copy
1. struct usb_skel {
2. struct usb_device *udev; /* the usb device for this device */
3. struct usb_interface *interface; /* the interface for this device */
4. struct semaphore limit_sem; /* limiting the number of writes in progress */
5. unsigned char *bulk_in_buffer; /* the buffer to receive data */
6. size_t bulk_in_size; /* the size of the receive buffer */
7. __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
8. __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
9. struct kref kref;
10. };
全部回复(1 )
只看楼主 我来说两句 抢板凳