linux-2.6.35.3 usb gadget zero 与skeleton 存在的问题

本文详细分析了Linux 2.6.35.3版本中driver/usb/usb-skeleton.c和gadget/f_loopback.c存在的问题,导致USB设备在读写操作后出现故障。通过源码分析和实验,找到了解决问题的方案:初始化processed_urb为1以及在接收IN请求时正确初始化req。作者还提到这些问题在更新的Linux内核版本中已得到修复,并质疑这些代码是否经过充分测试。

首先言明,linux-2.6.35.3 driver/usb/usb-skeleton.c  gadget/f_loopback.c均存在bug。

实验过程:

内核源码用linux-2.6.35.3是周立功MX287提供的。但上述两个源文件应该不是周立功公司提供的。

编译usb-skeleton.c,生成usb-skeleton.ko

编译gadget/zero.c 生成zero.ko.

准备两台设备,均为周立功MX287开发板A, B,A的otg口 和B的host口互联。

设备A  insmod usb-skeleton.ko

设备B :上insmod zero.ko loopdefault=1 。

 设备A 以阻塞的方式打开/dev/skel0, 先写一个一个hello world! 成功,然后去读直接卡死了,没有一点反应。开源的好处就是有源代码可以研究。分析源代码,通过加输出语句,发现第一次读就卡死在297行。

skel_read(....)

{

292         if (!dev->processed_urb) {
293                 /*
294                  * the URB hasn't been processed
295                  * do it now
296                  */
297                 wait_for_completion(&dev->bulk_in_completion);
298                 dev->bulk_in_copied = 0;
299                 dev->processed_urb = 1;
300         }

..

}

这个判断语句,意思是还有urb没有处理,我第一次读,哪里来的urb?这个processed_urb仅存在于struct usb_skel这个结构体中,并在是在probe中创建struct usb_skel dev, 当时全部初始化成0.而这个变量也仅仅在skel_read()中出现,问题那就简单了,在probe中把他初始化成1,在测试,OK,可以回读。这个问题在Linux4.0中的usb-skeleton.c中不存在,processed_urb这个成员变量直接就不存在了。OK, 排了第一个雷。

A端,能读,能写,但读写几次之后就又不行了,每次读失败,写也设备,真他妈的坑多。一开始仍然怀疑usb-skeleton.c。编译了一个Ubuntu10.04(编译嵌入式程序,这个是我第一个接触的linux 版本,启动快,又不需要什么其他新功能,好多前世如程序就在上面跑了,另外虚拟机里装了一个16.04, 启动慢,不怎么使用)下的PC上的驱动,加载测试,发现也是类似的情况,数次之后就OVER。通过usbmon查看,发现数据是发下去了,再读就太监了。但我的zero驱动也没有打印出错误呀。好了,一个说法了,另外一个说没收到,这特么不是调试程序经常遇到的问题么。到此,一般就要上USB逻辑分析仪了。这个鬼东西本来就是工作之余积累的新东西,玩玩而已,USB逻辑分析仪公司没有呀,示波器?等等看看再说吧。分析 一下zero驱动。貌似也没问题。OK, A设备加上读写次数,zero 也打印out, in次数,发现一个规律,32次之后就OVER。f_loopback.c里面32 和qlen,也就是一开始给out 端点分配的req的数量。将其修改成8,果然读写8次之后就GAME OVER.问题基本就在这里了,为什么是和qlen有关系呢?难道req在使用过程中出错了?

loopback_complete(struct usb_ep *ep, struct usb_request *req)

.......................

199         switch (status) {
200         
201         case 0:                         /* normal completion? */
202                 if (ep == loop->out_ep) {
203                         /* loop this OUT packet back IN to the host */
204                         req->zero = (req->actual < req->length);
205                         req->length = req->actual;
206                         status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
207                         if (status == 0)
208                                 return;
209 
210                         /* "should never get here" */
211                         ERROR(cdev, "can't loop %s to %s: %d\n",
212                                 ep->name, loop->in_ep->name,
213                                 status);
214                 }
215         
216                 /* queue the buffer for some later OUT packet */
217                 req->length = buflen;
219                 status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
220                 if (status == 0)
221                         return;

.................
236         case -ECONNABORTED:             /* hardware forced ep reset */
237         case -ECONNRESET:               /* request dequeued */
238         case -ESHUTDOWN:                /* disconnect from host */
239                 if(ep == loop->out_ep)
240                         printk(KERN_INFO"%s %d count %d free ep_out req\n", __func__, __LINE__,status);
241                 else if(ep == loop->in_ep)
242                         printk(KERN_INFO"%s %d count %d free ep_in req\n", __func__, __LINE__,status);
243                 free_ep_req(ep, req);
244                 return;
245         }

............................ 

然后就在 case -ESHUTDOWN:                /* disconnect from host */加打印代码,看到底有无释放,在哪个端点上被释放。

在上电测试,发现32次之后,并没有输出调试信息。在rmmod zero 的时候上述打印信息输出了,那些req全在out端点,这个和程序逻辑一致,每个读之后,req又挂载到了out端点之上。但zero为何不能再收到数据了呢? 这个时候,经验发挥了作用,去看看收到IN请求的时候,将req挂载到out端点和enable_loopback中挂载到out端点有何不同?enable_loopback调用的是 下面的函数alloc_ep_req,

182 struct usb_request *alloc_ep_req(struct usb_ep *ep)
183 {                       
184         struct usb_request      *req;
185                         
186         req = usb_ep_alloc_request(ep, GFP_ATOMIC);
187         if (req) {                              
188                 req->length = buflen;
189                 req->buf = kmalloc(buflen, GFP_ATOMIC);
190                 if (!req->buf) {
191                         usb_ep_free_request(ep, req);
192                         req = NULL;
193                 }
194         }
195         return req;
196 }      

alloc_ep_req 又最终调用了  fsl_alloc_request。

 835 fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
 836 {
 837         struct fsl_req *req = NULL;
 838 
 839         req = kzalloc(sizeof *req, gfp_flags);
 840         if (!req)
 841                 return NULL;
 842 
 843         req->req.dma = DMA_ADDR_INVALID;
 844         pr_debug("udc: req=0x%p   set req.dma=0x%x\n", req, req->req.dma);
 845         INIT_LIST_HEAD(&req->queue);
 846 
 847         return &req->req;
 848 }

除了req->length ,大部分其他参数都为0?回头看看收到out请求的时候,执行

204                         req->zero = (req->actual < req->length);
205                         req->length = req->actual;

enable_loopback 后out端点可用,但req->zero是0,嗯,那就先把zero设置成0,试一下,如果不可以,那就把req的状态恢复成enable_loopback时的状态。结果发现,妮玛OK了,回环读写了1万次都没有问题。

usb-skeleton.c f_loopback.c 中都存在问题,前者的问题是processed_urb没有正确初始化,后者的问题是收到IN请求,要把req还给out端点的时候没有正确的初始化。如果仅分析f_loopback.c linux 4.0上的f_loopback.c好像也有问题,但没有实测,linux-4.0上的usb-skeleton.c没有问题,要在2.6.35上交叉编译,要改几个地方才行。

最后,我想问个问题,这些代码写好之后,提交了,没有人测过吗?????

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值