1. 程式人生 > >USB協議架構分析

USB協議架構分析

一、USB的一些基本概念

  1. 管道(Pipe) 是主機和裝置端點之間資料傳輸的模型,共有兩種型別的管道:無格式的
    流管道(Stream Pipe)和有格式的資訊管道(Message Pipe)。任何USB 裝置一旦上電就存在
    一個資訊管道,即預設的控制管道,USB 主機通過該管道來獲取裝置的描述、配置、狀態,並
    對裝置進行配置。

  2. 端點(Endpoint) 是USB 裝置中的可以進行資料收發的最小單元,支援單向或者雙向的資料傳
    輸。裝置支援端點的數量是有限制的,除預設端點外低速裝置最多支援2 組端點(2 個輸入,2
    個輸出),高速和全速裝置最多支援15 組端點。

  3. 介面(Interface) 應用軟體通過和裝置之間的資料交換來完成裝置的控制和資料傳輸。通常
    需要多個管道來完成資料交換,因為同一管道只支援一種型別的資料傳輸。用在一起來對裝置進
    行控制的若干管道稱為裝置的介面.

  4. 裝置和端點之間關係: 一個USB 裝置可以包括若干個端點,不同的端點以端點編號和方向區分。
    不同端點可以支援不同的傳輸型別、訪問間隔以及最大資料包大小。除端點0 外,所有的端點只支
    持一個方向的資料傳輸。端點0 是一個特殊的端點,它支援雙向控制傳輸。管道和端點關聯,和
    關聯的端點有相同的屬性,如支援的傳輸型別最大包長度傳輸方向等。

  5. 描述符(Descriptor)

    描述裝置的屬性(Attributes). 它本身是一個數據結構, 第一個位元組表示
    描述符的大小(位元組數), 第二個位元組表示描述符的型別(Type). 描述符的種類有:
    在這裡插入圖片描述

  1. 裝置描述符(Device), 描述一個裝置的一般資訊.
    在這裡插入圖片描述
  2. 裝置修飾描述符(Device_Qualifier), 描述一個高速裝置在其它速度下該如何變化的資訊.
  3. 配置描述符(Configuration), 描述一個特定的裝置配置, 如介面的數目等. 一個USB裝置有一個或多個配置描述符. 每個配置有一個或多個介面並且每個介面有0個或多個端點.
    在這裡插入圖片描述
  4. 其它速度配置描述符(Other_speed_configuration)
    , 描述高速裝置在其它可能的速度下的
    一個配置.
  5. 介面描述符(Interface), 描述一種配置中的一個特定的介面.
  6. 端點描述符(Endpoint), 描述主機需要的去決定端點所需頻寬的資訊. 這個描述符只能附加在GetDescriptor()或GetDescriptor()請求中傳送, 不能單獨傳送. 端點0沒有此描述符.
    在這裡插入圖片描述
    在這裡插入圖片描述
  7. 字串描述符(String), 第0個字串描述符指定裝置支援的語言, 其它的描述符則各包含
    一個UNICODE字串. 裝置描述符, 配置描述符 和 介面描述符可能會包含字串描述符.
  1. USB裝置請求(USB Device Request) 請求是從主機通過控制管道傳送到裝置.
    標準的裝置請求有:
  1. Clear Feature
  2. Get Configuration
  3. Get Descriptor
  4. Get Interface
  5. Get Status
  6. Set Address
  7. Set Configuration
  8. Set Descriptor
  9. Set Feature
  10. Set Interface
  11. Synch Frame

二、USB3.0系統拓撲結構

系統是最多隻能有7層的樹狀結構
最多隻能支援127個裝置和Hub
Host+RootHub永遠是在第一層
複合(Compound)裝置一般佔兩層
功能裝置不能作為非葉節點, 只有Hub才可以

USB3.0 Hub其實包含了一個USB2的Hub和一個SuperSpeed Hub

