当前位置:嗨网首页>书籍在线阅读

12-USB骨架程序

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

20.3.4 USB骨架程序

Linux内核源代码中的driver/usb/usb-skeleton.c文件为我们提供了一个最基础的USB驱动程序,即USB骨架程序,可被看做一个最简单的USB设备驱动实例。尽管具体USB设备驱动千差万别,但其骨架则万变不离其宗。

首先看看USB骨架程序的usb_driver结构体定义,如代码清单20.16所示。

代码清单20.16 USB骨架程序的usb_driver结构体

1 static struct usb_driver skel_driver = {

2 .name = "skeleton",

3 .probe = skel_probe,

4 .disconnect = skel_disconnect,

5 .suspend = skel_suspend,

6 .resume = skel_resume,

7 .pre_reset = skel_pre_reset,

8 .post_reset = skel_post_reset,

9 .id_table = skel_table,

10 .supports_autosuspend = 1,

11};

从上述代码第9行可以看出,它所支持的USB设备的列表数组为skel_table[],其定义如代码清单20.17所示。

代码清单20.17 USB骨架程序的id_table

1 static struct usb_device_id skel_table [] = {

2 { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },

3 { } / Terminating entry /

4 };

5 MODULE_DEVICE_TABLE(usb, skel_table);

对上述usb_driver的注册和注销发生在USB骨架程序的模块加载与卸载函数内,如代码清单20.18所示,其分别调用了usb_register()和usb_deregister()。

代码清单20.18 USB骨架程序的模块加载与卸载函数

1 static int __init usb_skel_init(void)

2 {

3 int result;

4

5 / 注册USB驱动 /

6 result = usb_register(&skel_driver);

7 if (result)

8 err("usb_register failed. Error number %d", result);

9

10 return result;

11 }

12 static void __exit usb_skel_exit(void)

13 {

14 / 注销USB驱动 /

15 usb_deregister(&skel_driver);

16 }

usb_driver的probe()成员函数中,会根据usb_interface的成员寻找第一个批量输入和输出端点,将端点地址、缓冲区等信息存入为USB骨架程序定义的usb_skel结构体,并将usb_skel实例的指针传入usb_set_intfdata()作为USB接口的私有数据,最后,它会注册USB设备,如代码清单20.19所示。

代码清单20.19 USB骨架程序的探测函数

1 static int skel_probe(struct usb_interface interface, const struct usb_device_id id)

