嵌入式Linux USB驱动开发之教你一步步编写USB驱动程序
duyiwuer11
duyiwuer11 Lv.5
2017年11月03日 10:18:57
只看楼主

编写与一个USB 设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到 USB 子系统中,稍后再使用制造商和设备标识来判断是否安装了硬件。当然,这些制造商和设备标识需要我们编写进 USB 驱动程序中。 USB 驱动程序依然遵循设备模型 —— 总线、设备、驱动。和 I2C 总线设备驱动编写一样,所有的 USB 驱动程序都必须创建的主要结构体是

编写与一个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 驱动程序中重要数据结构
1usb-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. };


免费打赏

相关推荐

APP内打开