============= 本系列參考 =============html
《圈圈教你玩USB》、《Linux那些事兒之我是USB》緩存
協議文檔:https://www.usb.org/document-library/usb-20-specification usb_20_20190524/usb_20.pdf工具
調試工具:Beagle USB 480 邏輯分析儀編碼
====================================spa
前言:翻譯
咱們先不一上來說USB大而全的協議規範文檔, 會讓人退而卻步, 只要有協議, 在數據傳輸上波形就有規律可循, 翻譯成數據, 也先無論USB1.1/2.0等版本, 由於最終的傳輸單元是同樣的3d
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功能code
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. 插入上電波形分析(下面單獨抽出來分析)
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域和數據域
根據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應該, 丟了就丟了, 省下帶寬不如用來發數據
所以, 根據具體的使用場景, 事務能夠分紅四種傳輸類型:
一個批量事務包含三個階段, 令牌包階段 + 數據包階段 + 握手包階段, 其中數據包階段能夠發一個或多個數據包
以Beagle USB 480 邏輯分析儀抓U盤上電時序時爲例, 期間Host(PC機)會讀取U盤數據(bluk傳輸), 咱們能夠猜想應該發一個讀取U盤根目錄命令, 而後讀取扇區信息, 以下:
一個讀取扇區信息命令分別爲 Command + Data + Status, Command是一個寫操做, 往設備發送數據告知想幹嗎, 而後就是讀數據, 最後檢查狀態, 能夠看到這些操做都由三個packet構成 IN/OUT令牌包 + 數據包 + 握手包
由於U盤每次操做只能512byte/block, 因此想讀取多個扇區只能分屢次IN操做(傳輸最大字節數端點描述符有說明)
一箇中斷事務跟批量事務相似, 不一樣在於傳輸量比較少, 且但願Host每隔一段時間來訪問設備(不是靠硬件中斷告知系統, 而是端點描述符有個時間間隔變量, 告知Host最好小於這個時間間隔來訪問設備), 像鼠標鍵盤都是這類傳輸模式,
以Beagle USB 480 邏輯分析儀抓鍵盤爲例:
這裏能夠看出三點, 一是Host每間隔x時間就發起一次讀取鍵盤數據操做(仍是老樣子 IN包 + DATA0包 + ACK包); 二是若是我沒敲鍵盤, 則設備NAK告知Host沒有數據; 三是間隔時間約 72/10 344/44 = 8ms
查看鍵盤端點描述符bInterval=1, 根據datasheet表明1ms, 即鍵盤但願Host每隔1ms讀取一次數據, 但採不採納在於Host端
等時事務跟前兩種也差很少, 不一樣在於對時間敏感, 對數據準確性不關心, 因此不須要握手包, 主要用於音頻、視頻類設備
控制傳輸稍微複雜一點, 上面三個一個傳輸就是一個事務, 但控制傳輸有三個狀態, 每一個狀態對應一個事務, 因此須要三次事務
三次過程分別爲:
創建過程: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
剩餘數據的解析將在下一篇博文講解!