USB枚舉流程分析
對于每個usb系統來說,都有一個稱為HOST控制器的設備,該Host控制器和一個根Hub作為一個整體。這個根HUb下可以接多級Hub,每個Hub又可以接子Hub。每個usb設備作為一個節點接在不同級別的Hub上。每條usb總線上最多可以接127個設備。

usb主控制器:
負責處理主機與設備之間的電氣和協議層的互聯。常見的usb主控制器規格有:
OHCI:只要是非pc系統上的usb芯片
UHCI:usb1.1規格
EHCI: 兼容上面種規格,遵循usb2.0規范
每個usb host控制器都會自帶一個usb hub ,被稱為根Hub。這個根hub可以接子hub,每個hub上掛載usb設備。通過外接usb hub,可以插更多的usb設備。當usb設備插入到usb Hub或從上面拔出時,都會發出電信號通知系統。
usb設備就是插在usb總線上工作的設備,廣義的講usb Hub也算是usb設備。有的usb設備功能單一,直接掛載在usb hub上。而有的usb設備功能復雜,會將多個usb功能結合在一起,稱為一個復合設備。
usb設備邏輯結構

在usb設備的邏輯組織中,包含設備,配置,接口,端點4個層次。
設備通常有一個或多個配置,配置通常有一個或多個接口,接口有零或多個端點。
每個usb設備都可以包含一個或多個配置,不同的配置使設備表現出不同的功能組合(在探測,連接期間需從其中選定一個),配置由多個接口組成。在usb協議中,接口由多個端點組成,代表一個基本的功能,是usb設備驅動程序控制的對象,一個功能復雜的usb設備可以具有多個接口,而接口是端點的匯集
例子
一個usb播放器帶有音頻,視頻功能,還有旋鈕和按鈕
配置1 音頻(接口)+旋鈕(接口)
配置2 視頻(接口)+音頻(接口)+按鈕(接口)
每個接口對應需要一個驅動程序
usb設備中的唯一可尋址部分是設備的端點。它是位于usb設備或主機上的一個數據緩沖區,用來存放和發送usb的各種數據。主機和設備的通信最終作用于設備上的各個端點,它是主機與設備間通信流的一個邏輯終端
每個usb設備有一個唯一的地址,這個地址是在設備連上主機時,由主機分配的,而設備中的每個端點在設備內部有唯一的端點號,這個端點號是在設計設備時給定的。每個端點都是一個簡單的連接點,或者支持數據流進設備,或者支持其流出設備,兩者不可兼得。
基于PnP機制,設備被枚舉時,它必須向主機報告各個端點的特性,包括端點號,通信方向,端點支持的最大包大小,帶寬要求等(其中端點支持的最大包大小叫做數據有效負載)。每個設備必須有端點0,它用于設備枚舉和對設備進行一些基本的控制功能。除了端點0,其余的端點在設備配置之前不能與主機通信,只有向主機報告這些端點的特性并被確認后才能被激活。
LInux USB枚舉過程
USB架構中, hub負責檢測設備的連接和斷開,利用其中斷IN端點(Interrupt IN Endpoint)來向主機(Host)報告。在系統啟動時,主機輪詢它的根hub(Root Hub)的狀態看是否有設備(包括子hub和子hub上的設備)連接。USB總線拓撲結構見上圖(最頂端為主機的Root Hub)
一旦獲悉有新設備連接上來,主機就會發送一系列的請求(Resqusts)給設備所掛載到的hub,再由hub建立起一條連接主機(Host)和設 備(Device)之間的通信通道。然后主機以控制傳輸(ControlTransfer)的方式,通過端點0(Endpoint 0)對設備發送各種請求,設備收到主機發來的請求后回復相應的信息,進行枚舉(Enumerate)操作。所有的USB設備必須支持標準請求 (StandardRequests),控制傳輸方式(ControlTransfer)和端點0(Endpoint 0)。
從用戶角度來看,枚舉過程是自動完成并不可見的。但很多初次使用的設備連接時,系統會彈出說新硬件檢測到,設備安裝成功,可以使用之類的消息提示框,而且有時還需要用戶配合選擇安裝相關的驅動。
當枚舉完成后,這個新添加的設備可在Windows的設備管理器里面看到,當用戶刪除這個設備/硬件時,系統把這個設備從設備管理器里刪除。
對于一般的設備,固件(Firmware)內包含主機所要請求的信息,而有些設備則是完全由硬件來負責響應主機的請求。在主機方面則是由操作系統而非應用程序負責處理相關枚舉操作。
枚舉步驟
USB協議定義了設備的6種狀態,僅在枚舉過程種,設備就經歷了4個狀態的遷移:上電狀態(Powered),默認狀態(Default),地址狀態(Address)和配置狀態(Configured)(其他兩種是連接狀態和掛起狀態(Suspend))。
下面步驟是Windows系統下典型的枚舉過程,但是固件不能依此就認為所有的枚舉操作都是按照這樣一個流程行進。設備必須在任何時候都能正確處理所有的主機請求。
1、用戶把USB設備插入USB端口或給系統啟動時設備上電
這里指的USB端口指的是主機下的根hub或主機下行端口上的hub端口。Hub給端口供電,連接著的設備處于上電狀態。
2、Hub監測它各個端口數據線上(D+/D-)的電壓
在hub端,數據線D+和D-都有一個阻值在14.25k到24.8k的下拉電阻Rpd,而在設備端,D+(全速,高速)和D-(低速)上有一個1.5k 的上拉電阻Rpu。當設備插入到hub端口時,有上拉電阻的一根數據線被拉高到幅值的90%的電壓(大致是3V)。hub檢測到它的一根數據線是高電平, 就認為是有設備插入,并能根據是D+還是D-被拉高來判斷到底是什么設備(全速/低速)插入端口。 檢測到設備后,hub繼續給設備供電,但并不急于與設備進行USB傳輸。
根據規范,全速(Full Speed)和低速(Low Speed)很好區分,因為在設備端有一個1.5k的上拉電阻,當設備插入hub或上電(固定線纜的USB設備)時,有上拉電阻的那根數據線就會被拉高,hub根據D+/D-上的電平判斷所掛載的是全速設備還是低速設備。如下兩圖:


USB全速/低速識別相當簡單,但USB2.0,USB1.x就一對數據線,不能像全速/低速那樣僅依靠數據線上拉電阻位置就能識別USB第三種速度:高速。因此對于高速設備的識別就顯得稍微復雜些。
高速設備初始是以一個全速設備的身份出現的,即和全速設備一樣,D+線上有一個1.5k的上拉電阻。USB2.0的hub把它當作一個全速設備,之后,hub和設備通過一系列握手信號確認雙方的身份。在這里對速度的檢測是雙向的,比如高速的hub需要檢測所掛上來的設備是高速、全速還是低速,高速的設備需要檢測所連上的hub是USB2.0的還是1.x的,如果是前者,就進行一系列動作切到高速模式工作,如果是后者,就以全速模式工作。
下圖展示了一個高速設備連到USB2.0 hub上的情形:

3、Host了解連接的設備
每個hub利用它自己的中斷端點向主機報告它的各個端口的狀態(對于這個過程,設備是看不到的,也不必關心),報告的內容只是hub端口的設備連接/斷開 的事件。如果有連接/斷開事件發生,那么host會發送一個 Get_Port_Status請求(request)以了解更多hub上的信息。Get_Port_Status等請求屬于所有hub都要求支持的 hub類標準請求(standard hub-classrequests)。
4、Hub檢測所插入的設備是高速還是低速設備
hub通過檢測USB總線空閑(Idle)時差分線的高低電壓來判斷所連接設備的速度類型,當host發來Get_Port_Status請求時,hub 就可以將此設備的速度類型信息回復給host。(USB 2.0規范要求速度檢測要先于復位(Reset)操作)。
5、hub復位設備
當主機獲悉一個新的設備后,主機控制器就向hub發出一個 Set_Port_Feature請求讓hub復位其管理的端口。hub通過驅動數據線到復位狀態(D+和D-全為低電平 ),并持續至少10ms。當然,hub不會把這樣的復位信號發送給其他已有設備連接的端口,所以其他連在該hub上的設備自然看不到復位信號,不受影響。
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,不能進行高速檢測,設備將一直以全速工作。
7、Hub建立設備和主機之間的信息通道
主機不停得向hub發送 Get_Port_Status請求,以查詢設備是否復位成功。Hub返回的報告信息中有專門的一位用來標志設備的復位狀態。
當hub撤銷了復位信號,設備就處于默認/空閑狀態(Default state),準備著主機發來的請求。設備和主機之間的通信通過控制傳輸,默認地址0,端點號0進行。在此時,設備能從總線上得到的最大電流是100mA。(所有的USB設備在總線復位后其地址都為0,這樣主機就可以跟那些剛剛插入的設備通過地址0通信。)
8、主機發送Get_Descriptor請求獲取默認管道的最大包長度
默認管道(Default Pipe)在設備一端來看就是端點0。主機此時發送的請求是默認地址0,端點0,雖然所有位分配地址的設備都是通過地址0來獲取主機發來的信息,但由于枚 舉過程不是多個設備并行處理,而是一次枚舉一個設備的方式進行,所以不會發生多個設備同時響應主機發來的請求。
設備描述符的第8字節代表設備端點0的最大包大小。對于Windows系統來說,Get_Descriptor請求中的wLength一項都會設為64, 雖然說設備所返回的設備描述符(DeviceDescriptor)長度只有18字節,但系統也不在乎,此時,描述符的長度信息對它來說是最重要的,其他 的瞄一眼就過了。Windows系統還有個怪癖,當完成第一次的控制傳輸后,也就是完成控制傳輸的狀態階段,系統會要求hub對設備進行再一次的復位操作 (USB規范里面可沒這要求)。再次復位的目的是使設備進入一個確定的狀態。
9、主機給設備分配一個地址
主機控制器通過Set_Address請求向設備分配一個唯一的地址。在完成這次傳輸之后,設備進入地址狀態(Address state),之后就啟用新地址繼續與主機通信。這個地址對于設備來說是終生制的,設備在,地址在;設備消失(被拔出,復位,系統重啟),地址被收回。同 一個設備當再次被枚舉后得到的地址不一定是上次那個了。
10、主機獲取設備的信息
主機發送 Get_Descriptor請求到新地址讀取設備描述符,這次主機發送Get_Descriptor請求可算是誠心,它會認真解析設備描述符的內容。設 備描述符內信息包括端點0的最大包長度,設備所支持的配置(Configuration)個數,設備類型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由廠商自己定制)等信息。Get_Descriptor請求(Device type)和設備描述符(已抹去VID,PID等信息)見下圖:


