USB之基本協議和資料波形1

Vedic發表於2019-05-30

 

=============  本系列參考  =============

《圈圈教你玩USB》、《Linux那些事兒之我是USB》

協議文件:https://www.usb.org/document-library/usb-20-specification  usb_20_20190524/usb_20.pdf

====================================

前言:

  我們先不一上來講USB大而全的協議規範文件, 會讓人退而卻步, 只要有協議, 在資料傳輸上波形就有規律可循, 翻譯成資料, 也先不管USB1.1/2.0等版本, 因為最終的傳輸單元是一樣的

 

一. 最基本傳輸單位 --包(packet)

1.電氣訊號:

  a. 採用D+/D-差分訊號傳輸, LSB在前, NRZI編碼也就是0反轉, 1不反轉, 遇到連續6個1強插一個0

 

  b. 低速Lowspeed 1.5Mb/s, 全速Fullspeed 12Mb/s, 高速Highspeed 480Mb/s, USB1.1支援L/F  USB2.0支援L/F/H USB3.0也支援L/F/H 同時支援OTG功能

  c. OTG(on the go) 就是多了根ID線, 用於判斷主控器作Host還是device

  d. L/F S採用電壓傳輸(3.3v), HS採用電流傳輸(等效電阻後示波器顯示400mv)

  e. 傳輸方向以Host為準, 即IN表示device資料到Host, OUT表示Host資料到device

  f. 插入上電波形分析(下面單獨抽出來分析)

2. packet格式

      SYNC同步域  +  PID域  +  資料域  +  CRC  +  EOP

  a. 同步域: L/FS 固定00000001, HS前面31個0後一個1

  b. PID佔一個位元組,高4bit是低4bit的反碼, 用於校驗PID本身, 而PID[3:0] 表示該packet的型別(協議文件8.3.1):

    

    打*表示USB1.1不支援的, 而USB2.0全支援, 這裡要特別注意令牌包, 任何事務傳輸, 必須先發個令牌包說明意圖, 至於後面是否需要資料包還是握手吧取決事務型別(下面會說)

  c. 資料域是可選的, 取決PID是不是資料包(DATA0/1/2/M) 

  d. CRC也是可選的, PID自校驗,所以只對資料域校驗,  若資料域沒有那CRC也沒有, 令牌包的資料域採用CRC5校驗, 資料包的資料域採用CRC16校驗

  e. EOP結束包, 對於L/FS是兩個資料位寬的SE0訊號(D+/D-都是0),  對於HS使用故意位填充表示(待具體解釋)

  f. 空閒狀態, 在SYNC同步域前和EOP後 匯流排上處於空閒狀態, L/FS是一根高電平一根低電平(也就是J或K狀態, 後面會講), HS是SE0表示空閒狀態

  g. SYNC同步域、EOP、CRC是硬體發射器自動新增和硬體接收器自動解析的, 軟體看到的只有PID域和資料域

    

