USB協議架構分析
一、USB的一些基本概念
-
管道(Pipe) 是主機和裝置端點之間資料傳輸的模型,共有兩種型別的管道:無格式的
流管道(Stream Pipe)和有格式的資訊管道(Message Pipe)。任何USB 裝置一旦上電就存在
一個資訊管道,即預設的控制管道,USB 主機通過該管道來獲取裝置的描述、配置、狀態,並
對裝置進行配置。 -
端點(Endpoint) 是USB 裝置中的可以進行資料收發的最小單元,支援單向或者雙向的資料傳
輸。裝置支援端點的數量是有限制的,除預設端點外低速裝置最多支援2 組端點(2 個輸入,2
個輸出),高速和全速裝置最多支援15 組端點。 -
介面(Interface) 應用軟體通過和裝置之間的資料交換來完成裝置的控制和資料傳輸。通常
需要多個管道來完成資料交換,因為同一管道只支援一種型別的資料傳輸。用在一起來對裝置進
行控制的若干管道稱為裝置的介面. -
裝置和端點之間關係: 一個USB 裝置可以包括若干個端點,不同的端點以端點編號和方向區分。
不同端點可以支援不同的傳輸型別、訪問間隔以及最大資料包大小。除端點0 外,所有的端點只支
持一個方向的資料傳輸。端點0 是一個特殊的端點,它支援雙向的控制傳輸。管道和端點關聯,和
關聯的端點有相同的屬性,如支援的傳輸型別、最大包長度、傳輸方向等。 -
描述符(Descriptor)
描述符的大小(位元組數), 第二個位元組表示描述符的型別(Type). 描述符的種類有:
- 裝置描述符(Device), 描述一個裝置的一般資訊.
- 裝置修飾描述符(Device_Qualifier), 描述一個高速裝置在其它速度下該如何變化的資訊.
- 配置描述符(Configuration), 描述一個特定的裝置配置, 如介面的數目等. 一個USB裝置有一個或多個配置描述符. 每個配置有一個或多個介面並且每個介面有0個或多個端點.
- 其它速度配置描述符(Other_speed_configuration)
一個配置. - 介面描述符(Interface), 描述一種配置中的一個特定的介面.
- 端點描述符(Endpoint), 描述主機需要的去決定端點所需頻寬的資訊. 這個描述符只能附加在GetDescriptor()或GetDescriptor()請求中傳送, 不能單獨傳送. 端點0沒有此描述符.
- 字串描述符(String), 第0個字串描述符指定裝置支援的語言, 其它的描述符則各包含
一個UNICODE字串. 裝置描述符, 配置描述符 和 介面描述符可能會包含字串描述符.
- USB裝置請求(USB Device Request) 請求是從主機通過控制管道傳送到裝置.
標準的裝置請求有:
- Clear Feature
- Get Configuration
- Get Descriptor
- Get Interface
- Get Status
- Set Address
- Set Configuration
- Set Descriptor
- Set Feature
- Set Interface
- Synch Frame
二、USB3.0系統拓撲結構
系統是最多隻能有7層的樹狀結構
最多隻能支援127個裝置和Hub
Host+RootHub永遠是在第一層
複合(Compound)裝置一般佔兩層
功能裝置不能作為非葉節點, 只有Hub才可以
USB3.0 Hub其實包含了一個USB2的Hub和一個SuperSpeed Hub
三、 USB3.0的重要特性:
- 增加了一個重要的資料傳輸速率
- 點對點方式傳輸包, 使活動鏈路數目達到最少
- 非同步方式的通知功能, 去除了輪詢方式的必要
- 基於鏈路級的電源管理, 這是匯流排結構的基礎設計
- 向後相容USB2.0, 驅動級和物理層級別上都達到了相容的目的
四、USB3.0與USB2.0的區別
-
資料傳輸速率, 3.0是SuperSpeed 5.0Gbps,
2.0是 1.5Mbps, 12Mbps或480Mbps -
資料介面, 3.0是全雙工,獨立於USB 2.0訊號的四路差分訊號,支援同時雙向資料傳輸
2.0是半雙工,雙路差分訊號, 單向資料傳輸, 需要事先協商好總路線的傳輸方向 -
訊號線數目, 3.0是4路SuperSpeed資料線, 2路HighSpeed資料線和2路電源及地線
2.0是2路LS/FS/HS資料線, 2路電源及地線 -
匯流排事務協議, 3.0是主機主導的非同步方式的傳輸流量控制, 包傳輸是能顯式地進行路由
2.0是主機主導的輪詢方式的傳輸流量控制, 包傳輸是通過廣播方式到所有裝置 -
電源管理, 3.0是多級別的鏈路電源管理, 支援Idle, sleep和suspend狀態
2.0是在埠級進行管理, 可以在entry/exit上有兩種級別的掛機狀態 -
匯流排電源, 3.0是和USB 2.0差不多, 只是未配置的電源有50%的增幅, 已配置的電源有80%的增幅
-
主機控制器,3.0用的是xHCI,2.0則是EHCI。xHCI中提供了虛擬化技術支援。
五、USB3.0電源狀態
- U0, Link active
- U1, Link idle - fast exit. 退出延時us級
- U2, Link idle - slow exit. 退出延時us-ms級
- U3, Link suspend. 無時鐘訊號, 退出延時us-ms級
六、USB3.0資料包型別
- Link Management Packets, 鏈路管理包, 僅發生在兩個相連的埠之間,主要是用來進行鏈路管理
- Transaction Packets, 事務包, 發生在裝置和主機之間, 用來控制資料包的流量, 配置裝置和Hubs. 它沒有資料
- Data Packets, 資料包, 發生在裝置和主機之間. 它包括兩部分:包頭和實際資料. 其中資料部分還包括一個32位的CRC校驗碼來保證資料的完整性.
- 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主機驅動主要包含:
-
USB核驅動
-
主機控制器驅動
-
集線器驅動
驅動的載入執行流程如下圖所示:
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傳輸流程中。