USB 設備仿真介紹以及如何利用它

介紹

今天,設備的數量正在增加,現代操作系統必須嘗試在每次集成和每次發佈時支持所有類型和其中的幾種類型。 維護大量設備既困難又昂貴,也難以測試,尤其是對於 USB 記憶棒等即插即用設備。

因此,有必要建立一種機制,方便新舊USB設備的維護和測試。 這就是 USB 設備仿真發揮作用的地方。 這樣,一個包含大量模擬和驗證 USB 設備的完整框架將使集成和啟動更加容易。 應用領域將非常廣泛:即使在開發、自動測試、持續集成等過程中也能進行早期的錯誤搜索/檢測。

如何模擬 USB 設備

USB/IP 項目允許共享連接到本地計算機的 USB 設備,以便它們可以由通過 TCP/IP 連接連接到網絡的另一台計算機管理。

所以USB/IP項目由兩部分組成:

  • 本地(主機)設備支持以允許遠程訪問所有必要的事件和控制數據
  • 遠程控制,可捕獲像普通控制器一樣處理所需的每個控制事件和數據

該過程適用於 Linux 和 Windows,這裡我將只關注 Linux。

模擬背後的想法是用行為相同的應用程序替換遠程設備支持。 通過這種方式,我們可以使用遵循上述 USB/IP 協議規範的軟件應用程序來模擬設備。

在以下幾點中,我將描述如何配置和運行遠程支持以及如何連接到我們的模擬 USB 設備。

遠程支持

遠程支持分為兩部分:

  • 內核空間來控制遠程設備,就好像它是本地的一樣,即由普通驅動程序測試。
  • 用戶空間應用程序來配置對遠程設備的訪問。

此時,需要注意的是,設備仿真器在用戶空間應用程序配置後,將直接與內核空間通信。

本地支持具有非常相似的結構,但本文的重點是設備仿真。

讓我們分解遠程支持的每個部分。

核心空間

首先,要獲得功能,我們需要使用以下選項編譯 Linux 內核:

                      CONFIG_USBIP_CORE=m
                      
CONFIG_USBIP_VHCI_HCD=m

這些選項啟用在遠程計算機上運行的 USB/IP 虛擬主機控制器。

還應包括常規 USB 驅動程序,因為它們將以與虛擬主機控制器驅動程序相同的方式進行測試和配置。

此外還有其他重要的配置選項:

                      CONFIG_USBIP_VHCI_HC_PORTS=8
                      
CONFIG_USBIP_VHCI_NR_HCS=1

這些選項定義每個 USB/IP 虛擬主機控制器的端口數和 USB/IP 虛擬主機控制器的數量,就像添加物理主機控制器一樣。 如果啟用了 CONFIG_USBIP_VHCI_HCD,這些是默認值,必要時增加。

註釋選項和內核模塊已經包含在一些 Linux 發行版中,例如 Fedora linux

讓我們看一個我們稍後將使用的可用虛擬 USB 總線和端口的示例。

示例團隊中的默認資源和實際資源:

                      $ lsusb 
                      
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 0627:0001 Adomax Technology Co., Ltd
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$ lsusb -t
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/15p, 5000M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/15p, 480M
|__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 480M
$

現在,我們將 vhci-hcd 模塊加載到系統中(CONFIG_USBIP_VHCI_HC_PORTS 和 CONFIG_USBIP_VHCI_NR_HCS 的默認設置):

                      $ sudo modprobe vhci-hcd 
                      
$ lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 0627:0001 Adomax Technology Co., Ltd
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$ lsusb -t
/: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 5000M
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 480M
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/15p, 5000M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/15p, 480M
|__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 480M
$

遠程 USB/IP 虛擬主機控制器驅動程序將僅使用配置的虛擬化資源。 當然,模擬設備將以相同的方式工作。

用戶空間

USB/IP 項目中需要的另一部分是 usbip 用戶空間工具,它應該用於配置雙方引用的內核空間,儘管出於同樣的原因,我們只關注遠程端,因為本地端將由模擬器。

也就是說,usbip 工具會在內核空間配置 USB/IP 虛擬驅動程序(tcp 客戶端)連接到設備仿真器(tcp 服務器),以便在它們之間建立直接連接,用於 USB 配置、事件、數據等。 .

該工具獨立於設備類型,可以提供有關可用和保留資源的信息(有關更多信息,請參見下面的示例)。

