首先言明,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上交叉编译,要改几个地方才行。
最后,我想问个问题,这些代码写好之后,提交了,没有人测过吗?????
本文详细分析了Linux 2.6.35.3版本中driver/usb/usb-skeleton.c和gadget/f_loopback.c存在的问题,导致USB设备在读写操作后出现故障。通过源码分析和实验,找到了解决问题的方案:初始化processed_urb为1以及在接收IN请求时正确初始化req。作者还提到这些问题在更新的Linux内核版本中已得到修复,并质疑这些代码是否经过充分测试。

1738

被折叠的 条评论
为什么被折叠?



