首先言明,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上交叉編譯,要改幾個地方纔行。
最後,我想問個問題,這些代碼寫好之後,提交了,沒有人測過嗎?????