知识屋:更实用的电脑技术知识网站
所在位置:首页 > 操作系统 > linux

《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内核专栏
(免责声明:文章内容如涉及作品内容、版权和其它问题,请及时与我们联系,我们将在第一时间删除内容,文章内容仅供参考)
收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