三、 USB3.0的重要特性:

  1. 增加了一個重要的資料傳輸速率
  2. 點對點方式傳輸包, 使活動鏈路數目達到最少
  3. 非同步方式的通知功能, 去除了輪詢方式的必要
  4. 基於鏈路級的電源管理, 這是匯流排結構的基礎設計
  5. 向後相容USB2.0, 驅動級和物理層級別上都達到了相容的目的

四、USB3.0與USB2.0的區別

  1. 資料傳輸速率, 3.0是SuperSpeed 5.0Gbps,
    2.0是 1.5Mbps, 12Mbps或480Mbps

  2. 資料介面, 3.0是全雙工,獨立於USB 2.0訊號的四路差分訊號,支援同時雙向資料傳輸
    2.0是半雙工,雙路差分訊號, 單向資料傳輸, 需要事先協商好總路線的傳輸方向

  3. 訊號線數目, 3.0是4路SuperSpeed資料線, 2路HighSpeed資料線和2路電源及地線
    2.0是2路LS/FS/HS資料線, 2路電源及地線

  4. 匯流排事務協議, 3.0是主機主導的非同步方式的傳輸流量控制, 包傳輸是能顯式地進行路由
    2.0是主機主導的輪詢方式的傳輸流量控制, 包傳輸是通過廣播方式到所有裝置

  5. 電源管理, 3.0是多級別的鏈路電源管理, 支援Idle, sleep和suspend狀態
    2.0是在埠級進行管理, 可以在entry/exit上有兩種級別的掛機狀態

  6. 匯流排電源, 3.0是和USB 2.0差不多, 只是未配置的電源有50%的增幅, 已配置的電源有80%的增幅

  7. 主機控制器,3.0用的是xHCI,2.0則是EHCI。xHCI中提供了虛擬化技術支援。

五、USB3.0電源狀態

  1. U0, Link active
  2. U1, Link idle - fast exit. 退出延時us級
  3. U2, Link idle - slow exit. 退出延時us-ms級
  4. U3, Link suspend. 無時鐘訊號, 退出延時us-ms級

六、USB3.0資料包型別

  1. Link Management Packets, 鏈路管理包, 僅發生在兩個相連的埠之間,主要是用來進行鏈路管理
  2. Transaction Packets, 事務包, 發生在裝置和主機之間, 用來控制資料包的流量, 配置裝置和Hubs. 它沒有資料
  3. Data Packets, 資料包, 發生在裝置和主機之間. 它包括兩部分:包頭和實際資料. 其中資料部分還包括一個32位的CRC校驗碼來保證資料的完整性.
  4. Isochronous Timestamp Packets, 同步時間戳包, 它是唯一的多播方式傳送的. 傳送方向是從主機到所有U0狀態的裝置.

七、USB傳輸型別

1. 控制傳輸(Control Transfers)
控制傳輸是一種可靠的雙向傳輸,一次控制傳輸可分為三個階段。

第一階段為從HOST 到Device 的SETUP 事務傳輸,這個階段指定了此次控制傳輸的請求型別;
第二階段為資料階段,也有些請求沒有資料階段;
第三階段為狀態階段,通過一次IN/OUT 傳輸表明請求是否成功完成。

控制傳輸通過控制管道在應用軟體和Device 的控制端點之間進行,控制傳輸過程中傳輸的資料是
有格式定義的,USB 裝置或主機可根據格式定義解析獲得的資料含義。其他三種傳輸型別都沒有格式定義。

控制傳輸對於最大包長度有固定的要求。對於高速裝置該值為64Byte;對於低速裝置該值為8;全速裝置
可以是8 或16 或32 或64。
在這裡插入圖片描述
2. 批量傳輸(Bulk Transfers)
批量傳輸是一種可靠的單向傳輸,但延遲沒有保證,它儘量利用可以利用的頻寬來完成傳輸,適合資料量比較大的傳輸。

