Tuesday, July 22, 2014

USB 簡介[6]

終於到了最後也是最重要的一章,鼎鼎有名的Chapter 9,USB Device Framework。

為什麼這一章這麼有名?因為前面的鋪陳完全就是在這一章爆發啊。重點是,USB標準測試程式,USBCV(USB Command Verifier),跟微軟的WHCK(Windows Hardware Certification Kit),就是一直在找Chapter 9的碴啊。

第一點,USB裝置可分為好幾種狀態,而每個狀態可以吃的電流是有關連的,基本上只有Configured狀態才可以吃超過100mA的電流。而比較值得注意的是,任何狀態都可以進Suspend狀態,這是因為Suspend狀態是透過Suspend訊號來偵測,很簡單。但是哪個狀態進Suspend,出Suspend之後還得是那個狀態,這個就不簡單了。在Suspend狀態中,裝置最多只能吃500uA的電,要怎麼吃那少的電,還要能夠回到上個狀態,這個就不簡單了。

第二點,當有USB裝置插上去的時候,HOST怎麼知道這個裝置到底是什麼東西?那就是透過USB Device Descriptor啦。HOST只要發個"Get Descriptor"這個標準要求給這個裝置,裝置必須回正確的Device Descriptor,這個Device Descriptor內容很簡單,就是描述這個裝置支援的USB版本、實作的Class、Control Endpoint的最大資料量、Vendor ID跟Product ID等等。

先說明『標準要求』,USB有規範一些『標準要求』(Standard Request),目的是讓每個裝置能夠回覆足夠多的資訊給HOST,這樣HOST才能繼續下個動作。有幾個一定要知道:

  • Set Address:HOST透過它來設定USB裝置的位址。每個裝置插上去之後,預設的位址是0,這樣HOST才能透過位址0來讀取Device Descriptor,然後再把這個裝置的位址設到不同的位址去。
  • Get Descriptor:基本上HOST透過這個要求取得USB裝置的所有資訊。
  • Set Configuration:就是選擇USB裝置提供的設定,完成Set Configuration裝置才算進入Configured狀態。
其實最重要的就是Get Descriptor。提到這個要求得先提USB Descriptor。USB Descriptor有分幾種,下面只列常用的:
  • Device Descriptor
  • Configuration Descriptor 1
    • Interface Descriptor
      • Endpoint Descriptor
  • String Descriptor
請注意,他們有些是有階層概念的,Configuration Descriptor主要是描述這個裝置所提供的組態。大多數的裝置都只會提供一種組態,有的會提供兩個以上,主要是電力需求的不同。Configuration Descriptor最重要的資訊就是這個組態最大吃多少電流,以及這個組態支援幾個Interface。要選擇那個組態是HOST決定的,而對應的就是Set Configuration這個要求。

Interface Descriptor主要描述這個裝置有哪些功能(function),一個Interface代表一個功能,所以當你看到一個Configuration Descriptor裡面有包含兩個Interface時,基本上就是只這個裝置支援多種功能,也就是Composite Device。但是,還是有但是,USB後來又有出一種叫做IAD(Interface Association Descriptor)這種東西,它可以把兩個Interface組成一種功能,所以世事無絕對...。

Interface Descriptor主要描述它所支援的功能,以及它支援的Endpoint數目跟種類。它裡面還有一個叫做Alternate Setting的欄位,這個欄位主要是描述這個Interface支援哪幾種頻寬設定。還記得Interrupt跟Isochronous傳輸是有頻寬保證的嗎?這種保證是需要HOST來提供。那HOST要怎知道它能不能保證Device提出的頻寬需求?就是透過Interface Descriptor啦。Alternate Setting可以讓這個Interface提供多種Interrupt或Isochronous傳輸的組合。舉個例子,Interface Descriptor可以提供三種Alternate Setting:


  • 0:需要每個SOF能夠傳輸64 byte的Interrupt Endpoint
  • 1:需要每個SOF能夠傳輸512 byte的Interrupt Endpoint
  • 2:需要每個SOF能夠傳輸1024 byte的Interrupt Endpoint
然後HOST可以根據目前的剩餘頻寬,選擇適合的Alternate Setting。為什麼要搞這麼複雜?因為其他的裝置可能也需要HOST保證一些頻寬,所以得這樣搞。不過我很少遇到這種狀況就是了,尤其是如果都沒Interrupt或Isochronous Endpoint的話,其實只要提供Alternate Setting 0就好了,因為Bulk傳輸並不需要保證頻寬。

而HOST要怎麼選擇Alternate Setting?當然是透過Set Interface這個需求。如果一個Interface只提供一種Alternate Setting,沒有Set Interface其實也可以。

Endpoint則是說明這個Endpoint的類型以及最大傳輸量。

String Descriptor很煩,它主要是提供一些對於Device、Configuration、Interface的描述,也就是人可以看得懂的字串。它一定得提供Unicode字串,而且還有語系之分,但是因為實在太煩了,所以大家都只用英文語系....也造成Unicode字串根本沒有用...。一般就是提供Manufacturer、Product跟SerialNumber這三個字串而已。WHCK機車的地方就在於,它會要你插上兩個同樣Vendor ID跟Product ID的裝置,然後這兩個裝置所提供的SerialNumber還得不一樣。

總結一下,當USB裝置插上HOST時,HOST會SetAddress、Get Descriptor,抓完所有Descriptor之後,Set Configuration選擇適當的組態,然後再Set Interface選擇適當的頻寬需求,結束。接下來就可以透過支援的Endpoint傳輸資料了。說真的,這些東西都不難,只是規格絕對不會跟你講HOST應該會怎麼做,而這也是最機車的地方:每種HOST的實作方式都不同,所以流程絕對不同,光是插上去的流程,就足夠引起一堆相容性問題了。

解這種相容性問題最最重要的就是一台USB Analyzer....,它可以幫你省掉很多猜測的時間。

最後,你以為這樣就完了嗎?當然沒有,如果Chapter 9有這麼簡單,我也不會到現在還在翻它...。

第一,除了Standard Request之外,它還定義了Class Request跟Vendor Request。Class Request之後會提到,每種USB Class都會規定它專屬的Class Request。Vendor Request則是這個裝置特有的要求,說真的很少裝置會提供...就算提供,HOST也不一定會讓程式發出這類要求。

第二,還記得USB裝置必須支援Test Mode來量測眼圖嗎?要怎麼進Test Mode?當然就是透過Set Feature這個要求啦。








No comments:

codeblock