本地 USB/IP 虛擬主機驅動程序必須指定要用於遠程訪問的總線端口對,對於模擬設備也是如此,但在這種情況下,這對可以是任何東西,因為沒有設備或實際資源。 不需要預訂。

該工具位於 Linux 內核存儲庫中以與其完全同步。

該工具在 Linux 內核存儲庫中的位置:./tools/usb/usbip

在像 Fedora Linux 這樣的發行版上,可以使用存儲庫中的 usbip 包安裝 usbip 實用程序。 如果找不到 usbip 實用程序或相關軟件包,請按照可用 README 文件中的說明進行編譯和安裝。 一個合適的 rpm 包也可以從usbip 模擬器存儲庫:

                      $ git clone https://github.com/jtornosm/USBIP-Virtual-USB-Device.git 
$ cd USBIP-Virtual-USB-Device/usbip 
$ make rpm 
...
$
                    

如何模擬 USB 設備

模擬器是用 Python 和 C 構建的。我已經開始使用 C 開發(我將專注於這一部分),但同樣可以在 Python 中完成。

對於 C 開發,從usbip 模擬器存儲庫:

                      $ git clone https://github.com/jtornosm/USBIP-Virtual-USB-Device.git 
$ cd USBIP-Virtual-USB-Device/c 
$ make 
...
$
                    

此時將生成所有模擬的兼容設備:

  • 隱藏鍵盤
  • 隱藏鼠標
  • cdc-adm
  • hso
  • CDC-醚
  • 英國電信

rpm 包(usbip-emulator)也可以通過以下方式生成:

                      $ make rpm 
...
$
                    

例如,供應商和產品 ID 編碼在代碼中。

以下三個示例展示了仿真的工作原理。 我們為模擬器和遠程 USB/IP 使用同一台機器,但它們可能在不同的機器上工作。 此外,我們保留了不同的資源,以便可以同時模擬所有設備。

示例 1:hso

從終端,讓我們模擬 hso 設備:

(“1-1”是本地機器上 USB 設備的總線端口對,因為我們模擬它可以是任何東西。這很重要,因為 usbip 工具必須使用相同的名稱來請求模擬設備)

                      $ hso -p 3241 -b 1-1 
hso started.... 
server usbip tcp port: 3241 
Bus-Port: 3-0:1.0 
...
                    

從另一個終端連接到模擬器:

(localhost,因為模擬器在同一台機器上運行,並且總線端口對的名稱與模擬器相同)

                      $ sudo modprobe vhci-hcd 
                      
$ sudo usbip --tcp-port 3241 attach -r 127.0.0.1 -b 1-1
usbip: info: using port 3241 ("3241")
$

現在我們可以檢查新設備是否存在:

(正如我們之前看到的,對於這個示例機器,總線 3 是虛擬化的)

                      $ ip addr show dev hso
0 3: hso0: <POINTOPOINT,MULTICAST,NOARP> mtu 1486 qdisc noop state DOWN group default qlen 10 
link/none 
$ rfkill list 
0: hso-0: Wireless WAN 
Soft blocked: no 
Hard blocked: no 
...
$ lsusb 
... 
Bus 003 Device 002: ID 0af0:6711 Option GlobeTrotter Express 7.2 v2 
... 
$ lsusb -t 
...
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 480M 
|__ Port 1: Dev 2, If 0, Class=Vendor Specific Class, Driver=hso, 12M 
...
$
                    

釋放資源:

                      $ sudo usbip port 
                      
Imported USB devices
====================
Port 00: <Port in Use> at Full Speed(12Mbps)
Option : GlobeTrotter Express 7.2 v2 (0af0:6711)
3-1 -> usbip://127.0.0.1:3241/1-1
-> remote bus/dev 001/002
$ sudo usbip detach -p 00
usbip: info: Port 0 is now detached!
$

我們可以檢查設備是否越獄:

                      $ ip addr show dev hso0 
Device "hso0" does not exist. 
$ rfkill list 
...
$ lsusb 
... 
$
                    

之後,我們可以從第一個終端重新模擬或停止模擬設備(即使用 Ctrl-C)。

示例 2:cdc-ether

從終端,讓我們模擬 cdc-ether 設備(需要 root 權限,因為原始套接字必須綁定到為數據平面指定的接口):

(“1-1”是本地機器上 USB 設備的總線端口對,因為我們模擬它可以是任何東西。這很重要,因為 usbip 工具必須使用相同的名稱來請求模擬設備)

                      $ sudo cdc-ether -e 88:00:66:99:5b:aa -i enp1s0 -p 3242 -b 1-1 