2 {

3 struct usb_skel *dev = NULL;

4 struct usb_host_interface *iface_desc;

5 struct usb_endpoint_descriptor *endpoint;

6 size_t buffer_size;

7 int i;

8 int retval = -ENOMEM;

9

10 / 分配设备状态的内存并初始化 /

11 dev = kzalloc(sizeof(*dev), GFP_KERNEL);

12 if (dev == NULL) {

13 err("Out of memory");

14 goto error;

15 }

16 kref_init(&dev→kref);

17 sema_init(&dev→limit_sem, WRITES_IN_FLIGHT);

18

19 dev→udev = usb_get_dev(interface_to_usbdev(interface));

20 dev→interface = interface;

21

22 / 设置端点信息 /

23 / 仅使用第一个bulk-in和bulk-out /

24 iface_desc = interface→cur_altsetting;

25 for (i = 0; i < iface_desc→desc.bNumEndpoints; ++i) {

26 endpoint = &iface_desc→endpoint[i].desc;

27

28 if (!dev→bulk_in_endpointAddr &&

29 ((endpoint→bEndpointAddress & USB_ENDPOINT_DIR_MASK)

30 == USB_DIR_IN) &&

31 ((endpoint→bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

32 == USB_ENDPOINT_XFER_BULK)) {

33 / 找到了一个批量IN端点 /

34 buffer_size = le16_to_cpu(endpoint→wMaxPacketSize);

35 dev→bulk_in_size = buffer_size;

36 dev→bulk_in_endpointAddr = endpoint→bEndpointAddress;

37 dev→bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);

38 if (!dev→bulk_in_buffer) {

39 err("Could not allocate bulk_in_buffer");

40 goto error;

41 }

42 }

43

44 if (!dev→bulk_out_endpointAddr &&

45 ((endpoint→bEndpointAddress & USB_ENDPOINT_DIR_MASK)

46 == USB_DIR_OUT) &&

47 ((endpoint→bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

48 == USB_ENDPOINT_XFER_BULK)) {

49 / 找到了一个批量OUT端点 /

50 dev→bulk_out_endpointAddr = endpoint→bEndpointAddress;

51 }

52 }

53 if (!(dev→bulk_in_endpointAddr && dev→bulk_out_endpointAddr)) {

54 err("Could not find both bulk-in and bulk-out endpoints");

55 goto error;

56 }

57

58 / 在设备结构中保存数据指针 /

59 usb_set_intfdata(interface, dev);

60

61 / 注册USB设备 /

62 retval = usb_register_dev(interface, &skel_class);

63 if (retval) {

64 / something prevented us from registering this driver /

65 err("Not able to get a minor for this device.");

66 usb_set_intfdata(interface, NULL);

67 goto error;

68 }

69

70 ...

71 }

usb_skel结构体可以被看作一个私有数据结构体,其定义如代码清单20.20所示,应该根据具体的设备量身定制。

代码清单20.20 USB骨架程序的自定义数据结构usb_skel

1 struct usb_skel {

2 struct usb_device udev; / 该设备的usb_device指针 */

3 struct usb_interface interface; / 该设备的usb_interface指针 */

4 struct semaphore limit_sem; / 限制进程写的数量 /

5 unsigned char bulk_in_buffer; / 接收数据的缓冲区 */

6 size_t bulk_in_size; / 接收缓冲区大小 /

7 _ _u8 bulk_in_endpointAddr; /批量IN端点的地址 /

8 _ _u8 bulk_out_endpointAddr; / 批量OUT端点的地址 /

9 ...

10 struct mutex io_mutex;

11 };

USB骨架程序的断开函数会完成探测函数相反的工作,即设置接口数据为NULL,注销USB设备,如代码清单20.21所示。

代码清单20.21 USB骨架程序的断开函数

1 static void skel_disconnect(struct usb_interface *interface)

2 {

3 struct usb_skel *dev;

4 int minor = interface→minor;

5

6 / 阻止skel_open()与skel_disconnect()的竞争 /

7 lock_kernel();

8

9 dev = usb_get_intfdata(interface);

10 usb_set_intfdata(interface, NULL);

11

12 / 注销usb设备,释放次设备号 /

13 usb_deregister_dev(interface, &skel_class);

14

15 unlock_kernel();

16

17 / 减少引用计数 /

18 kref_put(&dev→kref, skel_delete);

19

20 info("USB Skeleton #%d now disconnected", minor);

21 }

代码清单20.18第62行的usb_register_dev(interface, &skel_class)中第二个参数包含了字符设备的file_operations结构体指针,而这个结构体中的成员实现也是USB字符设备的另一个组成成分。代码清单20.22给出了USB骨架程序的字符设备文件操作file_operations结构体的定义。

代码清单20.22 USB骨架程序的字符设备文件操作结构体

1 static const struct file_operations skel_fops = {

2 .owner = THIS_MODULE,

3 .read = skel_read,

4 .write = skel_write,

5 .open = skel_open,

6 .release = skel_release,

7 .flush = skel_flush,

8 };

由于只是一个象征性的骨架程序,open()成员函数的实现非常简单,它根据usb_driver和次设备号通过usb_find_interface()获得USB接口,之后通过usb_get_intfdata()获得接口的私有数据并赋予file→private_data,如代码清单20.23所示。

代码清单20.23 USB骨架程序的字符设备打开函数

1 static int skel_open(struct inode inode, struct file file)

2 {

3 struct usb_skel *dev;

4 struct usb_interface *interface;

5 int subminor;

6 int retval = 0;

7

8 subminor = iminor(inode);

9

10 interface = usb_find_interface(&skel_driver, subminor); /获得接口数据/

11 ...

12

13 dev = usb_get_intfdata(interface);

14 ...

15

16 kref_get(&dev→kref);

17

18 mutex_lock(&dev→io_mutex);

19

20 if (!dev→open_count++) {

21 retval = usb_autopm_get_interface(interface);

22 if (retval) {

23 dev→open_count--;

24 mutex_unlock(&dev→io_mutex);

25 kref_put(&dev→kref, skel_delete);

26 goto exit;

27 }

28 }

29

30 file→private_data = dev;/ 将接口数据保存在file→private_data中 /

31 mutex_unlock(&dev→io_mutex);

32 ...

33 }

由于在open()函数中并没有申请任何软件和硬件资源,因此与open()函数对应的release()函数不用进行资源的释放,它进行减少在open()中增加的引用计数等工作。

接下来要分析的是读写函数,前面已经提到,在访问USB设备的时候,贯穿于其中的“中枢神经”是urb结构体。

在skel_write()函数中进行的关于urb的操作与20.3.2小节的描述完全对应,即进行了urb的分配(调用usb_alloc_urb())、初始化(调用usb_fill_bulk_urb())和提交(调用usb_submit_urb())的操作,如代码清单20.24所示。

代码清单20.24 USB骨架程序的字符设备写函数

1 static ssize_t skel_write(struct file file, const char user_buffer, size_t count, loff_t *ppos)

2 {

3 ...

4 / 创建urb、urb的缓冲区,将数据复制给urb /

5 urb = usb_alloc_urb(0, GFP_KERNEL);

6 ...

7

8 buf = usb_buffer_alloc(dev→udev, writesize, GFP_KERNEL, &urb→transfer_dma);

9 ...

10

11 if (copy_from_user(buf, user_buffer, writesize)) {

12 retval = -EFAULT;

13 goto error;

14 }

15 ...

16

17 / 初始化urb /

18 usb_fill_bulk_urb(urb, dev→udev,

19 usb_sndbulkpipe(dev→udev, dev→bulk_out_endpointAddr),

20 buf, writesize, skel_write_bulk_callback, dev);

21 urb→transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

22 usb_anchor_urb(urb, &dev→submitted);

23

24 / 将数据发送到批量端口 /

25 retval = usb_submit_urb(urb, GFP_KERNEL);

26 ...

27

28 / 释放对urb的引用,USB将最终完全释放之 /

29 usb_free_urb(urb);

30

31 return writesize;

32

33 ...

34 }

写函数中发起的urb结束后,其完成函数skel_write_bulk_callback()将被调用,它会进行urb→status的判断,如代码清单20.25所示。

代码清单20.25 USB骨架程序的字符设备写操作完成函数

1 static void skel_write_bulk_callback(struct urb urb, struct pt_regs regs)

2 {

3 struct usb_skel *dev;

4

5 dev = (struct usb_skel *)urb→context;

6

7 ...

8

9 / 释放被分配的内存 /

10 usb_buffer_free(urb→dev, urb→transfer_buffer_length,

11 urb→transfer_buffer, urb→transfer_dma);

12 up(&dev→limit_sem);

13 }

USB骨架程序的字符设备读函数并没有进行类似写函数的一系列针对urb的操作,而是简单地调用usb_bulk_msg()发起一次同步urb传输操作,如代码清单20.26所示。

代码清单20.26 USB骨架程序的字符设备读函数

1 static ssize_t skel_read(struct file file, char buffer, size_t count, loff_t *ppos)

2 {

3 struct usb_skel *dev;

4 int retval = 0;

5 int bytes_read;

6

7 dev = (struct usb_skel *)file→private_data;

8

9 / 从设备进行一次阻塞的批量读 /

10 retval = usb_bulk_msg(dev→udev,

11 usb_rcvbulkpipe(dev→udev, dev→bulk_in_endpointAddr),

12 dev→bulk_in_buffer,

13 min(dev→bulk_in_size, count),

14 &bytes_read, 10000);

15

16 / 如果读成功,将数据复制到用户空间 /

17 if (!retval) {

18 if (copy_to_user(buffer, dev→bulk_in_buffer, bytes_read))

19 retval = -EFAULT;

20 else

21 retval = bytes_read;

22 }

23

24 return retval;

25 }