============= 本系列參考 =============
《圈圈教你玩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
剩餘資料的解析將在下一篇博文講解!