WSL doesn’t expose USB devices to the Linux side. The workaround is USB/IP — USB tunneled over TCP, with Windows as the server and WSL as the client. This is the setup I landed on. There are probably more correct ways to do it; this one worked.
Install and set up WSL [Windows]#
For the full instructions, see Microsoft’s guide. The short version:
- From PowerShell:
wsl --install -d Ubuntu-18.04. - Launch Ubuntu (
ubuntu) and finish first-boot setup. wsl --update.- Check the kernel version with
uname -r. If it’s 5.10.60.1 or higher, USB/IP support is built in. Older kernels need to be rebuilt — the usbipd developers have instructions.
Install usbipd [Windows]#
USB/IP is USB-over-TCP, so we need a server (Windows) and a client (WSL).
Grab the latest
.msifrom the usbipd-win releases page and run it.The first attach to WSL needs an elevated PowerShell. Subsequent attaches don’t.
List connected devices with
usbipd wsl list. I’ll use my Pixel 4a as the example.BUSID VID:PID DEVICE STATE 1-3 090c:3350 USB Mass Storage Device Not shared 2-6 18d1:4ee1 Pixel 4a Not shared 2-9 27c6:609c Framework Fingerprint Reader Not shared 2-10 8087:0032 Intel(R) Wireless Bluetooth(R) Not sharedAttach it:
usbipd wsl attach --busid=<BUSID>.Verify with
usbipd wsl list— the device should now show asAttached.BUSID VID:PID DEVICE STATE 1-3 090c:3350 USB Mass Storage Device Not shared 2-6 18d1:4ee1 Pixel 4a Attached 2-9 27c6:609c Framework Fingerprint Reader Not shared 2-10 8087:0032 Intel(R) Wireless Bluetooth(R) Not sharedFrom WSL, confirm with
lsusb:Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 004: ID 18d1:4ee1 Google Inc. Nexus Device (MTP) Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Helper scripts [WSL]#
At this point WSL can see the device, but if the connection drops — even briefly — control reverts to Windows. To make the attach sticky, I use three small scripts: list, attach (with auto-reconnect), detach.
/usr/bin/usbip-list:#!/bin/bash usbip list -r $(cat /etc/resolv.conf | sed -n 's/\(.*[^0-9]\|\)\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\2/p')/usr/bin/usbip-attach:#!/bin/bash mkdir /var/spool/usbip/attach # Get the Windows host IP IP=$(cat /etc/resolv.conf | sed -n 's/\(.*[^0-9]\|\)\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\2/p') SPOOL=/var/spool/usbip/attach touch $SPOOL # While the attach folder exists, keep trying to attach while [[ -e $SPOOL ]] do usbip attach -r $IP -b $1 1>/dev/null 2>&1 sleep 1 done # If the attach folder is gone, detach from port 0 usbip detach -p 0 exit 0The directory at
/var/spool/usbip/attachis the kill switch. While it exists, the loop keeps re-attaching.usbip-detachremoves it./usr/bin/usbip-detach:#!/bin/bash rmdir /var/spool/usbip/attachMark all three executable:
sudo chmod +x /usr/bin/<filename>.
Connect the device [WSL]#
Plug the device in and run
sudo usbip-list. The bus ID I want is2-6:Exportable USB devices ====================== - 192.168.16.1 2-6: Google Inc. : Nexus Device (MTP) (18d1:4ee1) : USB\VID_18D1&PID_4EE1\0A011JEC206170 : (Defined at Interface level) (00/00/00) : 0 - Imaging / Still Image Capture / Picture Transfer Protocol (PIMA 15470) (06/01/01)Attach with auto-reconnect:
sudo -b usbip-attach <busid>.When done:
sudo usbip-detach.
That’s it. Not the cleanest setup — a polling loop guarded by a directory’s existence is duct-tape work — but it held up well enough for the hardware projects I needed it for.