轉自https://www.cnblogs.com/shiwenjie/p/9817220.html
使用 usbmon 抓取 usb 總線上的數據
usbmon 即 usb monitor,是 linux 內置的 usb 抓包工具。usbmon 本質是一個內核模塊,在我的 ubuntu14.0 4中,模塊的位置:/lib/modules/4.4.0-31-generic/kernel/drivers/usb/mon/usbmon.ko。
1、檢測內核是否支持 debugfs 文件系統
linux 系統支持很多類型的文件系統,像 ext3、sysfs、ramfs、tmpfs等文件系統,首先檢測內核是否支持 debugfs 文件系統。
2、掛載 debugfs 文件系統
執行 sudo mount -t debugfs none_debugs /sys/kernel/debug 命令,如果提示已經掛載,則下次抓包就無需運行該命令了,表示系統默認會掛載該文件系統。
如上圖所示,我的 ubuntu 系統默認已經掛載了 debugfs 文件系統,無需再去手動掛載。
3、確認內核支持 usbmon 模塊
如上圖所示,目前內核不支持 usbmon 模塊,需要手動安裝 usbmon 模塊。
4、安裝 usbmon 模塊
執行 modprobe usbmon 命令,可以看到系統成功安裝了 usbmon 這個模塊。
5、監測 usb 總線上的數據

lsusb命令參數
-v:顯示usb設備的詳細信息
-t:以樹狀結構顯示usb設備的層次
-s<總線:設備號>:僅顯示指定的總線或設備號的設備
-d<廠商:產品>:僅顯示指定廠商和產品編號的設備
方法2:cat /sys/kernel/debug/usb/devices

