`
javatome
  • 浏览: 825834 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

《Linux那些事儿之我是USB》我是U盘(21)传说中的URB

 
阅读更多

有人问,怎么写一个驱动写这么久啊?

的确,一路走来,大家都不容易,但既然已经走到今天,我们能做的也只有是坚持下去。

usb_stor_acquire_resources(),从名字上来看是获取资源。什么是资源?之前不是申请了一大堆内存了吗?写个USB设备驱动程序怎么这么麻烦啊?不是专门为USB Mass Storage设备准备了一个struct us_data这么一个结构体了吗?不是说故事已经到高潮了吗?

如果你以为看到这里你已经对USB设备驱动程序有了足够的认识,认为接下来的代码已经没有必要再分析了,那么,我只想说,上帝创造世界的计划中,未必包括使你会写USB设备驱动程序。

的确,别看usb_stor_acquire_resources的代码不多,每一行都有每一行的故事。本节我们只讲其中的一行代码,没错,就是一行代码,因为我们需要隆重推出一个名词,一个响当当的名字,它就是传说中的“urb”,全称USB RequestBlock。USB设备需要通信,要传递数据,就需要使用urb,确切地说,应该是USB设备驱动程序使用urb。实际上,作为USB设备驱动,它本身并不能直接操纵数据的传输,在USB这个大观园里,外接设备永远都是配角,真正的核心只是USB Core,而真正负责调度的是USB主机控制。这个通常看不见的USB主机控制器芯片,俨然是USB大观园中的大管家。设备驱动要发送信息,所需要做的是建立一个urb数据结构,并把这个数据结构交给核心层,而核心层会为所有设备统一完成调度,而设备在提交了urb之后需要做的,只是等待。别急,我们慢慢来。

784行,一条赋值语句,等号左边us->current_urb,等号右边usb_alloc_urb()函数被调用。如果说struct us_data是usb mass storage中的主角,那么struct urb将毫无争议地成为整个USB子系统中的主角。Linux中所有的USB设备驱动,都必然也必须要使用urb。那么urb究竟长成什么样呢?在include/linux/usb.h中能找到它:

1126 struct urb

1127 {

1128 /* private: usb core and hostcontroller only fields in the urb */

1129 struct kref kref;/* reference count of the URB */

1130 spinlock_t lock;/* lock for the URB */

1131 void *hcpriv;/* private data for host controller */

1132 atomic_tuse_count;/* concurrent submissions counter */

1133 u8 reject;/* submissions will fail */

1134

1135 /* public: documented fields inthe urb that can be used by drivers*/

1136 struct list_head urb_list; /* list headfor use by the urb's

1137* current owner */

1138 struct usb_device *dev; /* (in)pointer to associated device */

1139 unsignedint pipe;/* (in) pipe information */

1140 intstatus;/* (return) non-ISO status */

1141 unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK |...*/

1142 void*transfer_buffer; /* (in)associated data buffer */

1143 dma_addr_ttransfer_dma; /*(in) dma addr for transfer_buffer */

1144 int transfer_buffer_length; /* (in) data bufferlength */

1145 int actual_length;/* (return) actual transfer length */

1146 unsigned char *setup_packet; /* (in) setup packet (control only) */

1147 dma_addr_tsetup_dma; /* (in) dma addr for setup_packet*/

1148 intstart_frame;/* (modify) start frame (ISO) */

1149 intnumber_of_packets; /* (in)number of ISO packets */

1150 intinterval;/* (modify) transfer interval

1151* (INT/ISO) */

1152 int error_count;/* (return) number of ISO errors */

1153 void*context;/* (in) context for completion */

1154 usb_complete_tcomplete; /* (in) completionroutine */

1155 struct usb_iso_packet_descriptoriso_frame_desc[0];

1156/* (in) ISO ONLY */

1157 };

我们常常抱怨,Linux内核源代码中注释太少了,以至于我们常常看不懂那些代码究竟是什么含义。但urb让开发人员做足了文章,结构体struct urb的定义不过30行,而说明文字却用了足足160余行。可见urb的地位。当然我们这里贴出来主要还是为了保持原汁原味,另一方面这个注释也说得很清楚,对于每一个成员都解释了,而我们接下来将必然要用到urb的许多个成员。

此刻,我们暂时不去理会这个结构体每一个元素的作用,只需要知道,一个urb包含了执行USB传输所需要的所有信息。而作为驱动程序,要通信就必须创建这么一个数据结构,并且赋值,显然不同类型的传输,需要对urb赋不同的值,然后将她提交给底层,完了底层的USB Core会找到相应的USB主机控制器,从而具体实现数据的传输。传输完了之后,USB主机控制器会通知设备驱动程序。

总之我们知道,784行就是调用usb_alloc_urb()申请了一个struct urb结构体。关于usb_alloc_urb()这个函数,我们不打算讲,它是USB Core所提供的一个函数,来自drivers/usb/core/urb.c,USB开发人员的确是给足了urb的面子,专门把和这个数据结构相关的代码整理在这么一个文件中了。我们可以在include/linux/usb.h中找到这个函数的声明:

1266 extern struct urb *usb_alloc_urb(intiso_packets, gfp_t mem_flags);

这个函数的作用很明显,就是为一个urb结构体申请内存。它有两个参数,其中第一个iso_packets用来在等时传输的方式下指定你需要传输多少个包,对于非等时模式来说,这个参数直接使用0。另一个参数mem_flags就是一个flag,表示申请内存的方式,这个flag将最终传递给kmalloc函数,我们这里传递的是GFP_KERNEL,这个flag是内存申请中最常用的,我们之前也用过,在为us申请内存时。usb_alloc_urb最终将返回一个urb指针,而us的成员current_urb也是一个struct urb的指针,所以就赋给它了。不过需要记住,usb_alloc_urb除了申请内存以外,还对结构体做了初始化,结构体urb被初始化为0,虽然这里我们没有把这个函数的代码“贴”出来,但你也千万不要以为写代码的人跟我似的,申请变量还能忘了初始化。同时,struct urb中还有一个引用计数,以及一个自旋锁,这些也同样被初始化了。

所以,接下来我们就将要和us->current_urb打交道了。如果你对urb究竟怎么用还有些困惑的话,可以查看主机控制器驱动的代码。如果你不想看,那么我可以用一种你最能接受的方式告诉你,USB是一种总线,是总线它就要通信。我们现实生活中真正要使用的是设备,但是光有设备还不足以实现USB通信,于是世界上有了USB主机控制器,它来负责统一调度。这就好比城市的交警,这个城市里真正需要的本来是车辆和行人,而光有车辆和行人,没有交警,那么这个城市里的车辆和行人必将乱套。于是诞生了交警这个行业,交警站在路口统一来管理调度混乱的交通。假如车辆和行人可以完全自觉遵守某种规矩而来来往往于这个城市的每一个角落及每一个路口,那么交警就没有必要存在了。同样,假如设备能够完全自觉地传递信息,每一个数据包都能到达它应该去的地方,那么我们根本就不需要有主机控制器。然而,事实上总会有不遵守交通规则的人。同样,在USB的世界中,设备也总是那么不守规矩,我们必须要设计一个东西出来管理来控制所有的USB设备的通信,这样,主机控制器就横空出世了。

那么设备和主机控制器的分工又是如何呢?硬件实现上我们就不说了,说点儿具体的,在Linux中,设备驱动程序只要为每一次请求准备一个urb结构体变量,把它填充好(就是说赋上该赋的值),然后它调用USB Core提供的函数,把这个urb传递给主机控制器,主机控制器就会把各个设备驱动程序所提交的urb统一规划,去执行每一个操作。而这期间,USB设备驱动程序通常会进入睡眠,而一旦主机控制器把urb要做的事情给做完了,它会调用一个函数去唤醒USB设备驱动程序,然后USB设备驱动程序就可以继续往下走了。

这又好比我们学校里的师生关系。考试时,我们只管把试卷填好,然后我们交给老师,然后老师拿去批改试卷,这期间我们除了等待别无选择,等待老师改完了试卷,告诉了我们分数,我们又继续我们的生活。同样,USB设备驱动程序也是如此,如果urb提交给USB主机了,但是最终却没有成功执行,那么也许该USB设备驱动程序的生命也就提前结束。不过这都是后话,现在只要有一个感性认识即可,稍后看到了就能更深刻的体会了,这种岗位分工的方式给我们编写设备驱动程序带来了巨大的方便。

继续usb_stor_acquire_resources函数。

785行到788行,就是刚才urb申请了之后判断是否申请成功了,如果指针为NULL那么就是失败了,直接返回-ENOMEM。

792行,us->unusual_dev->initFunction是什么?在分析unusual_devs.h文件时曾经专门举过例子的,说有些设备需要一些初始化函数,它就定义在unusual_devs.h文件中,而我们通过UNUSUAL_DEV的定义已经把这些初始化函数赋给了us->unusual_dev的initFunction指针了。所以这时候,在传输开始之前,我们判断是不是有这样一个函数,即这个函数指针是否为空,如果不为空,很好办,执行这个函数就行了。比如当时我们举例子时说的那两个设备就有初始化函数,那么就让它执行好了。当然,一般的设备肯定不需要这么一个函数。至于传递给这个函数的参数,在struct us_unusual_dev结构体定义时,就把这个函数需要什么样的参数定义好了,需要的就是一个struct us_data *,那么很自然,传递的就是us。

至此,我们终于走到了usb_stor_acquire_resources()中第799行,即将见到这个千呼万唤始出来的内核精灵。
分享到:
评论

相关推荐

    linux那些事儿我是USB.pdf

    关键词:Linux, Kernel, 2.6, bus, usb, device driver, mass storage, scsi, urb, bulk, control, host, pipe, command, 林志玲 ...... 作者用很诙谐的语言讲述了linux下USB驱动。

    Linux那些事儿之我是U盘

    主要讲述Linux下U盘驱动开发 Linux, Kernel, 2.6, bus, usb, device driver, mass storage, scsi,urb, bulk, control, host, pipe, command,

    LINUX那些事儿 linux经典之作

    到了这一阶段基本上就能回答我是U盘中所有的困惑了.(其中Block层由于过于复杂和规模庞大,主要是举例和个别函数分析,主要是辅助另外几个模块进行分析.) 以上每一阶段代码跳跃性会逐渐增强,即开始会讲的细,之后会讲的...

    usb_urb分析

    usb_urb分析:抓包工具抓到的数据包的详细解析。

    Linux System Error & Urb Error Status

    -EINPROGRESS 这个 urb 仍然在被 USB 主机控制器处理中. 如果你的驱动 曾见到这个值, 它是一个你的驱动中的 bug. -EPROTO 这个 urb 发生下面一个错误: 一个 bitstuff 错误在传送中 发生.硬件没有及时收到响应帧. ......

    Linux的USB虚拟串口驱动(CDC-ACM驱动)源码

    Linux下的CDC-ACM驱动源码,USB虚拟串口驱动程序,欢迎下载学习!

    linux系统USB键盘驱动源码+使用说明文档.zip

    linux系统下USB键盘驱动... struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/; unsigned char newleds;/*目标指定灯状态*/ char name[128];/*存放厂商名字及产品名字*/ char phys[64]

    嵌入式Linux下USB摄像头驱动实现

    :在嵌入式系统中开发USB 摄像头驱动需要充分利用USB 总线带宽并保证内存与摄像头之间数据的高速稳定交换。为满足该要求, 参考开源项目GSPCA/SPCA5xx,采纳Linux 内核建议并遵循Video4Linux 标准,提出双URB 分配...

    STM32/GD32的USB HostU盘读取例程

    USB作为主机读写U盘例程,集合Fatfs文件系统,适用于STM32F407以及GD32F407,稍加改动可适用于其他单片机。另外还有Keil5版本,请自行编译。

    基于Linux的USBoverIP系统设计

    这样我在驱动层将USB 请求包(URB)封装成IP 包在以太网上传送,实现远 端URB 到本地的透明映射,实现设备的相互共享。另外还对特定问题,如热插 拔,驱动绑定等,进行了详细的分析并提出自己的解决方法。

    linux usb api

    完整讲述linux下的USB API.包括usb_serial_driver等核心数据结构的定义,以及URB等

    Linux环境下的USB摄像头驱动开发

    本文从Linux内核的USB核心模块出发,遵循Video4Linux接口标准,采用urb策略与内存映射的方式以提高数据读取速度,设计开发了基于Linux环境下的USB摄像头驱动,并在ARM9实验平台上对该驱动程序进行了测试与分析。

    usb_hid_pc_stm32 USB HID STM32 开发资料合集

    有了这个资料包,你再说开发不了USB HID,打死我都不信! 好资料当然一分都不能少! 目录: STM32_USB_HID_PC_demo:USB HID Demonstrator Release软件和文档 STM32_USB_HID_分析:STM32 USB HID 固件学习分析 ...

    32位Windows操作系统下WDM型USB设备驱动程序设计

    在设计USB设备的过程中不可避免的要涉及到设备驱动程序的设计。首先整体上简要介绍了USB及其通信模型,然后详细讨论了Windows的WDM驱动程序模型,重点阐述了基于WDM的USB设备驱动程序的具体开发过程。最后给出了当前...

    USB助手(调试USB通信协议的工具) 源码

    驱动器 D 中的卷没有标签。 卷的序列号是 B837-9C57 D:\download\USB助手 源码 2008-12-30 08:44 <DIR> . 2008-12-30 08:44 <DIR> .. 2002-10-07 15:21 1,536 ChildFrm.cpp 2002-10-07 15:21 1,397 ChildFrm.h ...

    Linux DeviceDrivers 3rd Edition

    (英文版 第三版 中文书签我自己做的 大家要是不喜欢就用福听PDF删除掉我加上去的东西 我还有很多好资源 大家都去逛逛 还有这个是单本的 网上还有个分很多章的) 前言 1 第一章 设备驱动程序简介 9 设备驱动程序...

    Linux系统下USB摄像头驱动开发

    摘要:介绍了在Iinux系统下开发符合Video for Linux标准的USB摄像头驱动的方法,并对该标准提出“不间断采集”的改进思路,配合双URB、双帧缓冲等方法,提高采集速度。 关键词:Linux设备驱动 USB摄像头 Video for ...

    Android开发USB摄像头拔出后闪退

    使用UVC Camera框架的时候... 关键点在于 libuvccamera 依赖库中的 android_usbfs.c 中 handle_iso_completion (struct libusb_device_handle *handle, struct usbi_transfer *itransfer, struct usbfs_urb *urb)方法中

Global site tag (gtag.js) - Google Analytics