Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3808073
  • 博文数量: 197
  • 博客积分: 10086
  • 博客等级: 上将
  • 技术积分: 5145
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-13 10:50
文章分类

全部博文(197)

文章存档

2011年(2)

2009年(30)

2008年(165)

我的朋友

分类: LINUX

2008-05-05 09:49:21

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 {
3 .name ="skeleton",
4 .probe =skel_probe,
5 .disconnect =skel_disconnect,
6 .id_table =skel_table,
7 };

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

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

1  /* 本驱动支持的USB设备列表 */
2 static struct usb_device_id skel_table [] = {
3 { USB_DEVICE(USB_SKEL_VENDOR_ID,
4 USB_SKEL_PRODUCT_ID) },
5 { }
6 };
7 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 /* 告知用户设备依附于什么node */
71 info("USB Skeleton device now attached to USBSkel-%d", interface->minor);
72 return 0;
73
74 error:
75 if (dev)
76 kref_put(&dev->kref, skel_delete);
77 return retval;
78 }

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

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

1  struct usb_skel 
2 {
3 struct usb_device *udev;/* 该设备的usb_device指针 */
4 struct usb_interface *interface;/* 该设备的usb_interface指针 */
5 struct semaphorelimit_sem;/* 限制进程写的数量 */
6 unsigned char *bulk_in_buffer;/* 接收数据的缓冲区 */
7 size_tbulk_in_size;/* 接收缓冲区大小 */
8 _ _u8bulk_in_endpointAddr;/*批量IN端点的地址 */
9 _ _u8bulk_out_endpointAddr;/* 批量OUT端点的地址 */
10 struct krefkref;
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.19第62行的usb_register_dev(interface, &skel_class)中第二个参数包含了字符设备的file_operations结构体指针,而这个结构体中的成员实现也是USB字符设备 的另一个组成成分。代码清单20.22给出了USB骨架程序的字符设备文件操作file_operations结构体的定义。

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

1 static struct file_operations skel_fops = 
2 {
3 .owner =THIS_MODULE,
4 .read =skel_read, //读函数
5 .write =skel_write, //写函数
6 .open =skel_open, //打开函数
7 .release =skel_release, //释放函数
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 if (!interface) {
12 err ("%s - error, can't find device for minor %d",
13 _ _FUNCTION_ _, subminor);
14 retval = -ENODEV;
15 goto exit;
16 }
17
18 dev = usb_get_intfdata(interface);/*获得接口数据*/
19 if (!dev) {
20 retval = -ENODEV;
21 goto exit;
22 }
23
24 /* 增加设备的引用计数 */
25 kref_get(&dev->kref);
26
27 /* 将接口数据保存在file->private_data中 */
28 file->private_data = dev;
29
30 exit:
31 return retval;
32 }

由于在open()函数中并没有申请任何软件和硬件资源,因此与open()函数对应的release()函数不用进行资源的释放,它会减少在open()中增加的引用计数,如代码清单20.24所示。

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

1  static int skel_release(struct inode *inode, struct file *file)
2 {
3 struct usb_skel *dev;
4
5 dev = (struct usb_skel *)file->private_data;
6 if (dev == NULL)
7 return -ENODEV;
8
9 /* 减少设备的引用计数 */
10 kref_put(&dev->kref, skel_delete);
11 return 0;
12 }

接下来要分析的是读写函数,前面已经提到,在访问USB设备的时候,贯穿于其中的“中枢神经”是urb结构体。
在skel_write() 函数中进行的关于urb的操作与20.3.2小节的描述完全对应,即进行了urb的分配(调用usb_alloc_urb())、初始化(调用 usb_fill_bulk_urb())和提交(调用usb_submit_urb())的操作,如代码清单20.25所示。

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

1       static ssize_t skel_write
(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)
2 {
3 struct usb_skel *dev;
4 int retval = 0;
5 struct urb *urb = NULL;
6 char *buf = NULL;
7 size_t writesize = min(count, (size_t)MAX_TRANSFER);
8
9 dev = (struct usb_skel *)file->private_data;
10
11 /* 验证实际上有数据要写入USB设备 */
12 if (count == 0)
13 goto exit;
14
15 /* 限制urb的数量,防止一个用户用完所有的RAM */
16 if (down_interruptible(&dev->limit_sem)) {
17 retval = -ERESTARTSYS;
18 goto exit;
19 }
20
21 /* 创建urb、urb的缓冲区,将数据复制给urb */
22 urb = usb_alloc_urb(0, GFP_KERNEL);
23 if (!urb) {
24 retval = -ENOMEM;
25 goto error;
26 }
27
28 buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);
29 if (!buf) {
30 retval = -ENOMEM;
31 goto error;
32 }
33
34 if (copy_from_user(buf, user_buffer, writesize)) {
35 retval = -EFAULT;
36 goto error;
37 }
38
39 /* 恰当地初始化urb */
40 usb_fill_bulk_urb(urb, dev->udev,
41 usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
42 buf, writesize, skel_write_bulk_callback, dev);
43 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
44
45 /* 将数据发送到批量端口 */
46 retval = usb_submit_urb(urb, GFP_KERNEL);
47 if (retval) {
48 err("%s - failed submitting write urb, error %d", _ _FUNCTION_ _, retval);
49 goto error;
50 }
51
52 /* 释放对urb的引用 */
53 usb_free_urb(urb);
54
55 exit:
56 return writesize;
57
58 error:
59 usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
60 usb_free_urb(urb);
61 up(&dev->limit_sem);
62 return retval;
63 }

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

代码清单20.26  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 /* sync/async去除链路故障,不是错误 */
8 if (urb->status &&
9 !(urb->status == -ENOENT ||
10 urb->status == -ECONNRESET ||
11 urb->status == -ESHUTDOWN)) {
12 dbg("%s - nonzero write bulk status received: %d",
13 _ _FUNCTION_ _, urb->status);
14 }
15
16 /* 释放被分配的内存 */
17 usb_buffer_free(urb->dev, urb->transfer_buffer_length,
18 urb->transfer_buffer, urb->transfer_dma);
19 up(&dev->limit_sem);
20 }

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

代码清单20.27  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 }
阅读(1094) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~