Linux那些事儿之我是U盘(47)迷雾重重的Bulk传输(五)

来源:互联网 发布:java入门电子书百度云 编辑:程序博客网 时间:2024/05/01 05:43

  usb_stor_bulk_transfer_sglist()这个函数有一定的蛊惑性,我们前面说过,之所以采用sglist,就是为了提高传输效率.我们更知道,sg的目的就是让一堆不连续的buffers一次DMA操作就都传输出去.其实在usb的故事中,事情并非如此.不过如果你对usb core里边的行为不关心的话,那就无所谓了.有些事情,你不知道也好.

  446,447,aborting了或者disconnecting,就不要传递数据了.

  然后452,usb_sg_init()函数被调用,这个函数来自drivers/usb/core/message.c,也就是说,她是usb核心层提供的函数,干嘛用的?初始化sg请求.其第一个参数是struct usb_sg_request结构体的指针.这里咱们传递了us->current_sg的地址给她,这里us->current_sg第一次派上用场,所以咱们需要隆重的介绍一下.struct us_data,定义了这么一个成员,struct usb_sg_request   current_sg.曾几何时咱们见到过current_urb,这里又来了一个current_sg.也许你感觉很困惑,这很正常,色彩容易让人炫目,文字容易让人迷惑,只有张爱玲对现实的认识是清醒的.其实可以这样理解,之前我们知道struct urb表征的是一个usb request,而这里struct usb_sg_request实际上表示的是一个scatter gather request,从我们非usb核心层的人来看,这两个结构体的用法是一样的.对于每次urb请求,我们所作的只是申请一个结构体变量或者说申请指针然后申请内存,第二步就是提交urb,即调用usb_submit_urb(),剩下的事情usb core就会去帮我们处理了,Linux中的模块机制酷就酷在这里,每个模块都给别人服务,也同时享受着别人提供的服务.就像我们常说的,你站在桥上看风景,看风景的人在楼上看你.明月装饰了你的窗子,你装饰了别人的梦.你要想跟别人协同工作,你只要按照人家提供的函数去调用,把你的指针你的变量传递给别人,其它的你根本不用管,事成之后你人家自然会通知你.同样对于sg request,usb core也实现了这些,我们只需要申请并初始化一个struct usb_sg_request的结构体,然后提交,然后usb core那边自然就知道该怎么处理了.闲话少说,先来看struct usb_sg_request结构体.她来自include/linux/usb.h:

988 /**
    989  * struct usb_sg_request - support for scatter/gather I/O
    990  * @status: zero indicates success, else negative errno
    991  * @bytes: counts bytes transferred.
    992  *
    993  * These requests are initialized using usb_sg_init(), and then are used
    994  * as request handles passed to usb_sg_wait() or usb_sg_cancel().  Most
    995  * members of the request object aren't for driver access.
    996  *
    997  * The status and bytecount values are valid only after usb_sg_wait()
    998  * returns.  If the status is zero, then the bytecount matches the total
    999  * from the request.
   1000  *
   1001  * After an error completion, drivers may need to clear a halt condition
   1002  * on the endpoint.
   1003  */
   1004 struct usb_sg_request {
   1005         int                     status;
   1006         size_t                  bytes;
   1007
   1008         /*
   1009          * members below are private to usbcore,
   1010          * and are not provided for driver access!
   1011          */
   1012         spinlock_t              lock;
   1013
   1014         struct usb_device       *dev;
   1015         int                     pipe;
   1016         struct scatterlist      *sg;
   1017         int                     nents;
   1018
   1019         int                     entries;
   1020         struct urb              **urbs;
   1021
   1022         int                     count;
   1023         struct completion       complete;
   1024 };