2. packet型別

  根據PID[3:0]可以將包的型別分成4類

  a. 令牌包: 一次USB傳輸必須首發令牌包, 告知意圖, 同時後面資料表示跟哪個裝置及端點通訊, 這點很重要, 設想一下一個Host接了很多外設, Host發出的訊號會到達所有hub和普通外設, 如何避免串擾呢?

        那就是匯流排某一時刻只有一個外設與Host通訊, 外設硬體介面只響應令牌包, 因為令牌包的資料域表示裝置的地址和端點地址, 外設可以解析是否和自己匹配, 如果是則響應(使能硬體接收資料), 以及後續的資料包互動, 如果不是

        就不響應, 當然後續的資料包也會被外設硬體遮蔽, 不理會匯流排訊號, 除非一段時間後又檢測到令牌包, 再次進行地址匹配, 符合才使能硬體接收匯流排上的訊號

   IN OUT SETUP 包的資料域包含7bit裝置地址和4bit端點地址, 所以一個Host能夠最多接127個裝置(0是外設剛插入時的預設地址, 握手後必須賦值非0, 不然下一個裝置也是0就衝突了),  一個裝置端點最多隻能16個(端點0是必須的, 所以其他最多15個)

      

 

 

   SOF(幀起始包) 相當於心跳包, 讓所有外設知道Host還在活動(哪怕Host不是跟該裝置通訊但起碼知道跟其他裝置通訊), L/FS每隔1ms發一次, 每發一次11bit幀號加1, HS把1ms分割8份即每隔125us發一次, 但這8份裡面的11bit幀號是相同的

  

    這個心跳包主要用於休眠喚醒用的, 當Host沒有發SOF超過3ms時(一般是Host自己進入休眠或者想外設休眠), 外設設定自己進入低功耗狀態(如果支援), 然後進入監聽模式如果檢測到匯流排有訊號變化(只要跟睡眠前不一樣)立即喚醒,

  可能是Host要召喚裝置了, 當然裝置也可以喚醒Host, Host進入休眠也會設定監聽匯流排狀態,外設被人為喚醒改變匯流排訊號接著喚醒Host

 

  b. 資料包: 這沒啥好說的就是PID表明自己是資料包(DATA0還是DATA1主要用於 確保對方收到), 後面就是位元組資料了, 這裡需要注意就是沒有告知這個資料包到底多少個資料, 所以我猜想外設接收PID域後, 每接收一個位元組counter計數器加1

        直到EOP, 然後減2 CRC16校驗值就是資料量, 接著對FIFO資料CRC16和最後兩個位元組對比, 不一致就產生資料錯誤中斷, 一致就產生資料成功中斷並將資料量填充RX counter暫存器

     

  c. 握手包: 告知對方狀態, 比如Host傳送IN令牌包, 接著裝置傳送資料包, 然後Host接收完傳送ACK握手包告知裝置成功接收

    不用資料域!

  d. 特殊包主要用於高速, 比如上面Host發完IN令牌包後, 裝置應該要發資料包的, 但裝置還沒準備好資料, 導致Host等待超時, Host可以再次發IN包讓裝置進入傳送資料, Host切換等待接收資料狀態, 

    這裡有兩個小問題, 一是裝置資料未準備好, 卻沒有有效方式告知Host, 只能啥都不做靠超時告知, 浪費Host時間, 二是IN包讓裝置進入傳送資料模式, 裝置有資料早發了還等你吹, 還讓外設進入傳送模式影響準備資料

    而PING特殊包就是當第一次超時後, Host不發IN包改發PING包詢問裝置準備好沒, 裝置若準備好了回覆ACK握手包, 接著Host再發IN包, 如果還沒準備好就發NAK告知, Host就知道裝置還沒準備好而不用死等超時,

    其他幾個讀者可自行查閱

 

   總結: 匯流排是一個一個packet傳輸的, 且訊號達到所有外設, 當傳送SYNC域所有外設接收並調整時鐘取樣點做好同步, 接著解析PID域,  如果是非令牌包就不理會(只有已被選中的外設才理會), 如果是令牌包就解析後面地址是否和自己匹配,

      不匹配繼續不會理, 匹配的使能硬體接收資料功能, 並根據PID是IN OUT SETUP SOF再細分, 如果是OUT,產生OUT中斷, 軟體應該清空使能FIFO準備接收資料, 如果是IN, 產生IN中斷, 軟體要填充好即將發的資料然後使能端點傳送,

      如果是SETUP包(Host會接著發DATA0資料包資料域包含8個位元組的標準請求), 裝置要清空特殊FIFO並做好接受下一個資料, 接受完才產生SETUP中斷, 軟體就解析FIFO裡的8byte標準請求, 然後準備資料, 比如是獲取裝置描述符請求

      那軟體得準備好裝置描述符快取並ACK(必須ACK不能NAK)回覆, 然後Host會發IN包, 接著裝置IN中斷將剛才準備好的裝置描述符快取丟到端點0發出去!

      如果是SOF包, 裝置會重置時間計數器, 當3ms內沒有新的SOF包, 就會產生中斷, 裝置知道匯流排現在是空閒狀態, 可以自行決定是否休眠

 

 

 二、 事務--四種傳輸型別

  一個個packet只是一盤散沙, 通過組織起來作為一個有效傳輸我們稱之為事務, 所以一個事務起碼包含:

  一個令牌包, 通過地址選中具體外設

  可選的資料包, 如果是IN/OUT/SETUP包那後續有資料包, 如果是 SOF則資料包和握手吧都沒有

  可選的握手包, 像視訊聊天這種實時傳輸不需要ACK應該, 丟了就丟了, 省下頻寬不如用來發資料

  因此, 根據具體的使用場景, 事務可以分成四種傳輸型別:

1. 批量傳輸(Bulk transfers

  一個批量事務包含三個階段, 令牌包階段 + 資料包階段 + 握手包階段, 其中資料包階段可以發一個或多個資料包

              

 

  以Beagle USB 480 邏輯分析儀抓U盤上電時序時為例, 期間Host(PC機)會讀取U盤資料(bluk傳輸), 我們可以猜測應該發一個讀取U盤根目錄命令, 然後讀取扇區資訊, 如下:    

                 

  一個讀取扇區資訊命令分別為 Command + Data + Status, Command是一個寫操作, 往裝置傳送資料告知想幹嘛, 然後就是讀資料, 最後檢查狀態, 可以看到這些操作都由三個packet構成 IN/OUT令牌包 + 資料包 + 握手包

  因為U盤每次操作只能512byte/block, 所以想讀取多個扇區只能分多次IN操作(傳輸最大位元組數端點描述符有說明)

 

2. 中斷傳輸(Interrupt transfers

   一箇中斷事務跟批量事務類似, 不同在於傳輸量比較少, 且希望Host每隔一段時間來訪問裝置(不是靠硬體中斷告知系統, 而是端點描述符有個時間間隔變數, 告知Host最好小於這個時間間隔來訪問裝置), 像滑鼠鍵盤都是這類傳輸模式, 

   以Beagle USB 480 邏輯分析儀抓鍵盤為例:

  

  這裡可以看出三點, 一是Host每間隔x時間就發起一次讀取鍵盤資料操作(還是老樣子 IN包 + DATA0包 + ACK包); 二是如果我沒敲鍵盤, 則裝置NAK告知Host沒有資料; 三是間隔時間約 72/10  344/44 = 8ms

  檢視鍵盤端點描述符bInterval=1, 根據datasheet代表1ms, 即鍵盤希望Host每隔1ms讀取一次資料, 但採不採納在於Host端

          

 

 3. 等時傳輸(Isochronous transfers

  等時事務跟前兩種也差不多, 不同在於對時間敏感, 對資料準確性不關心, 所以不需要握手包, 主要用於音訊、視訊類裝置

 

4. 控制傳輸(Control transfers

   控制傳輸稍微複雜一點, 上面三個一個傳輸就是一個事務, 但控制傳輸有三個狀態, 每個狀態對應一個事務, 所以需要三次事務

三次過程分別為:

  建立過程:SETUP令牌包 + DATA0資料包(標準請求就在這) + ACK握手包(裝置必須返回ACK, 不能NAK 如果裝置連這個都不能保證的話就別玩了)

  資料過程: 可選, 如上面是獲取裝置描述符這裡就是 IN令牌包 + DATA1資料包 + ACK握手包; 如果是設定地址請求, 地址在請求內部了, 不需要資料過程 

  狀態過程: 上面的資料過程必須是同一個方向的, 如果方向改變, 則就是狀態過程, 如果沒有資料過程, 則這個資料包就是狀態過程不管哪個方向

      

  以Beagle USB 480 邏輯分析儀抓U盤為例:

     

  從捕捉的資料可看到, 建立過程的資料包包含著標準請求 80 06 00 01 00 00 12 00 (小端排序) , 前面的C3是PID, 後面E0 F4 是CRC16, 可以通過http://www.ip33.com/crc.html 驗證

 80 06 0100 0000 0012
struct usb_ctrlrequest {
    __u8 bRequestType; //0x80
    __u8 bRequest;    //0x06
    __le16 wValue;     //0x100   
    __le16 wIndex;    //0
    __le16 wLength;  //0x12
} __attribute__ ((packed));
具體請參考協議文件9-4

 

  上面log還有個有趣的現象: 狀態過程傳送1位元組0x00資料包,  U盤竟然返回NAK, 不知為何,  由於是高速模式下, 所以Host接下來會發PING包探測U盤是否ready, 直到U盤迴復ACK才再次傳送OUT包,如果是L/FS則繼續發OUT包直到接收ACK

 

剩餘資料的解析將在下一篇博文講解!

 

 

 

 

 

 

 

 

 

相關文章