低速USB 裝置不支援批量傳輸,高速批量端點的最大包長度為512,全速批量端點的最大包長度可以為8、16、32、64。

批量傳輸在訪問USB 匯流排時,相對其他傳輸型別具有最低的優先順序,USB HOST 總是優先安排其他型別的傳輸,
當匯流排頻寬有富餘時才安排批量傳輸。高速的批量端點必須支援PING 操作,向主機報告端點的狀態,NYET
表示否定應答,沒有準備好接收下一個資料包,ACK 表示肯定應答,已經準備好接收下一個資料包。
在這裡插入圖片描述
3. 中斷傳輸(Interrupt Transfers)
中斷傳輸是一種輪詢的傳輸方式,是一種單向的傳輸,HOST 通過固定的間隔對中斷端點進行查詢,若有
資料傳輸或可以接收資料則返回資料或傳送資料,否則返回NAK,表示尚未準備好。中斷傳輸的延遲有保
證,但並非實時傳輸,它是一種延遲有限的可靠傳輸,支援錯誤重傳。

對於高速/全速/低速端點,最大包長度分別可以達到1024/64/8 Bytes。高速中斷傳輸不得佔用超過80%的
微幀時間,全速和低速不得超過90%。中斷端點的輪詢間隔由在端點描述符中定義,全速端點的輪詢間隔可
以是1255mS,低速端點為10255mS,高速端點為(2interval-1)*125uS,其中interval 取1到16 之間的值。

除高速高頻寬中斷端點外,一個微幀內僅允許一次中斷事務傳輸,高速高頻寬端點最多可以在一個微幀內進
行三次中斷事務傳輸,傳輸高達3072 位元組的資料。
在這裡插入圖片描述
4. 同步傳輸(Isochronous Transfers)
同步傳輸是一種實時的、不可靠的傳輸,不支援錯誤重發機制。只有高速和全速端點支援同步傳輸,高速同步端
點的最大包長度為1024,低速的為1023。

除高速高頻寬同步端點外,一個微幀內僅允許一次同步事務傳輸,高速高頻寬端點最多可以在一個微幀內進行三
次同步事務傳輸,傳輸高達3072 位元組的資料。全速同步傳輸不得佔用超過80%的幀時間,高速同步傳輸不得佔用
超過90%的微幀時間。

同步端點的訪問也和中斷端點一樣,有固定的時間間隔限制。

在主機控制器和USB HUB 之間還有另外一種傳輸——分離傳輸(Split Transaction),它僅在主機控制器和HUB 之間執行,通過分離傳輸,可以允許全速/低速裝置連線到高速主機。分離傳輸對於USB 裝置來說是透明的、不可見的。

八、 OTG協議

OTG裝置採用Mini-AB插座,相對於傳統的USB資料線,Mini-AB介面多了一根資料線ID,ID線是否接入將Mini-AB介面分為Mini-A和Mini-B介面兩種型別。在OTG裝置之間資料連線的過程中,通過OTG資料線Mini-A和Mini-B介面來確定OTG裝置的主從:接入Mini-A介面的裝置預設為A裝置(主機裝置);接入Mini-B介面的裝置,預設為B裝置(從裝置)。

A裝置和B裝置無需交換電纜介面,即可通過主機交換協議(HNP)實現A、B裝置之間的角色互換。同時,為了節省電源,OTG允許匯流排空閒時A裝置判斷電源。此時,若B裝置希望使用匯流排,可以通過會話請求協議(SRP)請求A裝置提供電源。
8.1 HNP(主機交換)協議

當Mini-A介面接入A裝置並確定A裝置為主機時;若B裝置希望成為主機,則A裝置向B裝置傳送SetFeature命令,允許B裝置進行主機交換。B裝置檢測到匯流排掛起5ms後,即掛起D+並啟動HNP,使匯流排處於SE0狀態。此時A裝置檢測到匯流排處於SE0狀態,即認為B裝置發起主機交換,A裝置進行響應。待B裝置發現D+線為高電平而D-線為低電平(J狀態),表示A裝置識別了B裝置的HNP請求。B裝置開始匯流排復位並具有匯流排控制權,主機交換協議完成。