整个usb系统都会使用这个数据结构,如果我们希望使用scatter gather方式的话.usb core已经为我们准备好了数据结构和相应的函数,我们只需要调用即可.一共有三个函数,她们是usb_sg_init,usb_sg_wait,usb_sg_cancel.我们要提交一个sg请求,需要做的是,先用usb_sg_init来初始化请求,然后usb_sg_wait()正式提交,然后我们该做的就都做了.如果想撤销一个sg请求,那么调用usb_sg_cancel即可.

  咱们虽说不用仔细去看着三个函数内部是如何实现的,但至少得知道该传递什么参数吧.不妨来仔细看一下usb_sg_init()被调用时传递给她的参数.头一个刚才已经说了,就是sg request,第二个,需要告诉她是哪个usb设备要发送或接收数据,咱们给她传递的是us->pusb_dev,第三个,是哪个pipe,这个没什么好说的,pipe是上面一路传下来的.第四个参数,这是专门适用于中断传输的,被传输中断端点的轮询率,对于bulk传输,直接忽略,所以咱们传递了0.第五个和第六个参数就分别是sg数组和sg数组中元素的个数.然后第七个参数,length,传递的就是咱们希望传输的数据长度,最后一个是SLAB flag,内存申请相关的一个flag.如果驱动程序处于block I/O路径中应该使用GFP_NOIO,咱们这里SLAB_NOIO实际上是一个宏,实际上就是GFP_NOIO.不要问我为什么用SLAB_NOIO或者说GFP_NOIO,无可奉告.(如果你真的想知道为什么的话,回去看当初我们是如何调用usb_submit_urb(),理由当时就已经讲过了.)这个函数成功返回值为0,否则返回负的错误码.初始化好了之后就可以为us->flags设置US_FLIDX_SG_ACTIVE,对这个flag陌生吗?还是回去看usb_submit_urb(),当时我们也为urb设置了这么一个flag,US_FLDX_URB_ACTIVE,其实历史总是惊人的相似.当初我们对待urb的方式和如今对待sg request的方式几乎一样.所以其实是很好理解的.

  对比一下当初调用usb_submit_urb()的代码,就会发现464471这一段我们不会陌生,当年咱们提交urb之前就有这么一段,usb_stor_msg_common()函数中,只不过那时候是urb而不是sg,这两段代码之间何其的相似!只是年年岁岁花相似,岁岁年年人不同啊!然后474,usb_sg_wait()函数得到调用.她所需要的参数就是sg request的地址,咱们传递了us->current_sg的地址给她.这个函数结束,US_FLIDX_SG_ACTIVE这个flag就可以clear掉了.返回值被保存在us->current_sg.status,然后把她赋给了result.us->current_sg.bytes保存了实际传输的长度,把她赋给*act_len,然后返回之前,once more,调用interpret_urb_result()转换一下结果.

最后,usb_stor_bulk_transfer_sg()函数返回之前还做了一件事,将剩下的长度赋值给了*residual.*residual是形参,实参是&srb->resid.而最终usb_stor_bulk_transfer_sg()返回的值就是interpret_urb_result()翻译过来的值.但是需要明白的一点是,这个函数的返回就意味着Bulk传输中的关键阶段,即数据阶段的结束.剩下一个阶段就是状态阶段了,要传递的是CSW,就像当初传递CBW一样.

  回到usb_stor_Bulk_transport()函数中来,判断结果是否为USB_STOR_XFER_ERROR或者USB_STOR_XFER_LONG,前者表示出错,这没啥好说的.而后者表示设备试图发送的数据比咱们需要的数据要多,这种情况咱们使用一个fake sense data来向上层汇报,出错了,但是和一般的出错不一样的是,告诉上层,这个命令别再重发了.fake_sense刚开始初始化为0,这里设置为1,后面将会用到.到时候再看.目前只需要知道的是,这种情况并不是不存在,实际上usb mass storage bulk-only spec里边就定义了这种情况,spec说了对这种情况,下一个阶段还是要照样进行.至于设备干嘛要这样做,那就只有天知道了,就是说你明明只是对他说,给我十块钱”,他却硬塞给你一百块钱.(我只是打个比方,别做梦了.)文雅一点说,这叫,原想采撷一枚红叶,你却给了我整个的枫林.

  最后,解释一点,USB_STOR_XFER_LONG只是我们自己定义的一个宏,实际上是由interpret_urb_result()翻译过来的,真正的从usb core那一层传递过来的结果是叫做-EOVERFLOW,这一点在interpret_urb_result函数中能找到对应关系.-EOVERFLOW我们就常见了,顾名思义,就是溢出.

  最后的最后,再解释一点,实际上usb core这一层做的最人性化的一点就是对urb和对sg的处理了.写代码的人喜欢把数据传输具体化为request,urbsg都被化作request,即请求.usb core的能耐就是让你写设备驱动的人能够只要申请一个请求,调用usb core提供的函数进行初始化,然后调用usb core提供的函数进行提交,这些步骤都是固定的,完全就像使用傻瓜照相机一样,然后进程可以睡眠,或者可以干别的事情,完事之后usb core会通知你.然后你就可以接下来干别的事情了.我做一个比方,就好比你考四六级,找了一个枪手,让他去给你考,你只要告诉他你的基本信息,把你的准考证给他,然后你就不用管别的什么了,剩下的事情他会去处理,然后你也不用担心完事之后他不会通知你,这简直是不容置疑的,因为你还没给钱呢.明白了不,小朋友?