cdc-ether started.... 
server usbip tcp port: 3242 
Bus-Port: 1-1 
Ethernet address: 88:00:66:99:5b:aa 
Manufacturer: Temium 
Network interface to bind: enp1s0 
...
                    

從另一個終端連接到模擬器:

(localhost,因為模擬器在同一台機器上運行,並且總線端口對的名稱與模擬器相同)

                      $ sudo modprobe vhci-hcd 
                      
$ sudo usbip --tcp-port 3242 attach -r 127.0.0.1 -b 1-1
usbip: info: using port 3242 ("3242")
$

現在我們可以檢查新設備是否存在:

(正如我們之前看到的,對於這個示例機器,總線 3 是虛擬化的)

                      $ ip addr show dev eth0 
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000 
link/ether 88:00:66:99:5b:aa brd ff:ff:ff:ff:ff:ff 
$ sudo ethtool eth0 
...
Link detected: yes 
$ lsusb 
... 
Bus 003 Device 003: ID 0fe6:9900 ICS Advent 
... 
$ lsusb -t 
...
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 480M 
|__ Port 2: Dev 3, If 0, Class=Communications, Driver=cdc_ether, 480M 
|__ Port 2: Dev 3, If 1, Class=CDC Data, Driver=cdc_ether, 480M 
...
$
                    

對於此示例,我們還可以測試數據平面。

(兩邊都禁用IP轉發)

首先,我們可以在仿真設備上配置 IP 地址:

                      $ sudo ip addr add 10.0.0.1/24 dev eth0 
                      
$ ip addr show dev eth0
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
link/ether 88:00:66:99:5b:aa brd ff:ff:ff:ff:ff:ff
inet 10.0.0.1/24 scope global eth0
valid_lft forever preferred_lft forever
$

其次,例如,從另一台直接連接到以太網(真實或虛擬)的機器上,我們可以在同一子網上配置一個 macvlan 接口來發送/接收流量(ping、iperf 等):

                      $ sudo ip link add macvlan0 link enp1s0 type macvlan mode bridge 
$ sudo ip addr add 10.0.0.2/24 dev macvlan0 
$ sudo ip link set macvlan0 up 
$ ip addr show dev macvlan0 
3: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 
link/ether d6:f1:cd:f1:cc:02 brd ff:ff:ff:ff:ff:ff 
inet 10.0.0.2/24 scope global macvlan0 
valid_lft forever preferred_lft forever 
inet6 fe80::d4f1:cdff:fef1:cc02/64 scope link 
valid_lft forever preferred_lft forever 
$ ping 10.0.0.1 
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=55.6 ms 
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=2.19 ms 
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=1.74 ms 
64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=1.76 ms 
64 bytes from 10.0.0.1: icmp_seq=5 ttl=64 time=1.93 ms 
64 bytes from 10.0.0.1: icmp_seq=6 ttl=64 time=1.65 ms 
...
                    

釋放資源:

                      $ sudo usbip port 
Imported USB devices 
==================== 
...
Port 01: <Port in Use> at High Speed(480Mbps) 
ICS Advent : unknown product (0fe6:9900) 
3-2 -> usbip://127.0.0.1:3245/1-1 
-> remote bus/dev 001/003 
$ sudo usbip detach -p 01 
usbip: info: Port 1 is now detached! 
$
                    

我們可以檢查設備是否越獄:

                      $ ip addr show dev eth0 
                      
Device "eth0" does not exist.
$ lsusb
...
$

當然,來自另一台機器的流量不起作用:

                      From 10.0.0.2 icmp_seq=167 Destination Host Unreachable 
From 10.0.0.2 icmp_seq=168 Destination Host Unreachable 
From 10.0.0.2 icmp_seq=169 Destination Host Unreachable 
From 10.0.0.2 icmp_seq=170 Destination Host Unreachable 
...
                    

之後,我們可以從第一個終端重新模擬或停止模擬設備(即使用 Ctrl-C)。

示例 3:bt

從終端,讓我們模擬藍牙設備:

(“1-1”是本地機器上 USB 設備的總線端口對,因為我們模擬它可以是任何東西。這很重要,因為 usbip 工具必須使用相同的名稱來請求模擬設備)

                      $ bt -a aa:bb:cc:dd:ee:11 -p 3243 -b 1-1 
