1. 枚舉是什麼?
一旦獲悉有新設備連接上來,主機就會發送一系列的請求(Resqusts)給設備所掛載到的hub,再由hub建立起一條連接主機(Host)和設備(Device)之間的通信通道。然後主機以控制傳輸(Control Transfer)的方式,通過端點0(Endpoint 0)對設備發送各種請求,設備收到主機發來的請求後回覆相應的信息,進行枚舉(Enumerate)操作。所有的USB設備必須支持標準請求(StandardRequests),控制傳輸方式(Control Transfer)和端點0(Endpoint 0)。
在講解枚舉之前,先大概說說USB的一種傳輸模式——控制傳輸。這種傳輸在USB中是非常重要的,它要保證數據的正確性,在設備的枚舉過程中都是使用控制傳輸的。控制傳輸分爲三個階段:①建立階段。②數據階段。③確認階段。
建立(setup)階段:都是由USB主機發起,它是一個setup數據包,裏面包含一些數據請求的命令以及一些數據。如果建立階段是輸入請求,那麼數據階段就要輸入數據;如果建立階段是輸出請求,那麼數據階段就要輸出數據。如果在數據階段,即便不需要傳送數據,也要發一個0長度的數據包。數據階段過後就是確認階段。確認階段剛好跟數據階段相反,如果是輸入請求,則它是一個輸出數據包;如果是輸出請求,則它是一個輸入數據包。確認階段用來確認數據的正確傳輸。
1.1 通信傳輸流
1.2 設備狀態圖
1.3 狀態詳解
1) 連接(Attached)
設備可以連接到USB或者從USB上拔出.USB設備從總線上撥出後的狀態在規範沒定義,只說明一旦USB連到總線要求的操作以及屬性.
2) 上電(Powered)
USB設備的電源可來自外部電源,也可從USB接口的集線器而來。電源來自外部電源的USB設備被稱作自給電源式的(self-powered)。儘管自給電源式的USB設備可能在連接上USB接口以前可能已經帶電,但它們直到連線上USB接口後才能被看作是加電狀態(Powered state)。而這時候VBUS已經對設備產生作用了.
一個設備可能有既支持自給電源的,同時也支持總線電源式的配置。有一些支持其中的一種,而另一些設備配置可能只有在自給電源下才能被使用。設備對電源支持的能力是通過配置描述表(configuration descriptor)來反映的。當前的電源供給形式被作爲設備狀態的一部分被反映出來。設備可在任何時候改變它們的供電來源,比如說:從自給式向總線式改變,如果一個配置同時支持兩種模式,那此狀態的最大電源需求就是指設備在兩種模式下從VBUS上獲取電能的最大值。設備必須以此最大電源作爲參照,而究竟處於何狀態是不考慮的。如果有一配置僅支持一種電源模式,那麼電源模式的改變會使得設備失去當前配置與地址,返回加電狀態。如果一個設備是自給電源式,並且當前配置需要大於100mA電流,那麼如果此設備轉到了總線電源式,它必須返回地址狀態(Address
state)。自給電源式集線器使用VBUS來爲集線控制器(Hub controller)提供電源,因而可以仍然保持配置狀態(Configured state),儘管自給電源停止提供電源。
3)默認狀態(Default)
設備上電後,它不響應任何總線處理,直到總線接收到復位信號爲止.接收到復位信號後,用默認的地址可以對設備尋址.
當用復位過程完成後,USB設備在正確的速度下操作(即低速/全速/高速).低速和全速的數據選擇由設備的終端電阻決定.能進行高速操作的設備決定它是否在復位的過程的一部分執行高速操作.
能進行高速操作的設備在全速的電氣環境中操作時,必須能以全速成功復位.設備成功復位後,設備必須成功響應設備和配置描述符請求,並且返回適當的信息.當在全速下工作時,設備可能或者不能支持預定義的功能.
4) 地址(Address)
所有的USB設備在加電覆位以後都使用缺省地址。每一設備在連接或復位後由主機分配一個唯一的地址。當USB設備處於掛起狀態時,它保持這個地址不變。
USB設備只對缺省通道(Pipe)請求發生響應,而不管設備是否已經被分配地址或在使用缺省地址。
5) 配置狀態( Configured )
在USB設備正常工作以前,設備必須被正確配置。從設備的角度來看,配置包括一個將非零值寫入設備配置寄存器的操作。配置一個設備或改變一個可變的設備設置會使得與這個相關接口的終端結點的所有的狀態與配置值被設成缺省值。這包括將正在使用(date toggle)的結點(end point)的 (Date toggle)被設置成DATA0。
6) 掛起狀態
爲節省電源,USB設備在探測不到總線傳輸時自動進入中止狀態。當中止時,USB設備保持本身的內部狀態,包括它的地址及配置。
所有的設備在一段特定的時間內探測不到總線活動時必須進入中止態。不管設備是被分配了非缺省的地址或者是被配置了,已經連接的設備必須在任何加電的時刻隨時準備中止。總線活動的中止可能是因爲主機本身進入了中止狀態。另外,USB設備必須在所連接的集線器端口失效時進入中止態。這就是所指的選擇性中止(Selective suspend)。
USB設備在總線活動來到時結束中止態。USB設備也可以遠程喚醒的電流信號來請求主機退出中止態或選擇性中止態。具體設備具有的遠程喚醒的能力是可選的,也就是說,如果一個設備有遠程喚醒的能力,此設備必須能讓主機控制此能力的有效與否。當設備復位時,遠程喚醒能力必須被禁止。
2. 枚舉步驟
USB協議定義了設備的6種狀態,僅在枚舉過程中,設備就經歷了4個狀態的遷移:上電狀態(Powered),默認狀態(Default),地址狀態(Address)和配置狀態(Configured)(其他兩種是連接狀態和掛起狀態(Suspend))。
2.1 用戶把USB設備插入USB端口或給系統啓動時設備上電
這裏指的USB端口指的是主機下的根hub或主機下行端口上的hub端口。Hub給端口供電,連接着的設備處於上電狀態。此時,USB設備處於加電狀態,它所連接的端口是無效的。
2.2 Hub監測它各個端口數據線上(D+/D-)的電壓
在hub端,數據線D+和D-都有一個阻值在14.25k到24.8k的下拉電阻Rpd,而在設備端,D+(全速,高速)和D-(低速)上有一個1.5k的上拉電阻Rpu。當設備插入到hub端口時,有上拉電阻的一根數據線被拉高到幅值的90%的電壓(大致是3V)。hub檢測到它的一根數據線是高電平,就認爲是有設備插入,並能根據是D+還是D-被拉高來判斷到底是什麼設備(全速/低速)插入端口(全速、高速設備的區分在我將來的文章中描述)。如下圖。
USB全速/高速設備上電連接
檢測到設備後,hub繼續給設備供電,但並不急於與設備進行USB傳輸。
USB接口定義如下圖所示:
2.3 Host瞭解連接的設備
每個hub利用它自己的中斷端點向主機報告它的各個端口的狀態(對於這個過程,設備是看不到的,也不必關心),報告的內容只是hub端口的設備連接/斷開的事件。如果有連接/斷開事件發生,那麼host會發送一個 Get_Port_Status請求(request)給hub以瞭解此次狀態改變的確切含義。Get_Port_Status等請求屬於所有hub都要求支持的hub類標準請求(standard hub-class requests)。
2.4 Hub檢測所插入的設備是高速還是低速設備
hub通過檢測USB總線空閒(Idle)時差分線的高低電壓來判斷所連接設備的速度類型,當host發來Get_Port_Status請求時,hub就可以將此設備的速度類型信息回覆給host。USB 2.0規範要求速度檢測要先於復位(Reset)操作。
2.5 hub復位設備
主機一旦得知新設備已連上以後,它至少等待100ms以使得插入操作的完成以及設備電源穩定工作。然後主機控制器就向hub發出一個 Set_Port_Feature請求讓hub復位其管理的端口(剛纔設備插上的端口)。hub通過驅動數據線到復位狀態(D+和D-全爲低電平 ),並持續至少10ms。當然,hub不會把這樣的復位信號發送給其他已有設備連接的端口,所以其他連在該hub上的設備自然看不到復位信號,不受影響。
2.6 Host檢測所連接的全速設備是否是支持高速模式
因爲根據USB 2.0協議,高速(High Speed)設備在初始時是默認全速(Full Speed )狀態運行,所以對於一個支持USB 2.0的高速hub,當它發現它的端口連接的是一個全速設備時,會進行高速檢測,看看目前這個設備是否還支持高速傳輸,如果是,那就切到高速信號模式,否則就一直在全速狀態下工作。
同樣的,從設備的角度來看,如果是一個高速設備,在剛連接bub或上電時只能用全速信號模式運行(根據USB 2.0協議,高速設備必須向下兼容USB 1.1的全速模式)。隨後hub會進行高速檢測,之後這個設備纔會切換到高速模式下工作。假如所連接的hub不支持USB 2.0,即不是高速hub,不能進行高速檢測,設備將一直以全速工作。
2.7 Hub建立設備和主機之間的信息通道
主機不停地向hub發送Get_Port_Status請求,以查詢設備是否復位成功。Hub返回的報告信息中有專門的一位用來標誌設備的復位狀態。
當hub撤銷了復位信號,設備就處於默認/空閒狀態(Default state),準備接收主機發來的請求。設備和主機之間的通信通過控制傳輸,默認地址0,端點號0進行。此時,設備能從總線上得到的最大電流是100mA。(所有的USB設備在總線復位後其地址都爲0,這樣主機就可以跟那些剛剛插入的設備通過地址0通信。)
2.8 主機發送Get_Descriptor請求獲取默認管道的最大包長度
默認管道(Default Pipe)在設備一端來看就是端點0。主機此時發送的請求是默認地址0,端點0,雖然所有未分配地址的設備都是通過地址0來獲取主機發來的請求,但由於枚舉過程不是多個設備並行處理,而是一次枚舉一個設備的方式進行,所以不會發生多個設備同時響應主機發來的請求。
設備描述符的第8字節代表設備端點0的最大包大小。雖然說設備所返回的設備描述符(Device Descriptor)長度只有18字節,但系統也不在乎,此時,描述符的長度信息對它來說是最重要的,其他的瞄一眼就過了。當完成第一次的控制傳輸後,也就是完成控制傳輸的狀態階段,系統會要求hub對設備進行再一次的復位操作(USB規範裏面可沒這要求)。再次復位的目的是使設備進入一個確定的狀態。
2.9 主機給設備分配一個地址
主機控制器通過Set_Address請求向設備分配一個唯一的地址。在完成這次傳輸之後,設備進入地址狀態(Address state),之後就啓用新地址繼續與主機通信。這個地址對於設備來說是終生制的,設備在,地址在;設備消失(被拔出,復位,系統重啓),地址被收回。同一個設備當再次被枚舉後得到的地址不一定是上次那個了。
2.10 主機獲取設備的信息
主機發送 Get_Descriptor請求到新地址讀取設備描述符,這次主機發送Get_Descriptor請求可算是誠心,它會認真解析設備描述符的內容。設備描述符內信息包括端點0的最大包長度,設備所支持的配置(Configuration)個數,設備類型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由廠商自己定製)等信息。Get_Descriptor請求(Device
type)和設備描述符(已抹去VID,PID等信息)見下圖:
標準Get_Descriptor請求
設備描述符(Device Descriptor)
之後主機發送Get_Descriptor請求,讀取配置描述符(Configuration Descriptor),字符串等,逐一瞭解設備更詳細的信息。事實上,對於配置描述符的標準請求中,有時wLength一項會大於實際配置描述符的長度(9字節),比如255。這樣的效果便是:主機發送了一個Get_Descriptor_Configuration 的請求,設備會把接口描述符,端點描述符等後續描述符一併回給主機,主機則根據描述符頭部的標誌判斷送上來的具體是何種描述符。
接下來,主機就會獲取配置描述符。配置描述符總共爲9字節。主機在獲取到配置描述符後,根據裏面的配置集合總長度,再獲取配置集合。配置集合包括配置描述符,接口描述符,端點描符等等。
如果有字符串描述符的話,還要獲取字符串描述符。另外HID設備還有HID描述符等。
2.11 主機給設備掛載驅動(複合設備除外)
主機通過解析描述符後對設備有了足夠的瞭解,會選擇一個最合適的驅動給設備。 然後tell the world(announce_device)說明設備已經找到了,最後調用設備模型提供的接口device_add將設備添加到 usb 總線的設備列表裏,然後 usb總線會遍歷驅動列表裏的每個驅動,調用自己的 match(usb_device_match) 函數看它們和你的設備或接口是否匹配,匹配的話調用device_bind_driver函數,現在就將控制權交到設備驅動了。
對於複合設備,通常應該是不同的接口(Interface)配置給不同的驅動,因此,需要等到當設備被配置並把接口使能後纔可以把驅動掛載上去。
設備-配置-接口-端點關係見下圖:
USB 設備-配置-接口-端點關係
實際情況沒有上述關係複雜。一般來說,一個設備就一個配置,一個接口,如果設備是多功能符合設備,則有多個接口。端點一般都有好幾個,比如Mass Storage設備一般就有兩個端點(控制端點0除外)。
2.12 設備驅動選擇一個配置
驅動(注意,這裏是驅動,之後的事情都是有驅動來接管負責與設備的通信)根據前面設備回覆的信息,發送Set_Configuration請求來正式確定選擇設備的哪個配置(Configuration)作爲工作配置(對於大多數設備來說,一般只有一個配置被定義)。至此,設備處於配置狀態(Configured),當然,設備也應該使能它的各個接口(Interface)。
對於複合設備,主機會在這個時候根據設備接口信息,給它們掛載驅動。
3. 控制傳輸
1) bmRequestType(向誰請求)
0=主機至設備;1=設備至主機
D6..5:命令類型
D4..0:接受者類型
2=端點;3=其它
4..31 保留
USB說明定義了一系列所有設備必須支持的標準請求。這些請求見下面的表<Standard Device Requests>。另外,一個設備類可定義更多的請求。設備廠商也可定義設備支持的請求.
請求可被導引到設備,設備接口,或某一個設備端結點(endpoint)上。這個請求域也指定了接收者。當指定的是接口或端結點(endpoint)時,wIndex域指出那個接口或端節點。
此域用來傳送當前請求的參數,隨請求不同而變。
4) wIndex域
當bmRequestType的Recipient字段爲接口或端點時,wIndex域用來表明是哪一個接口或端結。
5) wLength域
這個域表明第二階段的數據傳輸長度。傳輸方向由bmRequstType域的Direction位指出。wLength域爲0則表明無數據傳輸。在輸入請求下,設備返回的數據長度不應多於wLength,但可以少於。在輸出請求下,wLength指出主機發出的確切數據量。如果主機發送多於wLength的數據,設備做出的響應是無定義的。
3.1 標準設備請求
3.2 標準請求碼(bRequest的值)
3.3 描述符類型
3.4 GET_DESCRIPTOR(讀取描述符)
wIndex域:
所有的設備必須提供一個設備描述符並且至少一個配置描述符,如果一個設備不支持一個請求的描述符,則返回請求錯誤。
•缺省狀態:此請求合法。
•地址狀態:此請求合法。
•配置狀態:此請求合法。
3.5 GET_INTERFACE(取得接口)
一些USB設備有接口設置互斥的配置。這個請求允許主機確定當前選定的備用設置。如果wValue或者wLength的值與上面指定的不一致,那麼設備的行爲沒有定義;如果指定的接口不存在,那麼設備將用請求錯誤響應。
•默認狀態:當設備處於默認狀態時接收到這個請求,設備的行爲沒 有定義
•地址狀態:設備給出請求錯誤
•配置狀態:當設備處於配置狀態時,這是一個有效的請求
1. USB協議
1.1 USB主機系統
在USB主機系統中,通過根集線器與外部USB從機設備相連的處理芯片,稱爲USB主機控制器。USB主機控制器包含硬件、軟件和固件一部分。
1.2 USB設備系統
USB設備按功能分爲兩部分:集線器(Hub)和功能部件。從下圖可知,主機通過根集線器連接到各種外圍設備(集線器和功能部件)。
1.3 主機和設備之間通信模型
主機與設備之間的通信模型
上圖展示了USB主機和USB設備之間的數據傳輸過程。在設備端,USB設備將非USB格式的數據進行打包處理,轉換成USB格式的數據包,然後傳遞到鏈路層,經過硬件處理、傳遞到物理層,由物理層通過PHY以數據流的形式傳輸到主機。
USB主機在USB設備和USB主機之間發起的傳輸過程,穩爲事務。每次事務以2到3個數據包的形式進行USB總線傳輸。每個數據包包含2到3個步驟:
1) USB主機控制器向USB設備發出命令
2) USB控制器和USB設備之間傳遞讀寫請求,其方向取決於第一部分的命令是讀還是寫
3) 握手信號。
USB主機控制器向USB設備發送事務類型請求,通過分組標識符來進行識別。
1.4 USB分組標識
主機和設備之間進行操作,通過分組標識(PID)來進行傳輸。數據包傳輸格式一般由:PID、數據/控制信息、CRC校驗碼組成。
常見的PID主要包括令牌、數據、握手等類型組成。PID碼以特定的方式組成,如下表所示:
PID分組碼是數據傳輸流程中的重要元素。無論硬件還是軟件,都要對PID分組碼進行分析,從而做出正確響應。USB主機和設備嚴格按照PID分組碼信息進行信息交互。
1.5 數據包傳輸模式
當USB設備連接到集線器,集線器狀態將發生相應的變化,並將狀態變化信息傳遞給USB主機。USB主機通過根集線器向USB設備發送命令,獲取USB設備的各種信息,包含USB設備傳輸類型、ID號、Product、USB速度等信息。
USB主機和USB設備之間的數據傳輸共有四種類型:控制傳輸、批量傳輸、中斷傳輸和同頻傳輸。與之對應,USB主機和USB設備之間有四種事務:控制事務、批量事務、中斷事務和同步事務。
1.5.1 批量(Bulk)傳輸
作用:主要用於非實時性傳輸,數據包較大而延時要求較低。
特點:數據傳輸準備即可,採用批量傳輸模式的USB從機設備,如U盤
數據傳輸分三個階段:
a) 令牌階段:主機發送請求,USB設備依據請求PID來判斷IN或OUT傳輸
b) 數據傳輸階段:依據令牌階段的IN或OUT傳輸,來決定數據傳輸爲DATA0或DATA1來進行數據傳輸
c) 握手階段:接收信息的一方發送ACK信號以表示接收成功;若爲NAK,表示發送失敗;STALL表示不可預知的錯誤
1.5.2 控制(Control)傳輸
作用:USB傳輸過程必須支持的傳輸模式。USB主機爲了獲取設備描述符、ID、Product等信息,向USB設備發送相應的PID命令。
特點:唯一可以進行IN/OUT傳輸的傳輸模式。
數據寬度:控制傳輸方式可以以8、16、32或64字節的數據進行傳輸,這取決於設備的傳輸速度。
USB主機和設備之間必須支持控制傳輸,通過端點0進行數據傳輸。控制傳輸分爲令牌、數據傳輸和握手階段。
1.5.3 中斷傳輸事務
作用:按照一定時刻輪詢設備是否有中斷傳輸請求
特點:查詢頻率取決於端點的模式結構,從1到255ms不等
中斷傳輸主要用於實時性要求非常高的從機設備,如鍵盤操縱桿和Mouse等
傳輸過程也分爲令牌階段、數據傳輸和握手階段
1.6 USB描述符
USB協議中共定義了以下四種描述符:
1) 設備描述符
2) 配置描述符
3) 接口描述符
4) 端點描述符
其關係如下圖所示:
1.6.1 設備描述符
每個USB設備都有一個唯一的設備描述符,如下表所示:
1.6.2 配置描述符
每個USB設備都有默認的配置描述符,支持至少一個接口,每個配置描述符如下表:
1.6.3 接口描述符
設備應至少支持一個接口,如:塊傳輸數據接口,部分設備可能支持其它的接口。複合設備可以支持額外接口,以支持音頻和視頻功能。標準中並沒有定義此類接口。接口可能有多個可選設置,主機將會檢查每個可選的設置。
1.6.4 端點描述符
每個設備至少支持控制端點0。USB設備應該支持三類端點:控制端點、輸入端點和輸出端點。
2. 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設備提供電源。
2.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設備開始總線復位並具有總線控制權,主機交換協議完成。
2.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
- 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傳輸流程中。