詳解 usbmon 抓取的 log 各字段的含義
在上篇文章中,我們已經介紹了如何在 linux 下使用 usbmon 抓取 usb 總線上數據的方法。(https://www.cnblogs.com/shiwenjie/p/8467057.html)
數據抓取到了,但是放眼一看,密密麻麻的全是數字,它們代表什么含義呢?本文將為你揭曉答案。
1. 預備知識
在解讀 usbmon 抓取的數據包的含義之前,我們需要了解一下與 usb 傳輸有關的基礎知識,這樣才能更好的理解數據包的各個字段所代表的含義。
務必要明確一點:usb 總線上傳輸的數據是以包為基本單位的,但是不能隨意的使用包來傳輸數據,必須按照一定的關系把這些不同的包組織成事務(transaction)進行傳輸。
1.1 usb 包的種類
介紹一下 usb 包的種類,總體上分為四類:令牌包、數據包、握手包、特殊包。
1)令牌包
令牌包用來發起一次 usb 傳輸,因為 usb 是主從結構的拓撲結構,所有的數據傳輸都是由主機發起的,設備只能被動的響應,這就需要主機發送一個令牌包來通知哪個設備進行響應,如何響應。
令牌包有 4 種,分別為輸出(OUT)、輸入(IN)、建立(SETUP)和幀起始(SOF)。各個包的功能見下表:
2)數據包:顧名思義,數據包就是用來傳輸數據的,可以從主機到設備,也可以從設備到主機,方向由令牌包來指定。
3)握手包:握手包的發送者一般為數據接收者,用來表示一個傳輸是否被對方確認。在傳輸正常的情況下,主機/設備會發送一個表示傳輸正確的 ACK 握手包。
4)特殊包:特殊包用在一些特殊的場合,這里就不介紹了。
1.2 usb 的事務
介紹了 usb 包的分類,下面就要着重介紹 usb 的事務(transaction)及傳輸類型。前面已經說了,我們不能隨意的使用 usb 包來傳輸數據,必須按照一定的關系把這些不同的包組織成事務才能傳輸數據。
那么事務是什么呢? 事務通常由三個包組成:令牌包、數據包和握手包。
※注意:usbmon 只抓取事務(transaction)中的數據包,不會抓取令牌包和握手包。
usb 協議規定了 4 種傳輸類型:批量傳輸、等時傳輸、中斷傳輸和控制傳輸。其中,批量傳輸、等時傳輸、中斷傳輸每傳輸一次數據都是一個事務;控制傳輸包括三個過程,建立過程和狀態過程分別是一個事務,數據過程可能包含多個事務。
1.3 usb 的四種傳輸類型
下面用圖來簡單的表示這四種傳輸的過程
1)批量傳輸
2)等時傳輸
3)中斷傳輸
4)控制傳輸
2. 分析 usbmon 抓取的 log
前面介紹了 usb 協議的一些基礎知識,下面就開始分析 usbmon 抓取的 log。
貼一張在 ubuntu 下抓取的 4G 模塊撥號上網的 log。
下面從左到右分析這些字段代表的含義:
1)URB Tag - URB 標簽
該字段表示驅動中定義的struct urb 結構體變量在內核空間的地址,可以使用該字段區分不同的 URB 數據包。
下面兩張圖分別是在 64 位和 32 位 系統上抓取的數據包。可以看到 URB Tag 字段的長度和系統的位數相同。
2)Timestamp - 時間戳
該字段表示的是時間戳,單位是微秒。1微秒 = 10^(-6)秒。
該時間是由下面的 mon_get_timestamp() 函數獲取的,使用 do_gettimeofday() 獲取的時間 位與(&) 上了0xFFF,因此usbmon log顯示的秒數范圍為0 ~ 4096s
// 2773就是當前URB的時間戳
3)Event Type - 事件的類型
S - submission,向 usb controller 提交 URB
C - callback,URB 提交完成后的回調
E - submission error,向 usb controller 提交 URB 發生錯誤
4)Address word - 地址字段
這個字段包含 4 部分,各個部分之間使用分號隔開,這 4 部分分別是 URB 類型及傳輸方向、usb 總線號、usb 設備地址、端點號。
URB 類型及傳輸方向:usb 有四種傳輸方式,分別是控制傳輸(Control)、批量傳輸(Bulk)、等時傳輸(Isochronous)和中斷傳輸(Interrupt)。usb 數據的傳輸方向是以 Host 端為參考對象的,Host 向 usb 設備發送數據那么傳輸方向就是 Output,Host 讀取 usb 設備的數據那么傳輸方向就是 Input。
usb 總線號:該字段表示 usb 總線號,Host 端一般有多個usb controller,每個usb controller 都有一條對應的 usb 總線,使用 usb 總線號區分它們,usb 設備可以掛接到某條總線上。
usb 設備地址:該字段表示 usb 設備的地址,每一個 usb 設備經過枚舉后在 Host 端都有一個唯一的地址。
端點號:表明該次數據傳輸是 Input/Output 到設備的哪個端點。上圖中該字段是 0,就表示這次數據傳輸使用的是設備的端點 0(控制端點)。
5)URB Status word - URB 的狀態
這個字段有兩種表示形式:
① s + 一串數字
② 一串以分號間隔的數字(或單個數字)構成的,這串數字包含下面幾個部分:URB status、interval、start frame 和 error count。特別注意一點,該字段不同於 "Address word" 字段,對於不同的傳輸方式這幾部分是可選的,並非所有部分都是必須的,下圖是不同的傳輸方式包含的信息。
下面分析不同的傳輸方式所包含的信息:
批量傳輸:只包含 URB status 這個字段,它對應着 struct urb 結構體中的 status 成員變量,表示 URB 的狀態。URB status 僅僅對 Event Type 中的 Callback 有意義,對於 Submission 是無意義的,之所以這么做是為了統一格式,方便使用腳本分析 usbmon 的 log。URB status 的具體含義見內核對於該成員變量的注釋。
中斷傳輸:URB status 和 Interval,URB status 見前面的分析,Interval 表示該 URB 對端點輪詢的間隔時間。
等時傳輸:URB status、Interval、start frame 和 error count。等時傳輸包含了所有部分,start frame 和 error count 是等時傳輸所特有的字段,但因為我們的驅動程序中不使用等時傳輸這種方式,因此這里不做過多的分析。
控制傳輸:控制傳輸在提交時(S:submission)這個字段是 s,這里的 s 后面緊跟的數據是控制傳輸的建立過程主機發送的數據包,可以參考前面控制傳輸的示意圖。控制傳輸在回調時(C:callback),這個字段代表的是 URB status。
6)Setup packet - 控制傳輸建立階段的數據包
如果是控制傳輸,這個字段表示控制傳輸建立階段主機發給設備的數據包。
該字段從左到右的格式如下,括號中的數字表示該部分占用的字節大小:
bmRequestType(1) + bRequest(1) + wvalue(2) + wIndex(2) + wLength(2)
每個字段的含義可以在 usb2.0 規范中找到,這部分與 usb 的標准請求等相關。
7)Data Length - 數據包的長度
對於 S(Submission),Data Length 字段是主機請求發送/讀取的數據長度,但是設備並不一定能夠接收/發送主機請求的數據長度。實際接收/發送的數據長度在 C(Callback)中的 Data Length 字段。
8)Data tag - 數據標簽
”=” 后面緊跟數據流
“>” 表示這是一次 Output 數據傳輸
“<” 表示這是一次 Input 數據傳輸
9)Data words follow - 數據流
這個字段就是一個事務(transaction)中的數據包,我們平時分析 usbmon 的 log 定位問題,也主要是看這個字段。注意一點,這個字段實際顯示的數據 <= Data Length 的值。
3. 使用 usbmon 分析解決問題
下面舉一個使用 usbmon 定位問題的小例子:
客戶由於沒有正確的根據文檔移植驅動,可能在 qmi_wwan 驅動中沒有剝除以太網頭,就導致 quectel-CM 在通過 dhcp 獲取 IP 地址階段失敗,可以通過抓取的 usbmon 的 log 快速的定位問題。
下圖是 usbmon 抓取的 log,可以看到在 dhcp 階段,主機發送的數據包是以太網包而非 IP 包,從而導致獲取 IP 失敗,進而定位到是驅動沒有正確的移植。
USB URB的status及其代表的意義
平時在處理客戶問題時,經常需要分析出現問題時抓取的usbmon log,這個log中有一個字段非常重要:URB Status word,這個字段就是struct urb結構體中的status成員變量。
通過這個status的值,可以分析出模塊端和HOST端usb的一些狀態,有利於定位到問題點。
Linux內核中對該成員變量的注釋如下:
表明該字段僅僅表示批量傳輸、控制傳輸和中斷傳輸的URB狀態,等時傳輸不在其中,不過我們的模組也沒有等時傳輸的端點。
在講解URB的status之前先看下linux中URB的處理流程,如下圖所示:
① 使用usb_alloc_urb()創建一個URB結構體
② 對於不同的傳輸方式,分別使用相應的函數初始化URB,中斷傳輸使用usb_fill_int_urb(),批量傳輸使用usb_fill_bulk_urb(),控制傳輸使用usb_fill_control_urb(),等時傳輸比較特殊需要手動初始化URB。
③ 使用usb_submit_urb()向usb控制器提交上一步初始化完成的URB,提交成功函數返回0,提交失敗函數返回一個負數。當提交失敗時,usbmon log中的Event Type字段就為 E(submission error)。
④ URB被提交到了usb控制器中,這個URB將來會有三種命運,而這三種URB的結束方式就對應到URB的status。
第一種:成功完成傳輸
第二種:傳輸發生了錯誤
第三種:URB從usb控制器中"unlinked"
⑤ 當URB結束時,會調用之前注冊的complete回調函數
URB成功傳輸的情況
在這種情況下,URB被成功發送給設備,並且設備返回正確的確認。對於一個輸出URB,表示數據被成功發送;對於一個輸入URB,成功獲取請求的數據。此時,urb->status = 0;
下面是一條qmi消息成功交互的過程:
ffff8800b7d91740 1791319201 S Ii:3:008:9 -115:256 8 < // 這里的-115(EINPROGRESS)僅僅表示URB被提交給usb控制器去處理了
ffff8800b7d90600 1791319354 S Co:3:008:0 s 21 00 0000 0004 000c 12 = 010b0000 00000001 27000000
ffff8800b7d90600 1791320572 C Co:3:008:0 0 12 > // 這里urb status為0,表示qmi request消息成功發送給模塊了
ffff8800b7d91740 1791329515 C Ii:3:008:9 0:256 8 = a1010000 04000000 // 這里Ti的urb status為0,表明可以讀取qmi的response信息了
ffff8800b7d915c0 1791329530 S Ci:3:008:0 s a1 01 0000 0004 1000 4096 <
ffff8800b7d91740 1791329570 S Ii:3:008:9 -115:256 8 <
ffff8800b7d915c0 1791331992 C Ci:3:008:0 0 19 = 01120080 00000101 27000700 02040000 000000 // 這里返回0,表示HOST端成功的讀取了模塊的qmi response
再看一下AT指令的交互過程:
ffff8800b1480f00 2819829981 S Bo:3:008:3 -115 1 = 61
ffff8800b1480f00 2819830240 C Bo:3:008:3 0 1 > // 成功向模塊發送了 A
ffff8800b1480f00 2819926000 S Bo:3:008:3 -115 1 = 74
ffff8800b1480f00 2819926105 C Bo:3:008:3 0 1 > // 成功向模塊發送了T
ffff8800b1480f00 2821094084 S Bo:3:008:3 -115 1 = 0d
ffff8800b1480f00 2821094267 C Bo:3:008:3 0 1 > // 成功向模塊發送了換行
ffff8800b1481e00 2821094738 C Bi:3:008:4 0 6 = 0d0a4f4b 0d0a
ffff8800b1481e00 2821094752 S Bi:3:008:4 -115 4096 <
ffff8800b1481b00 2821097257 C Ii:3:008:5 0:256 10 = a1200000 02000200 0000 // 成功讀取模塊的AT Response ==> OK
URB傳輸異常
urb傳輸異常就有很多種了,下面列舉一下常見的錯誤對應的status:
注:下面的usb status狀態碼在 linux-x.x.x/Documentation/usb/error-codes.txt 這個文件中有詳細的說明,可以參考一下。
-EINPROGRESS (-115)
這個字段前面已經說過了,它出現在URB submission的時候,表示這個URB的控制權交給了usb控制器,將由usb控制器處理,這個並不是真正的錯誤。
-EPROTO (-71)
有兩種原因會導致這個錯誤:
-
- A bitstuff error happened during the transfer
- No response packet was received in time by the hardware
這個錯誤經常發生在usb設備枚舉失敗時,下圖就是EC25模塊在客戶樹莓派上枚舉失敗對應的log,URB的status字段為-71
-EILSEQ (-84)
這個錯誤不太常見,具體可以參考error-codes.txt
-ECOMM (-70)
對於IN URB,在數據傳輸中,接收USB設備數據的速度 > 將數據寫入內存中的速度
-ENOSR (-63)
在OUT URB數據傳輸中,數據不能從系統內存中獲取的足夠快,以便可以跟上請求的usb的數據速率
-EPIPE (-32)
這個表明usb管道"不通",批量端點可能會設置了halt條件,設置了這種條件的端點必然會堵塞管道,可以使用usb_clear_halt()清除halt
-EOVERFLOW (-75)
當發送給端點的數據包長度大於端點特定的數據包長度時會出現這個錯誤,從錯誤碼也能看出來,數據“溢出了“
ENODEV (-19)
usb設備已經從系統中移除,但是hub沒有及時檢測到usb設備的斷開,驅動仍然提交URB,就會導致這個錯誤發生。
Note: -EPROTO、-EILSEQ、-EOVERFLOW一般是由於usb設備、設備固件或usb數據線出問題導致的。
URB從usb控制器中"unlinked"
"unlinked"發生下下面的場景下:
(1)驅動中調用 usb_unlink_urb( ) 或 usb_kill_urb( ) 主動取消一個已經提交給usb控制器的URB
調用 usb_unlink_urb( ) 之后,URB的status值為-104(ECONNRESET)
調用 usb_kill_urb( ) 之后,URB的status值為-2(ENOENT)
(2)當URB已經提交給某個usb設備,但該設備被remove了,硬件斷開了,比如將模塊從主機上拔掉,這種情況下URB的status字段就為-108(ESHUTDOWN)
下面舉幾個例子:
使用 busybox microcom /dev/ttyUSB2 命令打開模塊的AT口(open /dev/ttyUSB2),什么都不輸入,然后 ctrl+x 退出 microcom(close /dev/ttyUSB2),相應的usbmon log如下:
可以看到,當主動退出microcom時,相應的URB的status字段值為-2,對應到串口驅動中就是調用了usb_wwan_close()中的usb_kill_urb()
對應的E(submission error)是在回調函數重新提交URB產生的,此時串口已經close,再去提交就產生了錯誤。
后面URB的status字段為-1(EPERM),這里分析 usb_submit_urb() 的代碼,可以看到-1是 usb_submit_urb() 的返回值
usb_submit_urb
return usb_hcd_submit_urb;
同樣使用 busybox microcom /dev/ttyUSB2 命令打開模塊的AT口,然后此時拔掉EVB板,斷開usb設備,對應的usbmon log如下,可以比較和前面的區別:
-108(ESHUTDOWN)是usb物理層斷開URB對應的status
下面再看一下模塊autosuspend過程中的usbmon log:
echo auto > /sys/bus/usb/devices/3-2.1/power/control 使用模塊的autosuspend
ffff8800ba722240 2220899617 S Ci:3:009:0 s 80 00 0000 0000 0002 2 <
ffff8800ba722240 2220901604 C Ci:3:009:0 0 2 = 0000
ffff8800b7da3a40 2220901706 S Ii:3:009:5 -115:256 10 <
ffff8800b7da2180 2220901759 S Bi:3:009:4 -115 4096 <
ffff8800b7da2240 2220901798 S Bi:3:009:4 -115 4096 <
ffff8800b7da2f00 2220901824 S Bi:3:009:4 -115 4096 <
ffff8800b7da2d80 2220901850 S Bi:3:009:4 -115 4096 <
ffff8800ba722240 2220901891 S Co:3:009:0 s 21 22 0003 0002 0000 0
ffff8800ba722240 2220902834 C Co:3:009:0 0 0
ffff8800b7da3a40 2220905343 C Ii:3:009:5 0:256 10 = a1200000 02000200 0000
ffff8800b7da3a40 2220905360 S Ii:3:009:5 -115:256 10 <
ffff8800b7da2180 2223569116 C Bi:3:009:4 -2 0 // ① 這里等待了2s(2223, 569116 - 2220, 905360),然后進入了autosuspend ② -2是這個URB在suspend函數中被usb_kill_urb()取消了
ffff8800b7da2180 2223569160 S Bi:3:009:4 -115 4096 <
ffff8800b7da2180 2223569172 E Bi:3:009:4 -1 0 // 這里和前面出現的原因相同,模塊已經進入autosuspend,回調函數中又去提交這個URB
ffff8800b7da2240 2223569519 C Bi:3:009:4 -2 0
ffff8800b7da2240 2223569523 S Bi:3:009:4 -115 4096 <
ffff8800b7da2240 2223569528 E Bi:3:009:4 -1 0
ffff8800b7da2f00 2223569776 C Bi:3:009:4 -2 0
ffff8800b7da2f00 2223569810 S Bi:3:009:4 -115 4096 <
ffff8800b7da2f00 2223569815 E Bi:3:009:4 -1 0
ffff8800b7da2d80 2223570119 C Bi:3:009:4 -2 0
ffff8800b7da2d80 2223570159 S Bi:3:009:4 -115 4096 <
ffff8800b7da2d80 2223570167 E Bi:3:009:4 -1 0
ffff8800b7da3a40 2223571635 C Ii:3:009:5 -2:256 0
ffff8800ba722540 2223571783 S Co:3:009:0 s 00 03 0001 0000 0000 0
ffff8800ba722540 2223572943 C Co:3:009:0 0 0
ffff8800ba722540 2223573179 S Co:3:003:0 s 23 03 0002 0001 0000 0 // 這里是hub發出的,Port1:PORT_SUSPEND
ffff8800ba722540 2223574608 C Co:3:003:0 0 0