天天看點

全志科技公司A83T Qt Linux 支援gadget

用國産的CPU和linux系統做産品,由于以前對全志的晶片有些高成本效益的印象,所有選了全志的8核64位的A83T,看手冊是支援高速USB HOST和DRD(主從雙角色),産品是裝置類的,作為Device連接配接到電腦,看到linux有個Gadget,可以實作Device枚舉。

号稱支援ubuntu,linuxQt和Android三系統,實際上,Qt是可以支援的,但是gadget根本就出不來,Make menuconfig時選擇這些也不起作用,電腦根本無反應,但是跑Android系統沒有問題,是以,排除是硬體問題,由于和Android底層使用的是同一個Bsp,估計整個硬體的驅動應該是沒有問題,應該是某個或某幾個小細節沒有處理好,因為寄存器也基本不公開,也沒有可能去改查底層的寄存器設定,而且寄存器的設定理論上是不存在問題的,因為Android用的好好的。

網上查一部分資料,大緻對gadget有了點印象,因為之前調試過裸機的USB裝置通信,從插入計算機無反應來講,應該是連枚舉都沒有枚舉,同時用usb trace抓包,發現,的确,沒有檢測到Usb線上的反應,先從這一點入手,看看整個usb的系統在插入電腦的那一刻是怎麼做的。

先找到控制usb從裝置的底層代碼,分為兩部分,一部分是SOC晶片的裝置控制器,代碼裡叫udc,位置位于linux-3.4/drivers/usb/sunxi_usb/udc/檔案夾下,共有8個檔案

全志科技公司A83T Qt Linux 支援gadget

,基本上構成了對udc裝置的各種操作,然後和gadget裡的具體的usb裝置驅動綁定到一起,實作枚舉。因為我們做的是列印機裝置,是以用gadget的printer裝置進行枚舉,gadget的printer裝置在linux-3.4/drivers/usb/gadget/目錄下,有linux自帶的各種gadget裝置的枚舉,因為我們的比較簡單,隻用printer.c即可。

沒有别的辦法,先打日志,看看啟動過程,為什麼沒有枚舉,看來看去,發現udc裡面有個函數引出給别的地方,sunxi_usb_device_enable函數沒有被調用過,通過查找,在另外的檔案中有調用,位置在linux-3.4/drivers/usb/sunxi_usb/manager下,通過看這幾個檔案發現,終于明白了插入的過程了。裸機的usb插入是靠的usb裝置控制器引擎的中斷來啟動,linux下沒有研究過,不過從全志的做法上看是通過建立一個線程,定時檢測USB接口的狀态變化來初始化UDC,既可以通過vbus,id檢測,也可以通過dp,dm的變化檢測,這個設定在system_config.fex中可以進行設定。

通過對線程添加列印日志發現,插入usb和拔出時,線程均能非常好的檢測到usb的狀态變化,說明硬體和線程檢測都沒有問題,但是并沒有初始化enable裝置。找來找去發現了一個端倪,有一個函數叫做get_usb_gadget_functions,他是檢測到usb裝置插入後,進行驅動屬性檢測的,/sys/class/android_usb/android0/functions,檢測的是sysfs的一個屬性,乖乖,我哪有這個屬性啊,是以插入usb就退出了。本着最小改動的原則,給gadget的printer長出一個屬性來,也不費勁,就是填幾行代碼的事,基本從别的檔案中拷貝過來,啥功能也不同,就是讓這個檢測可以通過即可。這個改動完成之後,插入主機,終于可以調用sunxi_usb_device_enable,但還是沒有開始枚舉,這就怪了。隻能再看udc。

發現了一個變量static u8 is_udc_enable = 0; / is udc enable by gadget? /,再看看android的gadget使用的,有個屬性可以通過使用者層sysfs設定變量,瞬間想到以前用android手機時可以通過界面的勾選,選擇不同的枚舉裝置,選擇不枚舉隻充電。估計就出在這個變量上,直接設定為1.順利枚舉成功。

先看看沒和裝置互動時的流程

[ 78.068994] [sunxi_usb_udc]FUN:filtrate_irq_misc

[ 78.074384] [sunxi_usb_udc]irq:suspend

[ 78.078830] [printer]FUN:printer_disconnect

[ 78.078984] [printer]FUN:printer_reset_interface

[ 78.078984] [sunxi_usb_udc]FUN:sunxi_udc_ep_disable

[ 78.078984] [sunxi_usb_udc]FUN:sunxi_udc_nuke

[ 79.030069] [sunxi_usb_udc]FUN:sunxi_usb_device_disable

[ 79.036089] [printer]FUN:printer_disconnect

[ 79.041000] [printer]FUN:printer_reset_interface

[ 79.046380] [sunxi_usb_udc]FUN:sunxi_udc_stop_dma_work

大緻上是先挂起,然後複位接口,取消端點,然後停止一切相關。在看看讀取之後的流程

[ 1044.506474] [sunxi_usb_udc]FUN:filtrate_irq_misc

[ 1044.511917] [sunxi_usb_udc]irq:suspend

[ 1044.516310] [printer]FUN:printer_disconnect

[ 1044.516462] [printer]FUN:printer_reset_interface

[ 1044.516462] [sunxi_usb_udc]FUN:sunxi_udc_ep_disable

[ 1044.516462] [sunxi_usb_udc]FUN:sunxi_udc_nuke

[ 1044.516462] [sunxi_usb_udc]FUN:sunxi_udc_done

[ 1044.516462] [printer]FUN:rx_complete

死在gadget的rx_complete函數裡,找到函數所在的位置,一個非常可以的東西

spin_lock_irqsave(&dev->lock, flags);在這個之後列印一個資訊,發現,果然,是挂在了這裡。那肯定是有人和他沖突了,你也要搶這個,肯定是别人也要搶,因為正常的read過程也調用這個,就不會卡死,那他的上級不就是udc_nuke了,這個是唯一的不同之處,

nuke函數裡沒有啥,就是清空端點沒有完成的工作,那就再往上找找。

sunxi_udc_ep_disable函數在調用nuke之前,赤果果的寫着spin_lock_irqsave(&ep->dev->lock, flags),好吧,好吧,你高興就好,還是本着最小改動原則,告訴rx_complete,如果不是正常的資料接收,親,你别再搶了好伐。教訓完它,果然OK了。

繼續閱讀