8.2 SRP(會話請求)協議

對於主機,要求能響應會話請求;對於裝置,僅要求能夠發起SRP協議。OTG裝置,不僅要求發起SRP,而且還能響應SRP請求。
SRP分為資料線脈衝調製和電壓脈衝調兩種方式,B裝置發起SRP必須滿足以下兩個條件:
1) B裝置檢測到A裝置低於其有效的電壓閾值,同時B裝置低於有效的電壓閾值。
2) B裝置必須檢測到D+和D-資料線至少在2ms的時間內低於有效閾值,即處於SE0狀態。

資料線脈衝調製會話請求:B裝置必須等到滿足以上兩個條件後,將資料線接入上拉電阻一定的時間,以備A裝置過濾資料線上的瞬間電壓。與此同時,B裝置上拉D+以便於在全速模式下進行初始化操作。A裝置在檢測到D+變為高電平或D-變為低電平時產生SRP指示訊號。

Vbus脈衝調製會話請求:B裝置同樣需等待滿足上述兩個初始化條件,然後B裝置通過對電容充電以提高匯流排電壓,待達到總線上的電壓閾值,喚醒A裝置。在充電過程中,一定要保證充電的電壓峰值在一定的範圍以避免燒壞A裝置。
3. USB驅動架構

USB驅動架構如下圖所示:

在這裡插入圖片描述
3.1 USB主機端驅動
在這裡插入圖片描述
USB核心(USBD)是整個USB驅動的核心部分,從上圖可知,一方面USBD對接收到USB主機控制器的資料進行處理,並傳遞給上層的裝置端驅動軟體;同時也接收來自上層的非USB格式資料流,進行相應的資料處理後傳遞給USB主機控制器驅動。
在這裡插入圖片描述
USB資料傳輸都以URB(USB Request Block)請求、URB生成、URB遞交、URB釋放為主線。從上圖可知,當載入控制器驅動之後,註冊根據集線器,hub和hcd驅動成為一個整體。接著,主機通過控制傳輸獲取裝置的控制描述符等資訊,接著詳述整個控制傳輸的流程。usb_submit_urb依據是否連線到根集線器來決定呼叫urb_enqueue或rh_urb_enqueue函式。
USB從裝置通過集線器或根集線器連線到USB主機上。比如:主機通過根集線器與外界進行資料互動,根集線器通過探測資料線狀態的變化來通知USB主機是否有USB外圍裝置接入。
在主機端控制器驅動載入的過程中,註冊了根集線器,然後匹配了相應的hub驅動程式,同時完成了對Hub的輪詢函式和狀態處理函式的設定。這樣,一旦hub集線器的狀態發生變化,就會產生相應的中斷,主機端控制器就會執行相應的中斷處理函式,下圖為hub驅動程式的流程圖。
在這裡插入圖片描述

   USB Core中的usb_init()函式中完成了對hub執行緒(khubd,在usb_hub_init函式中真正地建立)的建立,然後完成相應裝置的探測。主機端控制器驅動進行探測時,將hub驅動和主機端控制器驅動結合在一起,相互之間完成呼叫。 相對於大容量儲存裝置與主機之間通過控制/批量傳輸,集線器與主機之間通過中斷/控制方式完成資料互動。

3.2 USB裝置端驅動
在這裡插入圖片描述

在這裡插入圖片描述

從上圖可知,裝置端驅動包含兩部分:
1) 底層裝置控制器驅動
2) 上層大容量儲存類驅動

3.2.1 裝置控制器驅動

