Skip to main content

Accessing USB Connected Hardware Through WSL

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:

  1. From PowerShell: wsl --install -d Ubuntu-18.04.
  2. Launch Ubuntu (ubuntu) and finish first-boot setup.
  3. wsl --update.
  4. 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).

  1. Grab the latest .msi from the usbipd-win releases page and run it.

  2. The first attach to WSL needs an elevated PowerShell. Subsequent attaches don’t.

  3. 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 shared
  4. Attach it: usbipd wsl attach --busid=<BUSID>.

  5. Verify with usbipd wsl list — the device should now show as Attached.

    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 shared
  6. From 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.

  1. /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')
  2. /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 0

    The directory at /var/spool/usbip/attach is the kill switch. While it exists, the loop keeps re-attaching. usbip-detach removes it.

  3. /usr/bin/usbip-detach:

    #!/bin/bash
    rmdir /var/spool/usbip/attach
  4. Mark all three executable: sudo chmod +x /usr/bin/<filename>.

Connect the device [WSL]
#

  1. Plug the device in and run sudo usbip-list. The bus ID I want is 2-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)
  2. Attach with auto-reconnect: sudo -b usbip-attach <busid>.

  3. 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.