- Linux那些事儿之我是USB
- 肖林甫 肖季东 任桥伟
- 4612字
- 2020-08-27 00:52:23
24.设备的生命线(五)
下面接着看内核代码的三个基本点。
第一个基本点,usb_alloc_urb函数,创建urb的专用函数,为一个urb申请内存并做初始化,在drviers/usb/core/urb.c中定义。
56 struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) 57 { 58 struct urb *urb; 59 60 urb = kmalloc(sizeof(struct urb) + 61 iso_packets * sizeof(struct usb_iso_packet_descriptor), 62 mem_flags); 63 if (!urb) { 64 err("alloc_urb: kmalloc failed"); 65 return NULL; 66 } 67 usb_init_urb(urb); 68 return urb; 69 }
这函数只做了两件事情,拿kmalloc来为urb申请内存,然后调用usb_init_urb进行初始化。usb_init_urb函数在前面讲struct urb中的引用计数时已经讲过了,它主要的作用就是初始化urb的引用计数,还用me mset顺便把这里给urb申请的内存清零。
usb_alloc_urb说:“别看我简单,我也是很有内涵的。”先看第一个问题,它的第一个参数iso_packets,表示struct urb结构最后那个变长数组iso_frame_desc的元素数目,也就是应该与number_of_packets的值相同,所以对于控制/中断/批量传输,这个参数都应该为0。这也算是给我们示范了变长数组的用法。
第二个问题是参数mem_flags的类型gfp_t,早几个版本的内核,类型还是int,当然这里变成gfp_t是因为kmalloc参数中的标志参数的类型从int变成gfp_t了,你要用kmalloc来申请内存,就得遵守它的规则。不过这里要说的不是kmalloc,而是gfp_t,它在江湖上也没出现多久,名号还没打出来,很多人不了解,我们来调查它的背景。它在include/linux/types.h中定义。
193 typedef unsigned __bitwise__ gfp_t;
很显然,要了解gfp_t,关键是要先了解__bitwise__,它也在types.h中定义。
170 #ifdef __CHECKER__ 171 #define __bitwise__ __attribute__((bitwise)) 172 #else 173 #define __bitwise__ 174 #endif
__bitwise__的含义又取决于是否定义了__CHECKER__,如果没有定义__CHECKER__,那__bitwise__就什么也不是。在哪里定义了__CHECKER__?内核代码里就没有哪个地方定义了__CHECKER__,它是有关Sparse工具的,内核编译时的参数决定了是不是使用Sparse工具来做类型检查。那Sparse又是什么?它是一种静态分析工具(static analysis tool), 用于在Linux内核源代码中发现各种类型的漏洞,一直都是比较神秘的角色,最初由Linus Torvalds写的,后来Linus没有继续维护,直到去年的冬天,它才又有了新的维护者Josh Triplett。
可能还会有第三个问题,usb_alloc_urb也没做多少事,它做的那些咱们自己很容易就能做了,为什么还说驱动里一定要使用它来创建urb呢?按照C++的说法,它就是urb的构造函数,构造函数是创建对象的唯一方式,如果你说C++里面使用位复制去复制一个简单对象给新对象就没使用构造函数,那是你不知道,C++的ARM里将这时的构造函数称为trivial copy constructor。再说,现在它做的这些事情,以后还是做这些吗?它将创建urb的工作给包装了,我们只管调用就是了,这就是孙子兵法里的“以不变应万变”。
对应的,当然还会有一个析构函数,是销毁urb的,也在urb.c中定义。
81 void usb_free_urb(struct urb *urb) 82 { 83 if (urb) 84 kref_put(&urb->kref, urb_destroy); 85 }
usb_free_urb更潇洒,只调用kref_put将urb的引用计数减1,减了之后如果变为0,也就是没人再用它了,就调用urb_destroy将它销毁掉。
接着看usb_fill_control_urb函数,初始化刚才创建的控制urb,你要想使用urb进行USB传输,不是光为它申请一点内存就够的,你得为它初始化,填充实点实实在在的内容。它是在include/linux/usb.h中定义的内联函数。
1175 static inline void usb_fill_control_urb (struct urb *urb, 1176 struct usb_device *dev, 1177 unsigned int pipe, 1178 unsigned char *setup_packet, 1179 void *transfer_buffer, 1180 int buffer_length, 1181 usb_complete_t complete_fn, 1182 void *context) 1183 { 1184 spin_lock_init(&urb->lock); 1185 urb->dev = dev; 1186 urb->pipe = pipe; 1187 urb->setup_packet = setup_packet; 1188 urb->transfer_buffer = transfer_buffer; 1189 urb->transfer_buffer_length = buffer_length; 1190 urb->complete = complete_fn; 1191 urb->context = context; 1192 }
这个函数基本上都是赋值语句,把你在参数中指定的值充实给刚刚创建的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函数的这俩孪生兄弟。
1207 static inline void usb_fill_bulk_urb (struct urb *urb, 1208 struct usb_device *dev, 1209 unsigned int pipe, 1210 void *transfer_buffer, 1211 int buffer_length, 1212 usb_complete_t complete_fn, 1213 void *context) 1214 { 1215 spin_lock_init(&urb->lock); 1216 urb->dev = dev; 1217 urb->pipe = pipe; 1218 urb->transfer_buffer = transfer_buffer; 1219 urb->transfer_buffer_length = buffer_length; 1220 urb->complete = complete_fn; 1221 urb->context = context; 1222 } 1223 1242 static inline void usb_fill_int_urb (struct urb *urb, 1243 struct usb_device *dev, 1244 unsigned int pipe, 1245 void *transfer_buffer, 1246 int buffer_length, 1247 usb_complete_t complete_fn, 1248 void *context, 1249 int interval) 1250 { 1251 spin_lock_init(&urb->lock); 1252 urb->dev = dev; 1253 urb->pipe = pipe; 1254 urb->transfer_buffer = transfer_buffer; 1255 urb->transfer_buffer_length = buffer_length; 1256 urb->complete = complete_fn; 1257 urb->context = context; 1258 if (dev->speed == USB_SPEED_HIGH) 1259 urb->interval = 1 << (interval - 1); 1260 else 1261 urb->interval = interval; 1262 urb->start_frame = -1; 1263 }
负责批量传输的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里。
21 static void usb_api_blocking_completion(struct urb *urb) 22 { 23 complete((struct completion *)urb->context); 24 }
这个函数更简洁,就一句,没有前面说的释放urb,也没有重新提交它。设置一个地址就可以了,没必要再将它提交给HCD,你就是再提交多少次,设置多少次,也只能有一个地址,USB的世界里不提倡囤积居奇。那在这里仅仅一句里面都做了些什么?接着往下看。
然后就是第三个基本点,usb_start_wait_urb函数,将前面历经千辛万苦创建和初始化的urb提交给USB core,让它移交给特定的主机控制器驱动进行处理,然后等待HCD回馈的结果,如果等待的时间超过了预期的限度,它不会再等。它在message.c中定义。
33 static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) 34 { 35 struct completion done; 36 unsigned long expire; 37 int status; 38 39 init_completion(&done); 40 urb->context = &done; 41 urb->actual_length = 0; 42 status = usb_submit_urb(urb, GFP_NOIO); 43 if (unlikely(status)) 44 goto out; 45 46 expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; 47 if (!wait_for_completion_timeout(&done, expire)) { 48 49 dev_dbg(&urb->dev->dev, 50 "%s timed out on ep%d%s len=%d/%d\n", 51 current->comm, 52 usb_pipeendpoint(urb->pipe), 53 usb_pipein(urb->pipe) ? "in" : "out", 54 urb->actual_length, 55 urb->transfer_buffer_length); 56 57 usb_kill_urb(urb); 58 status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status; 59 } else 60 status = urb->status; 61 out: 62 if (actual_length) 63 *actual_length = urb->actual_length; 64 65 usb_free_urb(urb); 66 return status; 67 }
35行,定义了一个struct completion结构体。completion是内核中一个比较简单的同步机制,一个线程可以通过它来通知另外一个线程某件事情已经做完了。
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可以通知所有等候的线程。
你不可能会毫无限度地等下去,所以针对不同的情况,等候的方式就有好几种,都在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((struct completion *)urb->context)就是用来通知这里的47行。有疑问的话看40行,将刚刚初始过的struct completion结构体done的地址赋值给了urb->context,47行等的就是这个done。再看42行,usb_submit_urb函数将这个控制urb提交给USB Core,它是异步的,也就是说提交了之后不会等到传输完成了才返回。
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值。在46行里,MAX_SCHEDULE_TIMEOUT比较陌生,在include/linux/sched.h里它被定义为LONG_MAX,最大的长整型值,在include/linux/kernel.h里看一看。
23 #define INT_MAX ((int)(~0U>>1)) 24 #define INT_MIN (-INT_MAX - 1) 25 #define UINT_MAX (~0U) 26 #define LONG_MAX ((long)(~0UL>>1)) 27 #define LONG_MIN (-LONG_MAX - 1) 28 #define ULONG_MAX (~0UL) 29 #define LLONG_MAX ((long long)(~0ULL>>1)) 30 #define LLONG_MIN (-LLONG_MAX - 1) 31 #define ULLONG_MAX (~0ULL)
各种整型数的最大值最小值都在这里了,‘~’是按位取反,‘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给销毁了。actual_length是从上头儿传递过来的一个指针,这里要先判断actual_length是不是空的。