《Linux那些事儿之我是USB》我是U盘(34)迷雾重重的批量传输(三)
发布时间:2014-09-05 16:27:19作者:知识屋
上一篇:http://www.zhishiwu.com/os/201112/114321.html
在usb_stor_Bulk_transport()中,这个函数中调用的第一个最重要的函数,那就是usb_stor_bulk_transfer_buf()。仍然是来自drivers/usb/stroage/transport.c:
391 int usb_stor_bulk_transfer_buf(struct us_data*us, unsigned int pipe,
392 void *buf, unsigned int length, unsigned int *act_len)
393 {
394 int result;
395
396 US_DEBUGP("%s: xfer %u Bytes/n",__FUNCTION__, length);
397
398 /* fill and submit the URB */
399 usb_fill_bulk_urb(us->current_urb,us->pusb_dev, pipe, buf, length,
400 usb_stor_blocking_completion, NULL);
401 result = usb_stor_ msg_common(us, 0);
402
403 /* store the actual length of the datatransferred */
404 if (act_len)
405 *act_len =us->current_urb->actual_length;
406 return interpret_urb_result(us, pipe, length,result,
407 us->current_urb->actual_length);
408 }
如果结果提交成功了,那么返回值result将是0。而act_len将记录实际传输的长度。不过光看这两个函数其实看不出什么,我们必须结合上下文来看。换句话说,我们需要结合usb_stor_Bulk_transport()中usb_stor_bulk_transfer_buf被调用的上下文,对比形参和实参来看,才能真的明白,才能拨开这团浓浓的迷雾。
而在仔细分析usb_stor_Bulk_transport()之前,我们先来看这个usb_fill_bulk_urb()函数。这个函数我们第一次见到,在USB世界里,和这个函数相似的函数还有usb_fill_control_urb(),除此之外还有一个叫做usb_fill_int_urb()的函数,不用说,这几个函数是差不多的,只不过它们分别对应USB传输模式中的批量、控制和中断。唯一一处与usb_fill_control_urb不同的便是,批量传输不需要有一个setup_packet。具体来看usb_fill_bulk_urb()定义于include/linux/usb.h:
1207 static inline void usb_fill_bulk_urb (structurb *urb,
1208 structusb_device *dev,
1209 unsignedint pipe,
1210 void*transfer_buffer,
1211 intbuffer_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 }
我们调用这个函数的目的是为了填充一个urb,然后我们可以把这个urb提交给USB Core那一层。很显然,它就是为特定的管道填充一个urb(最初urb申请时被初始化为0了)。
此处特意提一下usb_complete_t类型,在include/linux/usb.h中,有下面这一行。
961 typedef void (*usb_complete_t)(struct urb *);
这里用了typedef来简化声明,不熟悉typedef功能的人可以去查一下,typedef的强大使得以下两种声明作用相同。
一种作用是:
void (*func1)(struct urb *);
void (*func2)(struct urb *);
void (*func3)(struct urb *);
另一种作用是:
typedef void (*usb_complete_t)(struct urb *);
usb_complete_t func1;
usb_complete_t func2;
usb_complete_t func3;
看出来了吧,如果要声明很多个函数指针,那么显然使用typedef一次,就可以一劳永逸了,以后声明就很简单了。所以,咱们也就知道,实际上urb中的complete是一个函数指针,它被设置为指向函数usb_stor_blocking_completion()。不用说,这个函数之后肯定会被调用。
401行,usb_stor_ msg_common()这个函数被调用,它的作用是urb会被提交,然后核心层去调度,去执行它。
124 static int usb_stor_ msg_common(struct us_data*us, int timeout)
125 {
126 struct completion urb_done;
127 long timeleft;
128 int status;
129
130 /* don't submit URBs during abort/disconnectprocessing */
131 if (us->flags &ABORTING_OR_DISCONNECTING)
132 return -EIO;
133
134 /* set up data structures for the wakeupsystem */
135 init_completion(&urb_done);
136
137 /* fill the common fields in the URB */
138 us->current_urb->context =&urb_done;
139 us->current_urb->actual_length = 0;
140 us->current_urb->error_count = 0;
141 us->current_urb->status = 0;
142
143 /* we assume that if transfer_buffer isn'tus->iobuf then it
144 *hasn't been mapped for DMA. Yes, this isclunky, but it's
145 *easier than always having the caller tell us whether the
146 *transfer buffer has already been mapped. */
147 us->current_urb->transfer_flags =URB_NO_SETUP_DMA_MAP;
148 if (us->current_urb->transfer_buffer ==us->iobuf)
149 us->current_urb->transfer_flags |=URB_NO_TRANSFER_DMA_MAP;
150 us->current_urb->transfer_dma =us->iobuf_dma;
151 us->current_urb->setup_dma =us->cr_dma;
152
153 /* submit the URB */
154 status = usb_submit_urb(us->current_urb,GFP_NOIO);
155 if (status) {
156 /* something went wrong */
157 return status;
158 }
159
160 /* since the URB has been submittedsuccessfully, it's now okay
161 * tocancel it */
162 set_bit(US_FLIDX_URB_ACTIVE,&us->flags);
163
164 /* did an abort/disconnect occur during thesubmission? */
165 if (us->flags & ABORTING_OR_DISCONNECTING){
166
167 /* cancel the URB, if it hasn't been cancelledalready */
168 if (test_and_clear_bit(US_FLIDX_URB_ACTIVE,&us->flags)) {
169 US_DEBUGP("-- cancellingURB/n");
170 usb_unlink_urb(us->current_urb);
171 }
172 }
173
174 /* wait for the completion of the URB */
175 timeleft =wait_for_completion_interruptible_timeout(
176 &urb_done, timeout? : MAX_SCHEDULE_TIMEOUT);
177
178 clear_bit(US_FLIDX_URB_ACTIVE,&us->flags);
179
180 if (timeleft <= 0) {
181 US_DEBUGP("%s -- cancellingURB/n",
182 timeleft == 0 ?"Timeout" : "Signal");
183 usb_kill_urb(us->current_urb);
184 }
185
186 /* return the URB status */
187 return us->current_urb->status;
188 }
注意了,usb_fill_bulk_urb这个函数只填充了urb中的几个元素,而struct urb里面包含了很多东西,不过有一些设置是共同的,所以才需要用usb_stor_msg_common()函数来设置,可以看出给这个函数传递的参数只有两个:一个就是us,另一个是timeout(在我们这个案例中传给它的值是0),我们继续进入到这个函数中来把它看清楚、看明白。
首先看131行,让us->flags和ABORTING_OR_DISCONNECTING相与,ABORTING_OR_DISCONNECTING宏定义于drivers/usb/storage/usb.h中:
70 /* Dynamic flag definitions: used in set_bit()etc. */
71 #define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */
72 #define US_FLIDX_SG_ACTIVE 19 /*0x00080000 current_sg is in use */
73 #define US_FLIDX_ABORTING 20 /*0x00100000 abort is in progress */
74 #define US_FLIDX_DISCONNECTING 21/* 0x00200000 disconnect in progress*/
75 #define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | /
76 (1UL<< US_FLIDX_DISCONNECTING))
77 #define US_FLIDX_RESETTING 22 /*0x00400000 device reset in progress*/
78 #define US_FLIDX_TIMED_OUT 23 /*0x00800000 SCSI midlayer timed out */
它只是一个flag,咱们知道,每一个USB Mass Storage设备,会有一个struct us_data的数据结构,即us。所以,在整个probe的过程来看,它相当于一个“全局”变量,因此可以使用一些flags来标记一些事情。比如,此处对于提交urb的函数来说,显然它不希望设备此时已经处于放弃或者断开的状态,因为那样就没有必要提交urb了。
而下一个函数init_completion(),只是一个队列操作函数,它被定义于include/linux/completion.h中:
39 static inline void init_completion(structcompletion *x)
40 {
41 x->done= 0;
42 init_waitqueue_head(&x->wait);
43 }
它只是调用了init_waitqueue_head去初始化一个等待队列。而struct completion的定义也在同一个文件中:
13 struct completion {
14 unsigned int done;
15 wait_queue_head_t wait;
16 };
关于init_waitqueue_head咱们将在下面的故事中专门进行描述。
而接下来,都是在设置us的current_urb结构。看147行,transfer_flags被设置成了URB_NO_SETUP_DMA_MAP,而URB_NO_SETUP_DMA_MAP表明,如果使用DMA传输,则urb中setup_dma指针所指向的缓冲区是DMA缓冲区,而不是setup_packet所指向的缓冲区。不过这个setup_packet是控制传输特有的概念,对于批量传输,根本没有这个概念,所以我们完全可以不予理睬。
接下来URB_NO_TRANSFER_DMA_MAP则表明,如果urb有一个DMA缓冲区需要传输,则该缓冲区是transfer_dma指针所指向的那个缓冲区,而不是transfer_buffer指针所指向的那一个缓冲区。换句话说,如果没有设置这两个DMA的flag,那么USB Core就会使用setup_packet(仅对控制传输来说有意义)和transfer_buffer作为数据传输的缓冲区,然后下面两行就是把us的iobuf_dma和cr_dma赋给了urb的transfer_dma和setup_dma。143行到146行的注释表明,只要transfer_buffer被赋了值,那就假设有DMA缓冲区需要传输,于是就去设URB_NO_TRANSFER_DMA_MAP。关于DMA 这一段,因为比较难理解,所以我们多说几句。
首先,这里是两个DMA相关的flag:URB_NO_SETUP_DMA_MAP和URB_NO_TRANSFER_DMA_MAP。注意这两个是不一样的,前一个是专门为控制传输准备的,因为只有控制传输需要有这个Setup阶段,需要准备一个Setuppacket。我们只看后一个,关于transfer_buffer和transfer_dma的关系,当初同样用下面的方法申请了us->iobuf 的内存:
466 us->iobuf =usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,
467 GFP_KERNEL,&us->iobuf_dma);
468 if (!us->iobuf) {
469 US_DEBUGP("I/O buffer allocation failed/n");
470 return -ENOMEM;
471 }
这里就有us->iobuf和us->iobuf_dma这两个东西,但是我们注意到,148行和149行,在设置URB_NO_TRANSFER_DMA_MAP这个flag时,先做了一次判断,判断us->current_urb->transfer_buffer是否等于us->iobuf,这是什么意思呢?我们在什么地方对transfer_buffer赋过值?答案是在usb_fill_bulk_urb中,我们把us->iobuf传递了过去,它被赋给了urb->transfer_buffer,这样做就意味着我们这里将使用DMA传输,所以这里就设置了这个flag。倘若我们不希望进行DMA传输,那很简单,我们在调用usb_stor_ msg_common之前,不让urb->transfer_buffer指向us->iobuf就行了,反正这都是我们自己设置的。需要知道的是,transfer_buffer是给各种传输方式中真正用来数据传输的,而setup_packet仅仅是在控制传输中发送Setup的包的,控制传输除了Setup阶段之外,也会有数据传输阶段,这一阶段要传输数据还是得靠transfer_buffer,而如果使用DMA方式,那么就是使用transfer_dma。
154行,终于到了提交urb这一步了,usb_submit_urb得到调用,作为USB设备驱动程序,我们不需要知道这个函数究竟在做什么,只要知道怎么使用就可以了。它的定义在drivers/usb/core/urb.c中,我们得知道它有两个参数,一个是要提交的urb,另一个是内存申请的flag。这里我们使用的是GFP_NOIO,意思就是不能在申请内存时进行I/O操作。道理很简单,这是一个存储设备,调用usb_submit_urb很可能是因为我们要读些磁盘或者U盘,在这种情况如果申请内存的函数又再一次去读写磁盘,那就有问题了,什么问题?嵌套。什么叫申请内存的函数也会读写磁盘?因为内存不够。使用磁盘作为交换分区不就方便了,所以申请内存时可能要的内存在磁盘上,那就得交换回来。这不就读写磁盘了吗?所以我们为了读写硬盘而提交urb,那么这个过程中就不能再次有I/O操作了,这样做的目的是为了杜绝出现嵌套死循环。
于是我们调用了154行就可以往下走,剩下的事情USB Core和USB主机会去处理,至于这个函数本身的返回值,如果一切正常,status将是0。所以这里判断,如果status不为0那么就算出错了。162行,一个urb被提交了之后,通常我们会把us->flags中置上一个flag,US_FLIDX_URB_ACTIVE,让我们记录下这个urb的状态是活着的。
165行,这里我们再次判断us->flags,看是不是谁设置了aborting或者disconnected的flag。稍后我们会看到谁会设置这些flag,显然如果已经设置了这些flag的话,咱们就没必要往下了,这个urb可以取消了。
175行,引出时间机制
摘自 fudan_abc的Linux内核专栏 (免责声明:文章内容如涉及作品内容、版权和其它问题,请及时与我们联系,我们将在第一时间删除内容,文章内容仅供参考)