《Linux那些事儿之我是USB》我是U盘(34)迷雾重重的批量传输(三)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/fudan_abc/article/details/7103914

在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行,引出时间机制。
展开阅读全文

没有更多推荐了,返回首页