linux-2.6.35.3 usb gadget zero 與skeleton 存在的問題

首先言明,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上交叉編譯,要改幾個地方纔行。

最後,我想問個問題,這些代碼寫好之後,提交了,沒有人測過嗎?????

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章