Try   HackMD

WSL2 中連接 USB 的疑難雜症

Reference

前言

WSL 是一個能在 windows 撰寫 linux 程式並測試的優秀平台,搭配 VsCode 能夠在 Windows 和 Linux 來回切換,然而 WSL 預設是不會將掛載於 Windows 的外部設備直接掛載到 Linux 的內核之中,例如 USB 就需要透過 usbipd-win 這個專案來協助將外部設備掛載到 WSL 之中。

安裝 usbipd-win

In Windows

該軟體可以透過 winget 進行安裝

winget install usbipd

安裝完成後 Windows 系統中將多出一個名為 usbipd 的 service

到此 Windows 作業系統上的操作完成

In Linux

接下來切換到 WSL 中,以 Ubuntu 作業系統為例

sudo apt install linux-tools-generic hwdata
sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-tools/*-generic/usbip 20

How to use?

安裝完成後依循下列步驟:

  1. 確認 WSL 已經開啟
  2. usbipd wsl list 找出你要掛載到 WSL 的 USB budid
BUSID  VID:PID    DEVICE                                                        STATE
1-1    0483:374f  ST-Link Debug, USB Mass Storage Device, STMicroelectronic...  Not attached
1-3    0483:bdda  USB 序列裝置 (COM5)                                           Attached - Ubuntu
1-9    13d3:5463  USB2.0 HD UVC WebCam, Camera DFU Device                       Not attached
1-10   8087:0026  Intel(R) Wireless Bluetooth(R)                                Not attached
  1. 若想掛載 USB 序列裝置 (COM5),輸入以下指令
# mnt for one time, not functioning after replug
usbipd wsl attach --busid 1-3

# unmnt
usbipd wsl detach --busid 1-3

# mnt specific usb once it plugs in
usbipd wsl attach --busid 1-3 --auto-attach
  • flag auto-attach 能夠在不關閉輸入該指令的 powershell 時,持續監測指定 bus-id 的 usb 插拔情況,並在偵測到時自動掛載到 WSL 中,是 usbipd-win 專案中能夠自動掛載的最適選項 。
//call usbipd wsl attach --busid 1-3 --auto-attach
Attached
Detached // remove usb
usbip: error: Attach Request for 1-3 failed - Device busy (exported)
usbip: error: Attach Request for 1-3 failed - Device not found
Attached // plug in again

以下是相關 issue

  1. 回到 WSL,使用 lsusb 指令應該就能看到被掛載的 USB 裝置

USB 裝置無法開啟

當我們結束上述動作後,欣喜的嘗試讀寫 USB 就會發現 permission denied 出現在 terminal 中,但可以用 sudo 讀寫

於是我們開始檢查使用者是否在 dialout 之中,發現我們已經存在於該群組之中,理論上應該不用 sudo 即可讀寫 tty 設備

最後我們會發現,所有 tty 相關設備的權限都是 600,而且所屬都不是 dialout,只有 root 可以讀寫,似乎是一個 bug QQ

crw------- 1 root root 166,   0 Mar 25 08:00 ttyACM0
crw------- 1 root root   4,  64 Mar 23 22:06 ttyS0
crw------- 1 root root   4,  65 Mar 23 22:06 ttyS1
crw------- 1 root root   4,  66 Mar 23 22:06 ttyS2
crw------- 1 root root   4,  67 Mar 23 22:06 ttyS3

但好在我們可以通過 udev 修復,以下修復請搭配

  1. WSL2 中是沒有 udev 的,需要手動調用 sudo service udev start,當然可以將其設置為開啟 WSL 時調用,參考 How to enable a service to start with WSL2?

    ​​​​# 在 /etc/wsl.conf  新增 or 修改
    ​​​​# 注意不可以有空格
    ​​​​[boot]
    ​​​​command="service udev start"
    

    之後用 sudo service udev status 檢測是否開啟

  2. 設定 udev 規則在 /etc/udev/rules.d
    a. 文件名 <0~99>-<some_name_you_want>.rules,數字低表優先級高
    例如: 10-tty-usb.rules
    b. 在文件裡寫入

    ​​​​# modify usb related permission
    ​​​​SUBSYSTEM=="usb", GROUP="dialout", MODE="0660"
    ​​​​SUBSYSTEM=="usb_device", GROUP="dialout", MODE="0660"
    

    這樣修改完,/dev 下的 USB/ACM (where libserial open) 和 /dev/bus/usb/xxx (where libusb open) 都會被修改成 group dialout

  3. 確認目前使用者在 dialout 裡面

  4. 重新開啟 WSL (wsl --shutdown in powershell),注意-八秒規則

就可以得到 (group from root -> dialout):

# under /dev
crw-rw---- 1 root dialout 166,   0 Mar 25 16:07 ttyACM0
crw-rw---- 1 root dialout   4,  64 Mar 25 16:06 ttyS0
crw-rw---- 1 root dialout   4,  65 Mar 25 16:06 ttyS1
crw-rw---- 1 root dialout   4,  66 Mar 25 16:06 ttyS2
crw-rw---- 1 root dialout   4,  67 Mar 25 16:06 ttyS3

# under /dev/bus/usb/???
crw-rw-r-- 1 root dialout 189, 0 Mar 25 17:10 001
crw-rw---- 1 root dialout 189, 1 Mar 25 17:10 002

usbip client not correctly installed

usbipd: error: WSL 'usbip' client not correctly installed. See https://github.com/dorssel/usbipd-win/wiki/WSL-support for the latest instructions.

sudo apt install linux-tools-virtual hwdata

sudo update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 22

// powershell
wsl --shutdown

請重開 WSL 之後重開 windows terminal

使更新 usbip 可以被 script 執行

sudo apt install linux-tools-virtual hwdata
sudo update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 22

# reboot cmd
cd /mnt/c/ && cmd.exe /c start "rebooting WSL" cmd /c "timeout 5 && wsl -d $WSL_DISTRO_NAME" && wsl.exe --terminate $WSL_DISTRO_NAME