本文介绍的办法能够通过 Windows 侧安装的 USBIP 来将 USB 数据包转发至 Linux 侧的 USBIP,从而让 WSL 2 支持 USB 设备。默认情况下,WSL 2 的 Linux 内核并不支持 USB,本文将介绍如何向 WSL 2 的 Linux 内核中添加 USB 功能,以及如何用 USBIP 将 USB 设备连接至 Linux 上。
本文参考了:Dev on Windows with WSL、Adding USB support to WSL 2. 和 知乎文章
第一步:安装 USBIPD-WIN 连接 USB 设备
如果已经安装了 winget,在 Windows PowerShell 中直接安装即可
winget install usbipd
第二步:附加 USB 设备到 WSL 2
在附加 USB 设备之前,请确保 WSL 命令行已打开。 这将使 WSL 2 轻型 VM 保持活动状态。
通过以管理员模式打开 PowerShell 并输入以下命令,列出所有连接到 Windows 的 USB 设备。 列出设备后,选择并复制要附加到 WSL 的设备总线 ID。
usbipd list
在附加 USB 设备之前,必须使用命令 usbipd bind 来共享设备,从而允许它附加到 WSL。 这需要管理员权限。 选择要在 WSL 中使用的设备总线 ID,然后运行以下命令。 运行命令后,请再次使用命令 usbipd list 验证设备是否已共享。
usbipd bind --busid 4-4
若要附加 USB 设备,请运行以下命令。 (不再需要使用提升的管理员提示。)确保 WSL 命令提示符处于打开状态,以使 WSL 2 轻型 VM 保持活动状态。 请注意,只要 USB 设备连接到 WSL,Windows 将无法使用它。 附加到 WSL 后,任何作为 WSL 2 运行的分发版本都可以使用 USB 设备。 使用 usbipd list 验证设备是否已附加。 在 WSL 提示符下,运行 lsusb 以验证 USB 设备是否已列出,并且可以使用 Linux 工具与之交互。
usbipd attach --wsl --busid <busid>
打开 Ubuntu(或首选的 WSL 命令行),使用以下命令列出附加的 USB 设备:
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 004: ID 0951:1624 Kingston Technology DataTraveler G2
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
# 也可以使用 dmesg 查看内核信息
$ sudo dmesg
# /dev/sdd1 /dev/sdd2 是加载成功的 usb 磁盘,如果已经成功,则可忽略第三步
$ ls /dev/sd*
/dev/sda /dev/sdb /dev/sdc /dev/sdd /dev/sdd1 /dev/sdd2
# 挂载使用
$ sudo mkdir /mnt/test; sudo mount /dev/sdd2 /mnt/test
你应会看到刚刚附加的设备,并且能够使用常规 Linux 工具与之交互。 根据你的应用程序,你可能需要配置 udev 规则以允许非根用户访问设备。
在 WSL 中完成设备使用后,可物理断开 USB 设备,或者从 PowerShell 运行此命令:
usbipd detach --busid <busid>
# 或
usbipd detach -a
第三步:向 Linux 侧添加 USB 支持
这里我们将手动编译 Linux 内核,请注意可能出现的任何问题。缺啥安装啥
在 WSL 中,下载安装编译 Linux 内核所需要的库和工具:
$ sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev autoconf libudev-dev libtool
# 过程中发现缺少的包(可以先不安装,根据编译报错,缺啥安装啥)
$ sudo apt install gcc
$ sudo apt install dwarves
找到我们当前所使用的 Linux 内核名称:
$ uname -r
5.15.167.4-microsoft-standard-WSL2
这里我们得到的 5.15.167.4-microsoft-standard-WSL2 即为 WSL 的 Linux 内核名称。下文命令中将以此作为命令中所使用的内核名称,因此请将下文中涉及到这一内容的部分替换为你自己的内核版本名称。
将当前的 WSL 内核克隆至本地,Linux 内核源码一般放置在 /usr/src/<内核名称> 路径下。克隆完成后进入这一目录:
sudo git clone https://github.com/microsoft/WSL2-Linux-Kernel.git /usr/src/5.15.167.4-microsoft-standard-WSL2
cd /usr/src/5.15.167.4-microsoft-standard-WSL2
将 Git 分支 checkout 至我们的内核版本分支(这里是 linux-msft-wsl-5.15.167.4):
# 找到对应版本 branch 或 tag
$ git branch | grep 5.15.167
$ git tag | grep 5.15.167
linux-msft-wsl-5.15.167.4
# checkout 至对应内核版本tag
$ sudo git checkout linux-msft-wsl-5.15.167.4
将我们当前的内核设置复制进去:
sudo cp /proc/config.gz config.gz
sudo gunzip config.gz
sudo mv config .config
运行 menuconfig 来选择我们想要添加的内核模块:
# 默认使用上面拷贝的 .config 配置
$ sudo make menuconfig
# 指定 KCONFIG_CONFIG 文件
# $ sudo make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl
在 menuconfig 中选定我们想要添加的 USB 内核模块,以下这些模块就满足了我自己的个人需要,你可以根据你自己的需要选定不同的模块:
- *:将该功能编译进内核。
- 空:不将该功能编译进内核。
- M:将该功能编译成可以在需要时动态插入到内核中的模块。
中括号[]表示仅有两种选择(*或空),尖括号表示有三种选择(M,*或空),这里有两种选择
这里有两种选择(,配合第四步,比较方便,验证可行)
- 方案1. 全部选择内建到内核即选择 *,直接替换掉 WSL 2 bzImage —— 我们选择,再配合第6步,比较方便,验证可行
- 方案2. 动态插入 M,通过脚本方式加载对应模块 —— 再配合第7步
Device Drivers->USB support[*]
Device Drivers->USB support->Support for Host-side USB[*]
Device Drivers->USB support->Enable USB persist by default[*]
Device Drivers->USB support->USB Modem (CDC ACM) support[*]
Device Drivers->USB support->USB Mass Storage support[*]
Device Drivers->USB support->USB/IP support[*]
Device Drivers->USB support->VHCI hcd[*]
Device Drivers->USB support->VHCI hcd->Number of ports per USB/IP virtual host controller(8)
Device Drivers->USB support->Number of USB/IP virtual host controllers(1)
Device Drivers->USB support->USB Serial Converter support[*]
Device Drivers->USB support->USB Serial Converter support->USB FTDI Single Port Serial Driver[*]
Device Drivers->USB support->USB Physical Layer drivers->NOP USB Transceiver Driver[*]
Device Drivers->Network device support->USB Network Adapters[*]
Device Drivers->Network device support->USB Network Adapters->[Deselect everything you don't care about]
Device Drivers->Network device support->USB Network Adapters->Multi-purpose USB Networking Framework[*]
Device Drivers->Network device support->USB Network Adapters->CDC Ethernet support [smart devices such as cable modems](*)
Device Drivers->Network device support->USB Network Adapters->Multi-purpose USB Networking Framework->Host for RNDIS and ActiveSync devices[*]
方案1:开始编译内核,并配置使其生效(测试通过)
如果你的电脑拥有多个 CPU 核心,可以通过命令行设置 -j <核心数量> 来执行多核编译,大大加快编译速度。这一步骤将会持续较长时间,
# 使用默认配置文件
$ sudo make -j$(nproc) bzImage
...
DESCEND objtool
DESCEND bpf/resolve_btfids
CALL scripts/atomic/check-atomics.sh
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
Kernel: arch/x86/boot/bzImage is ready (#4)
# 指定 KCONFIG_CONFIG 文件
#$ sudo make -j$(nproc) bzImage KCONFIG_CONFIG=Microsoft/config-wsl
将刚编译好的内核 arch/x86/boot/bzImage 拷贝到 Windows 用户根目录下
PS C:\Users\yugua> ls
目录: C:\Users\yugua
Mode LastWriteTime Length Name
-a---- 2025/5/3 15:45 40 .wslconfig
-a---- 2025/5/3 16:22 14534592 bzImage
并创建一个 .wsconfig 配置文件,将 kernel 指向刚拷贝的内核文件 bzImage
PS C:\Users\yugua> cat .wslconfig
[wsl2]
kernel=C:\\Users\\yugua\\bzImage
方案2:编译动态模块,并配置加载方式使其生效 (未尝试)
编译安装下面命令中我们使用的进程数 12:
sudo make -j 12 && sudo make modules_install -j 12 && sudo make install -j 12
编译完成后我们将会看到我们都安装了哪些内核模块,我自己的模块列表如下:
INSTALL drivers/hid/hid-generic.ko
INSTALL drivers/hid/hid.ko
INSTALL drivers/hid/usbhid/usbhid.ko
INSTALL drivers/net/mii.ko
INSTALL drivers/net/usb/cdc_ether.ko
INSTALL drivers/net/usb/rndis_host.ko
INSTALL drivers/net/usb/usbnet.ko
INSTALL drivers/usb/class/cdc-acm.ko
INSTALL drivers/usb/common/usb-common.ko
INSTALL drivers/usb/core/usbcore.ko
INSTALL drivers/usb/serial/ftdi_sio.ko
INSTALL drivers/usb/phy/phy-generic.ko
INSTALL drivers/usb/serial/usbserial.ko
INSTALL drivers/usb/storage/usb-storage.ko
INSTALL drivers/usb/usbip/usbip-core.ko
INSTALL drivers/usb/usbip/vhci-hcd.ko
DEPMOD 4.19.43-microsoft-standard
编译 USBIP 工具:
cd tools/usb/usbip
sudo ./autogen.sh
sudo ./configure
sudo make install -j 12
将 USBIP 工具库复制到 USBIP 可以访问到的位置:
sudo cp libsrc/.libs/libusbip.so.0 /lib/libusbip.so.0
在我们的个人文件夹 $HOME 下新建一个脚本叫做 startusb.sh 用来 modprobe 全部驱动,确保先 modprobe usbcore 和 usb-common 这两个模块:
# !/bin/bash
sudo modprobe usbcore
sudo modprobe usb-common
sudo modprobe hid-generic
sudo modprobe hid
sudo modprobe usbnet
sudo modprobe cdc_ether
sudo modprobe rndis_host
sudo modprobe usbserial
sudo modprobe usb-storage
sudo modprobe cdc-acm
sudo modprobe ftdi_sio
sudo modprobe usbip-core
sudo modprobe vhci-hcd
echo $(cat /etc/resolv.conf | grep nameserver | awk '{print $2}')
其中,最后一行告诉我们 Windows 主机的 IP 地址,这一 IP 地址在我们向 Windows 设备中插入 USB 设备时会起到作用。另外,如果你遇到类似 /bin/bash^M: bad interpreter: No such file or directory 的报错信息,可能是因为你的脚本文件的换行符是 CRLF 而非 LF。
把我们新建的脚本文件赋予可执行权限:
sudo chmod +x startusb.sh
在 Windows PowerShell 中重启 WSL 2
wsl --shutdown
再次进入 WSL,并执行刚刚的脚本:
./startusb.sh
第四步:重启 WSL 2 使新内核生效
在 Windows PowerShell 中重启 WSL 2
wsl --shutdown
在 dmesg 中检测确保我们所有的 USB 驱动都载入成功:
# 确认使用了新内核
$ uname -r
5.15.167.4-microsoft-standard-WSL2+
$ sudo dmesg
# lsusb
$ lsusb
$ ls /dev/sd*
$ ls /dev/tty*
其他:WSL 2 Ubuntu 最新版本开启 Systemd 默认支持 Serial devices
$ nfc-list
nfc-list uses libnfc 1.8.0
error libnfc.driver.pn532_uart Invalid serial port: /dev/ttyUSB0
nfc-list: ERROR: Unable to open NFC device: pn532_uart:/dev/ttyUSB0
$ cat /etc/nfc/libnfc.conf
# Allow device auto-detection (default: true)
# Note: if this auto-detection is disabled, user has to set manually a device
# configuration using file or environment variable
#allow_autoscan = true
# Allow intrusive auto-detection (default: false)
# Warning: intrusive auto-detection can seriously disturb other devices
# This option is not recommended, user should prefer to add manually his device.
#allow_intrusive_scan = false
# Set log level (default: error)
# Valid log levels are (in order of verbosity): 0 (none), 1 (error), 2 (info), 3 (debug)
# Note: if you compiled with --enable-debug option, the default log level is "debug"
#log_level = 1
# Manually set default device (no default)
# To set a default device, you must set both name and connstring for your device
# Note: if autoscan is enabled, default device will be the first device available in device list.
device.name = "microBuilder.eu"
device.connstring = "pn532_uart:/dev/ttyUSB0"
RS232 cannot be discovered as /dev/ttyS* or /dev/ttyUSB* on ubuntu of wsl 2
nfc 读卡器还是无法使用,因为没有 /dev/ttyUSB*。找到以上几个相关信息,有一段时间 WSL 2 默认不支持 Serial Devices了,但是最新版本是默认编译成 module,并配合 systemd 支持了,解决方案如下:
# 编译并使用最新内核版本
git clone https://github.com/microsoft/WSL2-Linux-Kernel.git --depth=1 -b linux-msft-wsl-6.6.y
sudo apt update
sudo apt -y install build-essential flex bison libssl-dev libelf-dev bc
sudo apt -y install python3 pahole
cd WSL2-Linux-Kernel
## This line turns the FTDI driver from disabled or module to builtin
# 不需要,新版改成了module模式,配合systemd实现了自动加载 ./scripts/config --file Microsoft/config-wsl --set-val CONFIG_USB_SERIAL_FTDI_SIO y
## This line turns the CH341 driver from disabled or module to builtin
# 不需要,新版改成了module模式,配合systemd实现了自动加载 ./scripts/config --file Microsoft/config-wsl --set-val CONFIG_USB_SERIAL_CH341 y
make -j$(nproc) KCONFIG_CONFIG=Microsoft/config-wsl
# 安装 modules
sudo make modules_install headers_install
## 配置 WSL 2 使用新内核 Use another target directory if you don't want the kernel in the root of C:\
cp arch/x86/boot/bzImage /mnt/c/Users/yugua/bzImage-6.6
vi /mnt/c/Users/yugua/.wslconfig
cat !$
cat /mnt/c/Users/yugua/.wslconfig
[wsl2]
kernel=C:\\Users\\yugua\\bzImage-6.6