bt started.... 
server usbip tcp port: 3243 
Bus-Port: 1-1 
BD address: aa:bb:cc:dd:ee:11 
Manufacturer: Trust 
...
                    

從另一個終端連接到模擬器:

(localhost,因為模擬器在同一台機器上運行,並且總線端口對的名稱與模擬器相同)

                      $ sudo modprobe vhci-hcd 
                      
$ sudo usbip --tcp-port 3243 attach -r 127.0.0.1 -b 1-1
usbip: info: using port 3243 ("3243")
$

現在我們可以檢查新設備是否存在:

(正如我們之前看到的,對於這個示例機器,總線 3 是虛擬化的)

                      $ hciconfig -a 
hci0: Type: Primary Bus: USB 
BD Address: AA:BB:CC:DD:EE:11 ACL MTU: 310:10 SCO MTU: 64:8 
UP RUNNING PSCAN ISCAN INQUIRY 
RX bytes:1451 acl:0 sco:0 events:80 errors:0 
TX bytes:1115 acl:0 sco:0 commands:73 errors:0 
Features: 0xff 0xff 0x8f 0xfe 0xdb 0xff 0x5b 0x87 
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
Link policy: RSWITCH HOLD SNIFF PARK 
Link mode: SLAVE ACCEPT 
Name: 'BT USB TEST - CSR8510 A10' 
Class: 0x000000 
Service Classes: Unspecified 
Device Class: Miscellaneous, 
HCI Version: 4.0 (0x6) Revision: 0x22bb 
LMP Version: 3.0 (0x5) Subversion: 0x22bb 
Manufacturer: Cambridge Silicon Radio (10)

$ rfkill list 
...
1: hci0: Bluetooth 
Soft blocked: no 
Hard blocked: no 
$ lsusb 
...
Bus 003 Device 004: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode) 
...
$ lsusb -t 
...
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 480M 
|__ Port 3: Dev 4, If 0, Class=Wireless, Driver=btusb, 12M 
|__ Port 3: Dev 4, If 1, Class=Wireless, Driver=btusb, 12M 
...
$
                    

我們可以打開和關閉模擬的藍牙設備,檢測各種假藍牙設備:

(此時沒有模擬/模擬虛擬藍牙設備,因此我們無法對其進行配置)

關閉藍牙
激活藍牙

釋放資源:

                      $ sudo usbip port 
Imported USB devices 
==================== 
...
Port 02: <Port in Use> at Full Speed(12Mbps) 
Cambridge Silicon Radio, Ltd : Bluetooth Dongle (HCI mode) (0a12:0001) 
3-3 -> usbip://127.0.0.1:3243/1-1 
-> remote bus/dev 001/002 
$ sudo usbip detach -p 02 
usbip: info: Port 2 is now detached! 
$
                    

我們可以檢查設備是否越獄:

                      $ hciconfig 
$ rfkill list 
...
$ lsusb 
... 
$
                    

當然,設備沒有被檢測到(在仿真之前):

找不到藍牙

之後,我們可以從第一個終端重新模擬或停止模擬設備(即使用 Ctrl-C)。

模擬與真實 USB 設備

當真正的硬件和/或最終設備不用於測試時,我們總是會對結果感到不安全,這是我們通過仿真檢查設備的正確操作必須克服的最大障礙。

因此,為了自信,仿真必須盡可能接近真實硬件,並且為了獲得最真實的仿真,必須涵蓋設備的所有方面(或者至少是必要的,如果它們與其他方面無關)。 事實上,為了一個正確的測試,我們一定不能修改驅動,也就是只模擬物理層,所以驅動無法知道設備是真實的還是模擬的。

開始使用實際的硬件設備進行測試是一個非常好的主意,以獲取構建具有相同功能的模擬器的參考。 在 USB 設備的情況下,設備仿真器的構建更容易,因為現有的程序可以獲得符合上述所有特性的遙控器。

結論

USB 設備仿真是以高效、自動和簡單的方式集成和測試相關功能的最佳方式。 但是,為了對仿真過程有信心,必須首先驗證設備仿真器以確認它們與真實硬件的工作方式相同。

當然,USB設備仿真器和真實的硬件設備是不一樣的,但是註釋的方法,得益於經過驗證的設備遠程控製過程,非常接近真實場景,可以幫助我們提高很多。流程、啟動和測試。

最後,我想評論一下,使用軟件模擬器的最大優勢之一是我們將能夠以簡單的方式導致特定的行為,這將很難用真實的硬件重現,這有助於找到問題和更健壯。

相關文章