這個想法之前就在腦袋里有過,最近公司產品要用到,所以多做了些了解。
1. USB 簡介
USB 是 Universal Serial Bus 的縮寫,從字面上看,就是通用串行總線的意思。從物理上看,其實就是一對差分線,連接兩台設備后,相互間進行數據傳輸。加上另外兩路供電( 5V 和 GND)線,一共是 4 根線。
那么,既然是只有一對差分線,那么該如何決定由誰傳給誰呢(如果兩邊同時在線上建立電平,線路上的電平會是不確定態的,以致無法通信)?這就要說到 USB 傳輸的一個重要基礎:“詢問-應答” 機制—— Device(slave) 設備通常是處在等待狀態,只有 HOST 側設備發起詢問、請求,它才會在接下來的時間片中使用數據線向 HOST 發送數據。
那么,誰是 HOST,誰是 SLAVE 又是由什么來決定的呢?答案是硬件。也就是說,你 USB 后面的那塊驅動芯片如果是 HOST,那么,這個 USB 只能做 HOST 用了。反之,SLAVE 亦然。比如我們經常見到的,PC 上的 USB HOST 連接到 U盤、鼠標、鍵盤這些 SLAVE 設備。
后來有人覺得這樣一個設備只能是 HOST 或者只能是 SLAVE 太死板了,所以又發明了 USB OTG。USB OTG(on-the-go,大意為在使用時切換身份)是在原來 4 根線的基礎上,又加了一根線,ID。那塊 USB 后面的驅動芯片,就可以根據這根線,來選擇自己到底該扮演 HOST 還是 SLAVE 的角色。后面我們單獨介紹。
另外,因為使用一對差分線進行數據傳輸,所以,USB 又采用了基於 HUB 的星形拓撲結構(包括根控制器,最多7 層拓撲,且7層已不具備掛載 HUB 能力,只能是功能設備)。所以,更確切來說,“HOST-SLAVE“ 是在由 HUB 支持的物理鏈路之上的傳輸機制。同時,HUB 本身也是一個 USB SLAVE 設備。

2. USB 連接過程概述
下圖很好的解釋了 USB 的連接過程。需注意,1)如上圖的拓撲結構所示,每個網絡內只能有一個 HOST。2) HOST 和 SLAVE 之間,可以一對一直連,也可以通過 HUB ,一個 HOST 對應多個 SLAVE。

基本狀態包括:設備插入、設備上電、設備復位、設備獲得地址、設備配置完成、掛起狀態。下面略做解釋。
設備插入:HOST 側(可能是在 HUB 上,也可能是直接在 host controller 上)根據 D+ 和 D- 口的阻抗變化,來判斷是否有 device 插入,以及判斷插入設備的速度類型。
設備上電:因為 USB 口有供電功能(5V DC,多為 500-1000 mA ),所以,設備又分為 Bus powered 和 self powerd。當然,即使是設備自己供電,我們也認為只有當設備已連接,它才進入powered 狀態。設備一旦重新上電,后面的連接操作要重新執行一次。
設備復位:因為只有一對差分線,如果兩邊同時操作,那線路上的電平將是不確定態,根本無法通信。所以,上電后,USB device(slave)默認為等待狀態,不進行任何動作,直到 HOST 發給它一個 Reset 請求。復位完成,意味着低速/全速/高速的物理通路已經建立。
設備獲得地址:設備復位成功后,將獲得一個由 HOST 分配的地址。分配地址的對話,將在設備的 endpoint0 上完成。
設備配置完成:握手?配置?反正這一階段完成,意味着設備已經 ready了。
掛起狀態:所有 USB Device(slave)在空閑一段時間后,都必須將自己掛起,並保持自己的狀態,無論設備已經被分配地址還是沒有分配地址。
3. OTG
OTG 增加了一根可以動態配置為 HOST 或者 DEVICE(slave)的數據線,以 micro USB 接頭為例,其引腳分配如下:

因為傳統的 USB 線纜為 4 根線,所以,要將 OTG 設備接入,需對其進行配置(硬件短接):
1. 當配置 OTG 設備為 USB Device(slave) 時,將 ID 腳懸空。
2. 當配置 OTG 設備為 USB HOST時,將 ID 腳接地。
所以,我們這里,需要將OTG腳懸空,來將其配置為 Slave 設備。硬件上面,買來的 OTG 轉接線默認把 OTG USB 設備設置為 HOST,他們 ID 腳都是接地的。而我們是需要把 OTG 設備當作 Device(slave)來用,所以,最終選擇了將板子上的 ID 線割斷,使其懸空。
4. 一般的實現結構
前面我們討論了硬件部分,同時,作為通訊接口,不可能不需要系統及軟件層面的協作。先給出一張比較常見的 USB 通訊模型圖,然后我們再做解釋:

對於整個 USB 通訊過程,我們可以粗略的將其分為“總線層”、“功能層”和“設備層”三個層次。這三個層次的划分,主要是為了問題的集中解決。其中:
a. 總線層負責解決“點到點”的問題,主要是保障上一層可以和相鄰的端點“對話”,一般還會提供硬件 buffer 公上層使用;
b. 設備層才有了設備的概念,HOST 通過 Device(Slave)的 Endpoint0 對其進行配置,准備好數據管道給上一層使用;
c. 功能層在是 Device(Slave)功能的具體實現,才能看到一個個“鮮活”的設備,才是我們看得到的 U盤、鼠標這些設備。
以某安裝 Linux 的 PC 為例,作為 HOST,其中這三部分工作分別由控制器(如 EHCI 、UHCI 、OHCI),USB CORE (對前面控制器的內核支持、設備管理功能等)和 USB 上層驅動(usbmouse、usbkbd、usb-storge)。
我們要實現的 USB 串口,就是屬於 USB 上層驅動部分的實現。只不過,我們是通過類似於這里 HOST 的結構,實現了一個 Device(Slave)。
5. g_serial.ko
當前內核 3.0.8 支持 Gadget Serial 接口。也就是說,如果我們有一個硬件的 USB SLAVE(可以是由 OTG 支持的), 這一驅動可以支持我們實現一個軟件的 USB 串口;就像由 PL2303 或者 HM340 硬件實現的 USB Serial 一樣。 只有 HOST 控制器是不行的。不管是對 HOST 側的PC,還是我們添加 Gadget Serial 驅動支持的 PC,這條鏈路看起來都只是一個普通的串口連接。其源代碼在 /drivers/usb/gadget/serial.c,另外還有文檔 Documentation/usb/gadget_serial.txt。可自行閱讀。(其實,谷歌的 ADB 工具和這個是差不多的東西,可能甚至只有驅動號不一樣。)
具體應用時,我們並不需要做太多修改。。。只需要配置,編譯就足夠了。我把它編譯成了 module,所以,需要在文件系統起來之后再做一次 modprobe。
至於 HOST 側,據說是都不需要驅動的,但是,我在 Windows 上用的時候,還是安裝了 gadget serial v2.4 的(據說不支持 64 位系統,未驗證),UBUNTU 上即插即用。