USB裝置控制器驅動主要實現Gadget API定義的函式和中斷服務函式,可按功能劃分為:API函式實現模組和中斷處理模組。
API函式主要實現Gadget API定義的函式功能,如結構體usb_ep_ops和usb_gadget_ops中的函式、usb_gadget_register_driver函式。這些函式是供Gadget Driver呼叫。
中斷處理模組主要處理裝置控制器產生的各種中斷,包括端點中斷、復位、掛起等中斷。

在這裡插入圖片描述

上圖為裝置端控制器基本架構,主要完成了Gadget驅動和控制器驅動繫結、usb_gadget_register_driver註冊。
3.3 OTG驅動
在這裡插入圖片描述

OS_FS: 檔案系統
USBD: USB核心
HCD: 主機控制器驅動
UDC: 裝置端控制器驅動

OTG裝置支援HNP和SRP協議。OTG裝置通過USB OTG電纜連線到一起,其中接Mini-A介面的裝置為A裝置,預設為主機端,Mini-B介面的裝置預設為B裝置。當A、B裝置完成資料互動之後,A、B裝置之間的USB OTG電纜進入掛起狀態,如下圖所示:
在這裡插入圖片描述

當B裝置寫入b_bus_req,向A裝置發起HNP請求。待A裝置響應之後,A裝置傳送a_set_b_hnp_en,B裝置響應之後即進入主機狀態,同時傳送請求使用A裝置set_device,這樣A、B裝置完成主從交換。
4. USB 傳輸流程
在這裡插入圖片描述
4.1 USB初始化過程

USB驅動作為一個系統,集成了眾多的驅動模組,註冊過程非常複雜。從USB系統的角度來說,USB主機驅動主要包含:

  1. USB核驅動

  2. 主機控制器驅動

  3. 集線器驅動

驅動的載入執行流程如下圖所示:

USB初始化過程
4.1.1 USB Core的初始化

USB驅動從USB子系統的初始化開始,USB子系統的初始化在檔案driver/usb/core/usb.c
[cpp] view plaincopy

subsys_initcall(usb_init);  
module_exit(usb_exit);  

    subsys_initcall()是一個巨集,可以理解為module_init()。由於此部分程式碼非常重要,開發者把它看作一個子系統,而不僅僅是一個模組。USB Core這個模組代表的不是某一個裝置,而是所有USB裝置賴以生存的模組。在Linux中,像這樣一個類別的裝置驅動被歸結為一個子系統。subsys_initcall(usb_init)告訴我們,usb_init才是真正的初始化函式,而usb_exit將是整個USB子系統結束時的清理函式。

4.1.2 主機控制器的初始化及驅動執行(以EHCI為例)

   module_init(otg_init); 模組註冊
   static init __init otg_init(void);
   platform_driver_register(); 平臺註冊
   static int __init otg_probe(struct platform_device *pdev); 探測處理函式
   reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); 獲取暫存器資訊
   data = platform_get_resource(pdev,IORESOURCE_MEM, 1); 獲取記憶體資訊
   irq = platform_get_irq(pdev,0); 獲取中斷號
   usb_create_hcd(&otg_hc_driver, &pdev->dev, pdev->dev.bus_id);

分配和初始化HCD結構體。對裝置資料空間進行分配,初始化計數器、匯流排、定時器、hcd結構體各成員值。

ret = usb_add_hcd(hcd,irq,SA_INTERRUPT);

完成HCD結構體的初始化和註冊。申請buffer,註冊匯流排、分配裝置端記憶體空間,向中斷向量表中申請中斷,註冊根集線器,對根集線器狀態進行輪詢。
4.1.3 註冊集線器

   register_root_hub(hcd);

