人的一生就象在拉屎,有时你已经很努力了可出来的只是一个屁。
看这内核代码也一个样,已经很努力了,俺的葱葱玉指都磨出茧子了,才勉勉强强把struct urb这个中心给说完,下面接着看那三个基本点。看之前,再猜个谜语,蜘蛛侠是什么颜色的?
第一个基本点,
usb_alloc_urb函数,创建urb的专用函数,为一个urb申请内存并做初始化,在drviers/usb/core/urb.c里定义。
41* usb_alloc_urb - creates a new urb for a USB driver to use
42* @iso_packets: number of iso packets for this urb
43* @mem_flags: the type of memory to allocate, see kmalloc() for a list of
44* valid options for this.
46* Creates an urb for the USB driver to use, initializes a few internal
47* structures, incrementes the usage counter, and returns a pointer to it.
49* If no memory is available, NULL is returned.
51* If the driver want to use this urb for interrupt, control, or bulk
52* endpoints, pass '' as the number of iso packets.
54* The driver must call usb_free_urb() when it is finished with the urb.
64 err("alloc_urb: kmalloc failed");
这函数长的很让人提神,是个亲民的角色。它只做了两件事情,拿kmalloc来为urb申请内存,然后调用
usb_init_urb进行初始化。
usb_init_urb函数在前面说struct urb中的引用计数的时候已经贴过了,它主要的作用就是初始化urb的引用计数,还用memset顺便把这里给urb申请的内存清零。
没什么说的了么?
usb_alloc_urb说:别看我简单,我也是很有内涵的。先看第一个问题,它的第一个参数
iso_packets,表示的是struct urb结构最后那个变长数组iso_frame_desc的元素数目,也就是应该与number_of_packets的值相同,所以对于控制/中断/批量传输,这个参数都应该为0。这也算是给咱们示范了下变长数组咋个用法,内核里到处都是C的示范工程。
第二个问题是参数mem_flags的类型
gfp_t,早几个版本的内核,这里还是int,当然这里变成
gfp_t是因为
kmalloc参数里的那个标志参数的类型从int变成
gfp_t了,你要用kmalloc来申请内存,就得遵守它的规则。不过这里要说的不是kmalloc,而是gfp_t,它在江湖上也没出现多久,名号还没打出来,很多人不了解,咱们来调查一下它的背景。它在include/linux/types.h里定义
很显然,要了解gfp_t,关键是要了解__bitwise__,它也在types.h里定义
__bitwise__的含义又取决于是否定义了__CHECKER__,如果没有定义__CHECKER__,那__bitwise__就啥也不是。哪里定义了__CHECKER__?穿别人的鞋,走自己的路,让他们去找吧,咱们不找,因为内核代码里就没有哪个地方定义了__CHECKER__,它是有关Sparse工具的,内核编译时的参数决定了是不是使用Sparse工具来做类型检查。那Sparse又是什么?它是一种静态分析工具(static analysis tool), 用于在linux内核源代码中发现各种类型的漏洞,一直都是比较神秘的角色,最初由Linus Torvalds写的,后来linus没有继续维护,直到去年的冬天,它才又有了新的维护者Josh Triplett。有关Sparse再多的东东,咱们还是各自去研究吧,这里不多说了。
可能还会有第三个问题,
usb_alloc_urb也没做多少事啊,它做的那些咱们自己很容易就能做了,为什么还说驱动里一定要使用它来创建urb那?按照C++的说法,它就是urb的构造函数,构造函数是创建对象的唯一方式,你抬杠说C++里面儿使用位拷贝去复制一个简单对象给新对象就没使用构造函数,那是你不知道,C++的ARM里将这时的构造函数称为trivial copy constructor。再说,现在它做这些事儿,以后还是做这些么?它将创建urb的工作给包装了,咱们只管调用就是了,孙子兵法里有,以不变应万变。
对应的,当然还会有个析构函数,销毁urb的,也在urb.c里定义
72* usb_free_urb - frees the memory used by a urb when all users of it are finished
73* @urb: pointer to the urb to free, may be NULL
75* Must be called when a user of a urb is finished with it.When the last user
76* of the urb calls this function, the memory of the urb is freed.
78* Note: The transfer buffer associated with the urb is not freed, that must be
usb_free_urb更潇洒,只调用
kref_put将urb的引用计数减一,减了之后如果变为0,也就是没人再用它了,就调用
urb_destroy将它销毁掉。
接着看第二个基本点,
usb_fill_control_urb函数,初始化刚才创建的控制urb,你要想使用urb进行usb传输,不是光为它申请点内存就够的,你得为它初始化,充实点实实在在的内容,这个和女星要出名快,也要填充点内容的道理是一样的。它是在include/linux/usb.h里定义的内联函数
1162
* usb_fill_control_urb - initializes a control urb
1163
* @urb: pointer to the urb to initialize.
1164
* @dev: pointer to the struct usb_device for this urb.
1165
* @pipe: the endpoint pipe
1166
* @setup_packet: pointer to the setup_packet buffer
1167
* @transfer_buffer: pointer to the transfer buffer
1168
* @buffer_length: length of the transfer buffer
1169
* @complete_fn: pointer to the usb_complete_t function
1170
* @context: what to set the urb context to.
1172
* Initializes a control urb with the proper information needed to submit
1179
void *transfer_buffer,
1188
urb->transfer_buffer = transfer_buffer;
这个函数长的就让人兴奋,纯粹是来增长咱们自信的,自信多一分,成功就多十分,你就能搞懂内核,你就能成为一个成功的男人。这个函数基本上都是赋值语句,把你在参数里指定的值充实给刚刚创建的
urb
,
urb
的元素有很多,这里只是填充了一部分,剩下那些不是控制传输管不着的,就是自有安排可以不用去管的。
你想想,一个
struct urb
结构要应付四种传输类型,每种传输类型总会有点自己特别的要求,总会有些元素专属于某种传输类型,而其它传输类型不用管的。如果按
C++
的做法,这称不上是一个好的设计思想,应该有个基类
urb
,里面放点儿四种传输类型公用的,比如
pipe
,
transfer_buffer
等,再搞几个子类,
control_urb
,
bulk_urb
等等,专门应付具体的传输类型,如果不用什么虚函数,实际的时间空间消耗也不会增加什么。但是实在没必要这么搞,这年头儿内核的结构已经够多了,你创建什么类型的
urb
,就填充相关的一些字段好了,况且写
usb core
的哥们儿已经给咱们提供了不同传输类型的初始化函数,就像上面的
usb_fill_control_urb
,对于批量传输有
usb_fill_bulk_urb
,对于中断传输有
usb_fill_int_urb
,一般来说这也就够了,下面就看看
usb_fill_control_urb
函数的这俩孪生兄弟。
1195
* usb_fill_bulk_urb - macro to help initialize a bulk urb
1196
* @urb: pointer to the urb to initialize.
1197
* @dev: pointer to the struct usb_device for this urb.
1198
* @pipe: the endpoint pipe
1199
* @transfer_buffer: pointer to the transfer buffer
1200
* @buffer_length: length of the transfer buffer
1201
* @complete_fn: pointer to the usb_complete_t function
1202
* @context: what to set the urb context to.
1204
* Initializes a bulk urb with the proper information needed to submit it
1210
void *transfer_buffer,
1218
urb->transfer_buffer = transfer_buffer;
1225
* usb_fill_int_urb - macro to help initialize a interrupt urb
1226
* @urb: pointer to the urb to initialize.
1227
* @dev: pointer to the struct usb_device for this urb.
1228
* @pipe: the endpoint pipe
1229
* @transfer_buffer: pointer to the transfer buffer
1230
* @buffer_length: length of the transfer buffer
1231
* @complete_fn: pointer to the usb_complete_t function
1232
* @context: what to set the urb context to.
1233
* @interval: what to set the urb interval to, encoded like
1234
* the endpoint descriptor's bInterval value.
1236
* Initializes a interrupt urb with the proper information needed to submit
1238
* Note that high speed interrupt endpoints use a logarithmic encoding of
1239
* the endpoint interval, and express polling intervals in microframes
1240
* (eight per millisecond) rather than in frames (one per millisecond).
1245
void *transfer_buffer,
1254
urb->transfer_buffer = transfer_buffer;
负责批量传输的usb_fill_bulk_urb和负责控制传输的
usb_fill_control_urb的相比,只是少初始化了一个
setup_packet,因为批量传输里没有Setup包的概念,中断传输里也没有,所以usb_fill_int_urb里也没有初始化
setup_packet这一说。不过usb_fill_int_urb比那两个还是多了点儿内容的,因为它有个
interval,比控制传输和批量传输多了个表达自己期望的权利,1258行还做了次判断,如果是高速就怎么怎么着,否则又怎么怎么着,主要是高速和低速/全速的间隔时间单位不一样,低速/全速的单位为帧,高速的单位为微帧,还要经过2的(bInterval-1)次方这么算一下。至于1262行start_frame,它是给等时传输用的,这里自然就设置为-1,关于为什么既然start_frame是等时传输用的这里还要设置那么一下,你往后看吧,现在我也不知道。
作为一个共产主义接班人,我们很快就能发现
usb_fill_control_urb的孪生兄弟里,少了等时传输对应的那个初始化函数,三缺一啊,在哪里都会是个遗憾。不是不想有,而是没办法,对于等时传输,urb里是可以指定进行多次传输的,你必须一个一个的对那个变长数组iso_frame_desc里的内容进行初始化,没人帮得了你。难道你能想出一个办法搞出一个适用于各种情况等时传输的初始化函数?我是不能。如果想不出来,使用urb进行等时传输的时候,还是老老实实的对里面相关的字段一个一个的填内容吧。如果想找个例子旁观一下别人是咋初始化的,可以去找个摄像头驱动,或者其它usb音视频设备的驱动看看,内核里也有一些的。
现在,你应该还记得咱们是因为要设置设备的地址,让设备进入Address状态,调用了usb_control_msg,才走到这里遇到
usb_fill_control_urb的,参数里的
setup_packet就是之前创建和赋好值的struct usb_ctrlrequest结构体,设备的地址已经在struct usb_ctrlrequest结构体wValue字段里了,这次控制传输并没有DATA transaction阶段,也并不需要urb提供什么transfer_buffer缓冲区,所以transfer_buffer应该传递一个NULL,当然transfer_buffer_length也就为0了,有意思的是这时候传递进来的结束处理函数usb_api_blocking_completion,可以看一下当这次控制传输已经完成,设备地址已经设置好后,接着做了些什么,它的定义在drivers/usb/core/message.c里
这个函数更简洁,就那么一句,没有前面说的释放urb,也没有重新提交它,本来就想设置个地址就完事儿了,没必要再将它提交给HCD,你就是再提交多少次,设置多少次,也只能有一个地址,usb的世界里不提倡囤积居奇,不鼓励一人多个Address去炒。那在这仅仅一句里面都做了些什么?你接着往下看。
然后就是第三个基本点,
usb_start_wait_urb函数,将前面历经千辛万苦创建和初始化的urb提交给咱们的usb core,让它移交给特定的主机控制器驱动进行处理,然后望眼欲穿的等待HCD回馈的结果,如果等待的时间超过了预期的限度,它不会再等,不会去变成望夫石。它在message.c里定义
28* Starts urb and waits for completion or timeout. Note that this call
29* is NOT interruptible. Many device driver i/o requests should be
30* interruptible and therefore these drivers should implement their
31* own interruptible routines.
41urb->actual_length = 0;
50 "%s timed out on ep%d%s len=%d/%d/n",
55 urb->transfer_buffer_length);
63 *actual_length = urb->actual_length;
35行,定义了一个struct
completion结构体。completion是内核里一个比较简单的同步机制,一个线程可以通过它来通知另外一个线程某件事情已经做完了。你使用某个下载软件去下载,然后撇一边儿不管就去忙着聊QQ泡mm了,下载完了那个软件会通知你,然后你想怎么做就怎么做,自个看也成,不怕被扁和正在聊的mm一块看也成,没人会去管你。怎么?你的那个软件下载完了也没通知你?那就紧赶的换个别的吧,写那个软件的也太没职业道德了,该做的事情不做。completion机制也同样是这么回事儿,你的代码执行到某个地方,需要再忙点儿其它的,就新开个线程,让它去忙活,然后自个接着忙自己的,想知道那边儿忙活的结果了,就停在某个地方等着,那边儿忙活完了会通知一下已经有结果了,于是你的代码又可以继续往下走。
completion机制围绕struct
completion结构去实现,有两种使用方式,一种是通过DECLARE_COMPLETION宏在编译时就创建好struct
completion的结构体,另外一种就是上面的形式,运行时才创建的,先在35行定义一个struct
completion结构体,然后在39行使用
init_completion去初始化。光是创建struct
completion的结构体没用,关键的是如何通知任务已经完成了,和怎么去等候完成的好消息。片子下载完了可能会用声音、对话框等多种方式来通知你,同样这里用来通知已经完成了的函数也不只一个,
void complete(struct completion *c);
void complete_all(struct completion *c);
complete只通知一个等候的线程,complete_all可以通知所有等候的线程,大家都一个宿舍的好兄弟,你总不好意思自己藏着好东西,不让大家欣赏吧,所以可能会有多个人来等着片子下完。
你不可能会毫无限度的等下去,21世纪最缺的是什么?耐心,凡事都有个限度,即使片子再精彩,多会儿下不完也不等它了,当然会有比我还穷极无聊的人愿意一直在那里等着,毕竟林子大了什么鸟儿都有,或者说正等着那,一个ppmm过来打断你,你赶着花前月下去了,也不会去继续等了。所以针对不同的情况,等候的方式就有好几种,都在kernel/sched.c里定义
void wait_for_completion(struct completion *c);
unsigned long wait_for_completion_timeout(struct completion *x, unsigned long timeout);
int wait_for_completion_interruptible(struct completion *x);
unsigned long wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout);
上面47行使用的就是wait_for_completion_timeout,设定一个时间限度,然后在那里候着,直到得到通知,或者超过时间。既然有等的一方,也总得有通知的一方吧,不然岂不是每次都超时?写代码的哥们儿没这么变态,记得上面刚出现过的那个结束处理函数
usb_api_blocking_completion不?不是吧,被窝都热乎着就不认人了啊,它里面唯一的一句
complete就是用来通知这里的47行的。有疑问的话看40行,将刚刚初始过的struct
completion结构体done的地址赋值给了
urb,47行等的就是这个done。再看42行,usb_submit_urb函数将这个控制urb提交给usb core,它是异步的,也就是说提交了之后不会等到传输完成了才返回。->
context((struct
completion *)
urb->
context)
现在就比较清晰了,usb_start_wait_urb函数将urb提交给usb core去处理后,就停在47行等候usb core和HCD的处理结果,而这个urb代表的控制传输完成之后会调用结束处理函数
usb_api_blocking_completion,从而调用
complete来通知usb_start_wait_urb说不用再等了,传输已经完成了,当然还有种可能是usb_start_wait_urb已经等的超过了时间限度仍然没有接到通知,不管是哪种情况,usb_start_wait_urb都可以不用再等,继续往下走了。
下边儿挨个说一下,42行,提交urb,并返回一个值表示是否提交成功了,显然,成功的可能性要远远大于失败的可能性,不然就是或者写代码的哥们儿有问题,或者就是我有问题,所以接下来的判断加上了unlikely,如果你真的那么衰,遇上了小概率事件,那也就没必要在47行等通知了,直接到后边儿去吧。
46行,计算超时值。超时值在参数里不是已经给了么,还计算什么?没错,你是在参数里是指定了自己能够忍受的最大时间限度,不过那是以ms为单位的,作为一个平头小百姓,咱们的时间概念里也只有分钟啊妙啊毫秒啊什么的,不过作为一个要在linux里混的平头小百姓,咱们的时间概念里必须得加上一个号称jiffy的东东,因为函数wait_for_completion_timeout里的超时参数是必须以jiffy为单位的。
jiffy,金山词霸告诉我们,是瞬间,短暂的时间跨度,短暂到什么程度?linux里它表示的是两次时钟中断的间隔,时钟中断是由定时器周期性产生的,关于这个周期,内核里有个巨直白巨形象的变量来描述,就是HZ,它是个体系结构相关的常数。内核里还提供了专门的计数器来记录从系统引导开始所度过的jiffy值,每次时钟中断发生时,这个计数器就增加1。
既然你指定的时间和wait_for_completion_timeout需要的时间单位不一致,就需要转换一下,msecs_to_jiffies函数可以完成这个工作,它将ms值转化为相应的jiffy值。这一行里还剩个MAX_SCHEDULE_TIMEOUT比较陌生,在include/linux/sched.h里它被定义为LONG_MAX,最大的长整型值,我知道你会好奇这个LONG_MAX是怎么来的,好奇就说出来嘛,好奇又不会真的害死猫,我也很好奇,所以咱们到生它养它的include/linux/kernel.h里看看
各种整型数的最大值最小值都在这里了,俺现在替谭浩强再普及点基础知识,‘~’是按位取反,‘UL’是无符号长整型,那么‘ULL’就是64位的无符号长整型,‘<<’左移运算的话就是直接一股脑的把所有位往左边儿移若干位,‘>>’右移运算比较容易搞混,主要是牵涉到怎么去补缺,有关空缺儿的事情在哪里都会比较的复杂,勾心斗角阶级斗争的根源,在C里主要就是无符号整数和有符号整数的之间的冲突,在你补管三七二十一一直往右移多少位之后,空出来的那些空缺,对于无符号整数得补0,对于有符号的,得补上符号位。
还是拿LONG_MAX来说事儿,上边定义为((long)(~0UL>>1)),0UL按位取反之后全为1的无符号长整型,向右移1位,左边儿空出来的那个补0,这个数对于无符号的long来说不算什么,但是再经过long这么强制转化一下变为有符号的长整型,它就是老大了。每个老大的成长过程都是一部血泪史,都要历经很多曲折。
现在你可以很明白的知道写代码的哥们儿在46行都做了些什么,你指定的超时时间被转化为相应的jiffy值,或者直接被设定为最大的long值。
47行,等待通知,我们需要知道的是怎么去判断等待的结果,也就是wait_for_completion_timeout的返回值代表什么意思?一般来说,一个函数返回了0代表了好消息,一切顺利,如果你这么想那可就错了。wait_for_completion_timeout返回0恰恰表示的是坏消息,表示直到超过了自己的忍耐的极限仍没有接到任何的回馈,而返回了一个大于0的值则表示接到通知了,那边儿不管是完成了还是出错了总归是告诉这边儿不用再等了,这个值具体的含义就是距你设定的时限提前了多少时间。为什么会这样?你去看看wait_for_completion_timeout的定义就知道了,我就不贴了,它是通过schedule_timeout来实现超时的,schedule_timeout的返回值就是这么个意思。
那么现在就很明显了,如果超时了,就打印一些调试信息提醒一下,然后调用usb_kill_urb终止这个urb,再将返回值设定一下。如果收到了通知,就简单了,直接设定了返回值,就接着往下走。
62行,actual_length是用来记录实际传输的数据长度的,是上头儿的上头儿usb_control_msg需要的。不要给我说这个值urb里本来就有保存,何必再多次一举找个地儿去放,没看接下来的65行就用usb_free_urb把这个urb给销毁了啊,到那时花非花树非树,urb也已经不是urb,哪里还去找这个值。actual_length是从上头儿那里传递过来的一个指针,写内核的哥们儿教导我们,遇到指针一定要小心再小心啊,同志们。所以这里要先判断一下actual_length是不是空的。
现在,只剩一个usb_submit_urb在刚才被有意无意的飘过了,咱们下面说。