Linux
Linux cdc_acm 設備 - 發送到 USB 設備的意外字元
我在使用 cdc_acm 驅動程序在 USB 埠上發送意外字元時遇到問題。更令人困惑的是,程式碼在 Ubuntu 12.04(3.2 核心)上執行良好,但在 Centos 6(3.6 核心)上卻失敗了(這個問題的主題)。
USB 設備是 Bluegiga BLED112 藍牙智能加密狗。它的嵌入式微控制器將隨時在其 USB 介面上出現意外輸入時復位。
測試程式碼打開埠,寫入 4 個字節(一個 hello 消息)並期望讀取響應。讀取永遠不會完成,因為意外字元會導致設備重置,從而導致集線器丟棄設備並重新列舉。
為了排除故障,這是我所做的:
- 下載了 cdc_acm 驅動程序的原始碼。添加了一堆 printk 調試消息和 stack_dumps 來跟踪正在發生的事情。
- 我對“庫存”cdc_acm 進行了 rmmod,並且對我的檢測模組進行了 insmod。所有設備列舉都有效,附加了正確的驅動程序等。
- 由於程式碼在 Ubuntu 12.04/Linux 3.2 上執行,我獲取了 3.2 cdc_acm 程式碼並在 CentOS 6/Linux 3.6 平台上編譯了該模組。使用 3.2 模組而不是 3.6 模組並沒有什麼不同。我恢復到 3.6 模組。
- 使用 usbmon 打開調試文件系統並觀察 USB 流量。我可以看到 USB 介面上發送了額外的字元。
- 為了觀察發生了什麼,在 cdc_acm 模組中的 printk 之上,我合併了 usb mon 的輸出 (cat /sys/kernel/debug/usb/usbmon/3u | logger) 和測試應用程序的輸出 ( scan_example /dev/ttyACM0 | logger -s) 所以我有一個時間相關的調試跟踪流。
- 在 USB 端點上發送的虛假字元是 x5E x40 x5E x40 x5E x40 x5E x40 x41(在 ASCII 中是 ^@^@^@^@A),看起來像是某種探測或試圖引起調製解調器的注意這些字元在應用程序的 write() 導致 4 個 hello 字節被發送到端點後立即發送。
- 由於 cdc_acm 設備應該是調製解調器,因此我嘗試通過將其添加到 usb_device_id acm_ids 來關閉調製解調器控制
$$ $$在 cdc_acm.c
/* bluegiga BLED112*/ { USB_DEVICE(0x2458, 0x0001), .driver_info = NOT_A_MODEM, },
- 重新編譯和 insmod’d 和 syslog 顯示這已被辨識(怪癖是 8),但功能沒有變化。
NetowrkManager 和調製解調器管理器都沒有執行,但我仍然懷疑某處有某種調製解調器控制功能,我只是不知道在哪裡。
這是一個帶註釋的調試日誌(MDV 為我添加到 cdc_acm 的那些 printk 加上前綴)
Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_bulk Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_done
這是應用程序發送的 4 個字節 00 00 00 01
Feb 13 18:14:32 localhost cpcenter: df046a80 3672670191 C Bi:3:006:4 0 4 = 00000001 Feb 13 18:14:32 localhost cpcenter: 1360797272.669690 write: data2: len=0 contains:
…這些額外的字元出乎意料地出現了 5e 40 5e 40 5e 40 ….
Feb 13 18:14:32 localhost cpcenter: df046a80 3672670232 S Bi:3:006:4 -115 128 < Feb 13 18:14:32 localhost cpcenter: f3cc5740 3672670297 S Bo:3:006:4 -115 1 = 5e Feb 13 18:14:32 localhost cpcenter: df2e1300 3672670332 S Bo:3:006:4 -115 1 = 40 Feb 13 18:14:32 localhost cpcenter: f3cc5740 3672670347 C Bo:3:006:4 0 1 > Feb 13 18:14:32 localhost cpcenter: f3cc5740 3672670392 S Bo:3:006:4 -115 1 = 5e Feb 13 18:14:32 localhost cpcenter: df2e1180 3672670426 S Bo:3:006:4 -115 1 = 40 Feb 13 18:14:32 localhost cpcenter: df2e1c00 3672670461 S Bo:3:006:4 -115 1 = 5e Feb 13 18:14:32 localhost cpcenter: df2e1840 3672670496 S Bo:3:006:4 -115 1 = 40 Feb 13 18:14:32 localhost cpcenter: df2e1300 3672670591 C Bo:3:006:4 0 1 >
在這一點上,我們會自發地斷開連接。
Feb 13 18:14:32 localhost kernel: usb 3-1: USB disconnect, device number 6 Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_bulk Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_done Feb 13 18:14:32 localhost kernel: MDV:cdc-acm read_bulk_callback Feb 13 18:14:32 localhost kernel: MDV 1 acm_read_bulk_callback - urb 1, len 0 Feb 13 18:14:32 localhost kernel: MDV 3 acm_read_bulk_callback - non-zero urb status: -71 Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_bulk Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_done Feb 13 18:14:32 localhost kernel: MDV:cdc-acm read_bulk_callback Feb 13 18:14:32 localhost kernel: MDV 1 acm_read_bulk_callback - urb 1, len 0 Feb 13 18:14:32 localhost kernel: MDV 3 acm_read_bulk_callback - non-zero urb status: -71 Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_bulk Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_done Feb 13 18:14:32 localhost kernel: MDV:cdc-acm read_bulk_callback Feb 13 18:14:32 localhost kernel: MDV 1 acm_read_bulk_callback - urb 2, len 0 Feb 13 18:14:32 localhost cpcenter: df2e1d80 3672670629 S Bo:3:006:4 -115 1 = 5e Feb 13 18:14:32 localhost kernel: MDV 3 acm_read_bulk_callback - non-zero urb status: -71 Feb 13 18:14:32 localhost cpcenter: df2e1300 3672670677 S Bo:3:006:4 -115 1 = 41 Feb 13 18:14:32 localhost cpcenter: f3cc5740 3672670802 C Bo:3:006:4 0 1 > Feb 13 18:14:32 localhost cpcenter: df2e1180 3672671019 C Bo:3:006:4 0 1 > Feb 13 18:14:32 localhost cpcenter: df2e1c00 3672671237 C Bo:3:006:4 0 1 > Feb 13 18:14:32 localhost cpcenter: dfbf8c00 3672673193 C Ii:3:001:1 0:2048 1 = 02 Feb 13 18:14:32 localhost cpcenter: dfbf8c00 3672673207 S Ii:3:001:1 -115:2048 4 < Feb 13 18:14:32 localhost cpcenter: f3c26c00 3672673221 S Ci:3:001:0 s a3 00 0000 0001 0004 4 < Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_disconnect Feb 13 18:14:32 localhost kernel: Pid: 29, comm: khubd Tainted: G O 3.5.3-1.el6.elrepo.i686 #1
斷開連接時的堆棧跟踪
Feb 13 18:14:32 localhost kernel: Call Trace: Feb 13 18:14:32 localhost kernel: [<f82dabc5>] acm_disconnect+0x35/0x1f0 [cdc_acm] Feb 13 18:14:32 localhost kernel: [<c13835db>] usb_unbind_interface+0x4b/0x180 Feb 13 18:14:32 localhost cpcenter: f3c26c00 3672673239 C Ci:3:001:0 0 4 = 00010100 Feb 13 18:14:32 localhost kernel: [<c1318bfb>] __device_release_driver+0x5b/0xb0 Feb 13 18:14:32 localhost kernel: [<c1318d05>] device_release_driver+0x25/0x40 Feb 13 18:14:32 localhost kernel: [<c1317f0c>] bus_remove_device+0xcc/0x130 Feb 13 18:14:32 localhost kernel: [<c131612f>] ? device_remove_attrs+0x2f/0x90 Feb 13 18:14:32 localhost kernel: [<c1316275>] device_del+0xe5/0x180 Feb 13 18:14:32 localhost kernel: [<c1380326>] usb_disable_device+0x96/0x240 Feb 13 18:14:32 localhost kernel: [<c1379f91>] usb_disconnect+0x91/0x130 Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_bulk Feb 13 18:14:32 localhost kernel: [<c137a2c0>] hub_port_connect_change+0xb0/0xa60 Feb 13 18:14:32 localhost kernel: [<c1380f4e>] ? usb_control_msg+0xce/0xe0 Feb 13 18:14:32 localhost kernel: MDV:cdc-acm acm_write_done Feb 13 18:14:32 localhost kernel: [<c137b296>] hub_events+0x536/0x810 Feb 13 18:14:32 localhost cpcenter: f3c26c00 3672673243 S Co:3:001:0 s 23 01 0010 0001 0000 0 Feb 13 18:14:32 localhost cpcenter: f3c26c00 3672673250 C Co:3:001:0 0 0 Feb 13 18:14:32 localhost kernel: [<c1065bdf>] ? finish_wait+0x4f/0x70 Feb 13 18:14:32 localhost kernel: [<c137b5aa>] hub_thread+0x3a/0x1d0 Feb 13 18:14:32 localhost cpcenter: df2e1840 3672673260 C Bo:3:006:4 -71 0 Feb 13 18:14:32 localhost kernel: [<c1065a70>] ? wake_up_bit+0x30/0x30 Feb 13 18:14:32 localhost kernel: [<c137b570>] ? hub_events+0x810/0x810 Feb 13 18:14:32 localhost kernel: [<c106564c>] kthread+0x7c/0x90 Feb 13 18:14:32 localhost cpcenter: f3c16c80 3672673292 C Bi:3:006:4 -71 0 Feb 13 18:14:32 localhost cpcenter: df2e1d80 3672673453 C Bo:3:006:4 -71 0 Feb 13 18:14:32 localhost cpcenter: f3c16d40 3672673553 C Bi:3:006:4 -71 0 Feb 13 18:14:32 localhost kernel: [<c10655d0>] ? kthread_freezable_should_stop+0x60/0x60 Feb 13 18:14:32 localhost kernel: MDV:cdc-acm read_bulk_callback Feb 13 18:14:32 localhost kernel: [<c14dedbe>] kernel_thread_helper+0x6/0x10 Feb 13 18:14:32 localhost kernel: MDV 1 acm_read_bulk_callback - urb 3, len 0 Feb 13 18:14:32 localhost kernel: MDV:cdc-acm stop_data_traffic Feb 13 18:14:32 localhost cpcenter: f3d19500 3672674474 C Ii:3:006:2 -108:64 0 Feb 13 18:14:32 localhost kernel: MDV 2 acm_read_bulk_callback - disconnected Feb 13 18:14:32 localhost cpcenter: df2e1300 3672674636 C Bo:3:006:4 -71 0 Feb 13 18:14:32 localhost cpcenter: f3c16140 3672674753 C Bi:3:006:4 -71 0
添加更多核心跟踪後,我發現
localhost kernel: [<c12c6757>] process_echoes+0x117/0x2c0 localhost kernel: [<c12c8409>] n_tty_receive_char+0x379/0x770 localhost kernel: [<c121fce4>] ? rb_erase+0xb4/0x120 localhost kernel: [<c12c89f6>] n_tty_receive_buf+0x1f6/0x380 localhost kernel: [<c14d62cb>] ? __schedule+0x39b/0x6d0
這顯示了 tty 子系統回顯和擦除字元——那些是導致問題的字元。以下程式碼刪除了 tty 行規則,現在可以使用
struct termios usb_termio; // set the USB to raw mode memset(&usb_termio, 0, sizeof(usb_termio)); // clear the structure cfmakeraw(&usb_termio); if (tcsetattr(BLED_fd, TCSANOW, &usb_termio)< 0) { perror("tcsetattr usb"); return(1); }
Stack Overflow 的 @Sergey Vlasov 分析了 USB 消息跟踪,並從另一條路徑得出了相同的結論。他的解釋幫助我更好地理解了 usbmon 輸出 https://stackoverflow.com/questions/14866899/linux-cdc-acm-device-unexpected-characters-sent-to-device