在USB系統驅動載入的過程中,建立了集線器的執行緒(khubd),並且一直查詢相應的執行緒事務。HCD驅動中,將集線器作為一個裝置新增到主機控制器驅動中,然後進行集線器埠的初始化。在USB主機看來,根集線器本身也是USB主機的裝置。USB主機驅動載入完成之後,即開始註冊根集線器,並且作為一個裝置載入到主機驅動之中。
USB主機和USB裝置之間進行資料互動,USB裝置本身並沒有匯流排控制權,U盤被動地接收USB主機發送過來的資訊並做出響應。USB主機控制器與根集線器構成了主機系統,然後外接其它的USB裝置。
為了更好地探測到根集線器的狀態變化,USB主機控制器驅動增加了狀態輪詢函式,以一定的時間間隔輪詢根集線器狀態是否發生變化。一旦根集線器狀態發生變化,主機控制器就會產生相應的響應。
USB主機和USB裝置之間的資料傳輸以URB(USB Request Block)的形式進行。
4.2 URB傳輸過程

USB初始化過程中,無論是主機控制器驅動還是根集線器驅動,都是通過URB傳輸獲取裝置資訊。

4.2.1 申請URB

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
為urb分配記憶體並執行初始化。

4.2.2 初始化URB
初始化具體的urb包

static inline void usb_fill_bulk_urb(struct urb *urb,  
     struct usb_device *dev,  
     unsigned int pipe,  
     void *transfer_buffer,  
     int buffer_length,  
     usb_complete_t complete_fn,  
     void *context)  
  
static inline void usb_fill_control_urb(struct urb *urb,  
    struct usb_device *dev,  
    unsigned int pipe,  
    unsigned char *setup_packet,  
    void *transfer_buffer,  
    int buffer_length,  
    usb_complete_t complete_fn,  
    void *context)  
static inline void usb_fill_int_urb(struct urb *urb,  
    struct usb_device *dev,  
    unsigned int pipe,  
    void *transfer_buffer,  
    int buffer_length,  
    usb_complete_t complete_fn,  
    void *context,  
    int interval)  

不同的傳輸模式下,驅動為之申請不同的URB。其中,Linux核心只支援同步傳輸外的三種傳輸事件,ISO事務需要手工進行初始化工作。控制傳輸事務、批量傳輸事務、中斷傳輸事務API如上所示。
三種事務傳輸模式下的URB初始化函式有很多相似之處,主要引數含義如下:
• urb: 事務傳輸中的urb
• dev: 事務傳輸的目的裝置
• pipe: USB主機與USB裝置之間資料傳輸的通道
• transfer_buffer: 傳送資料所申請的記憶體緩衝區首地址
• length: 傳送資料緩衝區的長度
• context: complete函式的上下文
• complete_fn: 呼叫完成函式
• usb_fill_control_urb()的setup_packet: 即將被髮送的裝置資料包
• usb_fill_int_urb()的interval: 中斷傳輸中兩個URB排程的時間間隔

4.2.3 提交URB

URB初始化完成之後,USBD開始通過usb_start_wait_urb()提交urb請求(它呼叫usb_submit_urb來真正的傳送URB請求),新增completition函式。接下來,從message.c傳到主機控制器(hcd.c),開始真正的usb_hcd_submit_urb()。此時,根據是否為根集線器,進入不同的工作佇列。

   usb_start_wait_urb->
   usb_submit_urb->
   usb_hcd_submit_urb

a) root_hub傳輸

若為root hub,將呼叫rh_urb_enqueue(),共有兩種傳輸事務(控制傳輸和中斷傳輸

static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)  
{  
    if (usb_endpoint_xfer_int(&urb->ep->desc)) // 中斷傳輸  
        return rh_queue_status (hcd, urb);  
    if (usb_endpoint_xfer_control(&urb->ep->desc)) // 控制傳輸  
        return rh_call_control (hcd, urb);  
    return -EINVAL;  
}  

b) 非root_hub傳輸
對於非常root_hub傳輸,它呼叫:

   status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);

c) 批量傳輸
root_hub本身沒有批量傳輸流程,按照控制傳輸流程,控制傳輸最終要通過switch語句跳轉到Bulk-Only傳輸流程中。