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

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

423行,424行,退出或者断链接,就不要传递数据。

然后429行,usb_sg_init()函数被调用,这个函数来自drivers/usb/core/message.c,也就是说,它是USB核心层提供的函数,干什么用的?初始化sg请求。其第1个参数是struct usb_sg_request结构体的指针。这里我们传递了us->current_sg的地址给它,这里us->current_sg第一次派上用场,所以我们需要隆重介绍一下。

在structus_data中,定义了一个成员structusb_sg_request  current_sg。曾几何时我们见到过current_urb,这里又来了一个current_sg。也许你感觉很困惑。其实可以这样理解,之前我们知道struct urb表示的是一个USB请求,而这里structusb_sg_request实际上表示一个scattergather 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:

1350 struct usb_sg_request {

1351     int                     status;

1352     size_t                  Bytes;

1353

1354     /*

1355     *members below are private: to usbcore,

1356      * andare not provided for driver access!

1357      */

1358     spinlock_t              lock;

1359

1360     struct usb_device       *dev;

1361     int                     pipe;

1362     structscatterlist      *sg;

1363     int                     nents;

1364

1365    int                     entries;

1366    struct urb              **urbs;

1367

1368    int                     count;

1369     struct completion       complete;

1370 };

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

我们虽说不用仔细去看着三个函数内部是如何实现的,但至少得知道该传递什么参数吧。不妨来仔细看一下usb_sg_init()被调用时传递给它的参数。第1个刚才已经说了,就是sgrequest,第2个,需要告诉它是哪个USB设备要发送或接收数据,我们给它传递的是us->pusb_dev,第3个,是哪个管道,这个没什么好说的,管道是上面一路传下来的。第4个参数,这是专门适用于中断传输的,被传输中断端点的轮询率,对于批量传输,直接忽略,所以我们传递了0。第5个和第6个参数就分别是sg数组和sg数组中元素的个数。第7个参数,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()的代码,就会发现441行到448行这一段我们不会陌生,当年我们提交urb之前就有这么一段,在usb_stor_msg_common()函数中,只不过那时候是urb而不是sg,这两段代码之间何其相似!只是年年岁岁花相似,岁岁年年人不同啊!451行,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,然后返回之前,再来一次调用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 MassStorage Bulk-only spec里边就定义了这种情况,spec说了对这种情况,下一个阶段还是要照样进行。

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

最后,再解释一点。实际上USB Core这一层做的最人性化的一点就是对urb和对sg的处理了。写代码的人喜欢把数据传输具体化为request,urb和sg都被具体化为request,即请求。而USB Core的能耐就是让写设备驱动的人能够只要申请一个请求,调用USB Core提供的函数进行初始化,然后调用USB Core提供的函数进行提交,这些步骤都是固定的,完全就像使用傻瓜照相机一样,然后进程可以睡眠,或者可以干别的事情,之后USB Core会通知你。你就可以接下来干别的事情了。
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux系统中,可以通过配置和编译内核驱动来实现USB模拟U盘的功能。首先,需要加载相关的内核模块,例如drivers/usb/gadget/libcomposite.ko、drivers/usb/gadget/legacy/gadgetfs.ko、drivers/usb/gadget/legacy/g_mass_storage.ko和drivers/usb/gadget/function/usb_f_mass_storage.ko等模块。 接下来,需要在ARM板的Linux系统上进行配置和编译。现在大量的ARM CPU芯片都可以支持USB OTG2.0/3.0接口,该接口可以工作USB Host,也可以作为USB设备工作。通过配置OTG接口,可以将ARM板配置为U盘,实现ARM板与PC机之间的数据传输。以RK3399板为例,可以通过执行适当的命令来将RK3399板的Linux系统配置为U盘工作。 具体操作步骤如下: 1. 配置和编译内核驱动,确保相关的内核模块被加载。 2. 插入USB线,并执行“sudo insmod g_mass_storage.ko file=/dev/loop7 removable=1”命令。 3. 此时,PC机会发出“叮咚”的声音,表示PC机已经找到了U盘,表示U盘创建成功。 通过以上步骤,就可以在Linux系统上实现USB模拟U盘的功能。通过配置和编译内核驱动,以及正确地插入USB线和执行相应的命令,可以使ARM板的Linux系统被识别为U盘,从而实现与PC机之间的数据传输。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [利用USB OTG把ARM板(瑞芯微RK3399+Linux)模拟为U盘](https://blog.csdn.net/stevenqian/article/details/127224086)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值