之后主機發送Get_Descriptor請求,讀取配置描述符(ConfigurationDescriptor),字符串等,逐一了解設備更詳細的信 息。事實上,對于配置描述符的標準請求中,有時wLength一項會大于實際配置描述符的長度(9字節),比如255。這樣的效果便是:主機發送了一個 Get_Descriptor_Configuration的請求,設備會把接口描述符,端點描述符等后續描述符一并回給主機,主機則根據描述符頭部的標 志判斷送上來的具體是何種描述符。
接下來,主機就會獲取配置描述符。配置描述符總共為9字節。主機在獲取到配置描述符后,根據里面的配置集合總長度,再獲取配置集合。配置集合包括配置描述符,接口描述符,端點描符等等。
如果有字符串描述符的話,還要獲取字符串描述符。另外HID設備還有HID描述符等。
11、主機給設備掛載驅動(復合設備除外)
主機通過解析描述符后對設備有了足夠的了解,會選擇一個最合適的驅動給設備。在驅動的選擇過程中,Windows系統會和系統inf文件里的廠商 ID,產品ID,有時甚至用到設備返回來的產品版本號進行匹配。如果沒有匹配的選項,Windows會根據設備返回來的類,子類,協議值信息選擇。如果該 設備以前在系統上成功枚舉過,操作系統會根據以前記錄的登記信息而非inf文件掛載驅動。當操作系統給設備指定了驅動之后,就由驅動來負責對設備的訪問。
對于復合設備,通常應該是不同的接口(Interface)配置給不同的驅動,因此,需要等到當設備被配置并把接口使能后才可以把驅動掛載上去。
設備-配置-接口-端點關系見下圖:

實際情況沒有上述關系復雜。一般來說,一個設備就一個配置,一個接口,如果設備是多功能符合設備,則有多個接口。端點一般都有好幾個,比如Mass Storage設備一般就有兩個端點(控制端點0除外)。
12. 設備驅動選擇一個配置
驅動(注意,這里是驅動,之后的事情都是有驅動來接管負責與設備的通信)根據前面設備回復的信息,發送Set_Configuration請求來正式確定 選擇設備的哪個配置(Configuration)作為工作配置(對于大多數設備來說,一般只有一個配置被定義)。至此,設備處于配置狀態,當然,設備也 應該使能它的各個接口(Interface)。
對于復合設備,主機會在這個時候根據設備接口信息,給它們掛載驅動。
評論