频 道 直 达 - 新闻 - 培训 - 软件 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 读书 - 博客 - BBS
51CTO.COM_中国最大的网络技术网站
找资料:

20.3.4 USB骨架程序

作者: 宋宝华 编著 出处:人民邮电出版社  2008-03-11 09:45    砖    好    评论   进入论坛
阅读提示:《Linux设备驱动开发详解》本书全面而详细地讲解了Linux设备驱动开发中涉及的理论以及多种设备驱动的框架。第20章主要讲解从主机侧角度看到的USB主机控制器驱动和设备驱动。本文写的是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 {
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 }

【责任编辑:董书 TEL:(010)68476606】

回书目      
专题
Linux——从菜鸟到高手
Linux/Solaris服务器的安全配置
Linux 集群技术专题
Linux中文环境
Linux防火墙
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 最 大 的 网 络 技 术 网 站 ·
技 术 成 就 梦 想
订阅技术快讯
电子杂志下载
名称:SQL Server数据库管理精品黄皮书
简介:书中文章经过精挑细选,便于用户能根据自己的实际工作和学习,快速在本书寻找到相关资料。内容涵盖了SQL Server的安装与升级、语句查询、数据备份和恢复、自动化任务、数据同步、数据字典、安全和预防、性能和优化、集群等各方面应用信息,以及DBA管理人员在数据库管理工作中
名称:2007路由技术大全
简介:《2007路由技术大全》由51CTO.com网站特别策划制作,该书包括路由器技术、路由器产品、路由器配置、安全设置、路由器故障处理、路由器密码恢复,以及广大网友在实践使用中的心得经验和技巧文章,内容注重实用性,适用于初学者入门,也适合多年从业者提高,是一本实践和理论完
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
RAID——磁盘阵列基础
RAID——磁盘阵列基础
华为员工自杀频频拷问企业文化
华为员工自杀频频拷问企业文化
Linux——从菜鸟到高手
Linux——从菜鸟到高手
· Linux——从菜鸟到高手
· 微软Forefront企业安全..
· 如何优化IT 控制能耗
· 国际文档格式标准开战
· CISSP认证成长之路
· 珊瑚虫QQ作者侵权案开庭
· 微软出价446亿美元收购..
· Windows Server 2008专..
· 隐私保护技术探讨
· 贝恩资本携手华为22亿..
· 802.11n:下一代的无线..
· 体验Visual Studio 200..
· 运营商封堵非法ADSL共享
· ADSL应用面面俱到
· 龙芯要做中国的“奔腾”
· 华为七千人主动辞职规..
清除流氓软件——51CTO特别专题
清除流氓软件——51CTO特别专..
ARP攻击防范与解决方案
ARP攻击防范与解决方案
VPN技术
VPN技术
· VPN技术
· SQL Server 2008/2005..
· SOA 面向服务架构
· 子网掩码教程
· SQL Server 2008/2005..
· RAID——磁盘阵列基础
· 中间件应用技术专题
· 深入了解PGP加密技术
· MySQL数据库备份
· 病毒查杀专题
· VPN技术
· Solaris 10 配置管理
· SSL VPN详细知识
· Linux防火墙
· 打造安全服务器
· Sniffer安全技术从入门..
服务器选型与选购
服务器选型与选购
ARP攻击防范与解决方案
ARP攻击防范与解决方案
VPN技术
VPN技术
· VPN技术
· SQL Server 2008/2005..
· 中间件应用技术专题
· SQL Server 2008/2005..
· SOA 面向服务架构
· 子网掩码教程
· MySQL数据库备份
· RAID——磁盘阵列基础
· 身份认证技术
· 病毒查杀专题
· 清除流氓软件——51CTO..
· SSL VPN详细知识
· Sniffer安全技术从入门..
· VPN技术
· SOA 面向服务架构
· 了解统一威胁管理(UTM)..