Event Horizon of Thought

The limits of language mean the limits of the world.

← All articles

usb device management

windows

It interactively scans your Windows USB host bus, lists the available devices, lets you choose one, then bind the device to wsl.

param(
    [string]$Distro = ""   # WSL distro name; empty = default distro
)

Write-Host "====================================================" -ForegroundColor Cyan
Write-Host "    INTERACTIVE WINDOWS TO WSL USB BIND MANAGER"
Write-Host "====================================================" -ForegroundColor Cyan

if (-not (Get-Command usbipd -ErrorAction SilentlyContinue)) {
    Write-Error "usbipd not found. Install it with: winget install usbipd"
    exit 1
}

# 1. Fetch connected devices (lines that start with a BUSID such as 2-3).
#    Stop at the 'Persisted' section so stale entries are not listed.
$devices = @()
foreach ($line in (usbipd list)) {
    if ($line -match '^Persisted') { break }
    if ($line -match '^\d+-\d+\s') { $devices += $line.TrimEnd() }
}

if ($devices.Count -eq 0) {
    Write-Error "No USB devices detected or usbipd service is down."
    exit 1
}

# 2. Interactive selection menu
Write-Host "`nSelect the target USB device to attach to WSL:"
Write-Host "----------------------------------------------------"
for ($i = 0; $i -lt $devices.Count; $i++) {
    Write-Host ("  [{0}] {1}" -f ($i + 1), $devices[$i])
}
Write-Host "----------------------------------------------------"

$choice = 0
while ($choice -lt 1 -or $choice -gt $devices.Count) {
    $inputVal = Read-Host "Enter choice (1-$($devices.Count))"
    if ($inputVal -match '^[0-9]+$') {
        $choice = [int]$inputVal
    }
}

# 3. Parse BUSID
$selectedDevice = $devices[$choice - 1]
$busId = ($selectedDevice.Trim() -split '\s+')[0]
Write-Host "`nSelected BUSID: [$busId]" -ForegroundColor Green

# 4. Bind (once, needs admin) and attach
$distroArg = if ($Distro) { "--distribution $Distro" } else { "" }

if ($selectedDevice -match "Attached") {
    Write-Host "Device is already attached to a WSL instance." -ForegroundColor Yellow
} else {
    if ($selectedDevice -match "Not shared") {
        Write-Host "Binding device... (accept the admin UAC prompt)"
        Start-Process usbipd -ArgumentList "bind --busid $busId" -Verb RunAs -Wait
    }
    Write-Host "Attaching USB device to WSL..."
    # attach does not need admin once the device is bound
    usbipd attach --wsl $Distro --busid $busId 2>&1 | Out-Host
    if ($LASTEXITCODE -ne 0) {
        Write-Host "Attach failed (see message above)." -ForegroundColor Red
        exit 1
    }
}

# Done. Inside WSL run: lsusb ; ls /dev/ttyACM*
Write-Host "`nDone. In WSL run: lsusb ; ls /dev/ttyACM*" -ForegroundColor Green
Write-Host "===================================================="

ubuntu

Here is an updated, interactive version of the script. It scans the system for all connected USB devices, presents them in a numbered menu for you to select, and prompts you to enter your preferred custom name for the /dev/ symlink.

#!/usr/bin/env bash

# Exit on unexpected script errors
set -e

echo "===================================================="
echo "      🔮 INTERACTIVE USB DEVLINK GENERATOR"
echo "===================================================="

sudo usermod -a -G dialout $USER

# 1. Gather all USB devices with readable indexes
echo "Scanning USB bus..."
mapfile -t USB_DEVICES < <(lsusb | grep -v "root hub")

if [ ${#USB_DEVICES[@]} -eq 0 ]; then
    echo "❌ No external USB devices found. Is your device attached to WSL?"
    exit 1
fi

# 2. Present interactive selection menu
echo -e "\nSelect the target USB device:"
echo "----------------------------------------------------"
for i in "${!USB_DEVICES[@]}"; do
    echo "  [$((i+1))] ${USB_DEVICES[$i]}"
done
echo "----------------------------------------------------"

while true; do
    read -rp "Enter choice (1-${#USB_DEVICES[@]}): " CHOICE
    if [[ "$CHOICE" =~ ^[0-9]+$ ]] && [ "$CHOICE" -ge 1 ] && [ "$CHOICE" -le "${#USB_DEVICES[@]}" ]; then
        SELECTED_DEVICE="${USB_DEVICES[$((CHOICE-1))]}"
        break
    else
        echo "Invalid choice. Please pick a number from the menu."
    fi
done

echo -e "\nYou selected: $SELECTED_DEVICE"

# 3. Parse selected device IDs
VID_PID=$(echo "$SELECTED_DEVICE" | awk '{print $6}')
VID=$(echo "$VID_PID" | cut -d':' -f1)
PID=$(echo "$VID_PID" | cut -d':' -f2)

# 4. Prompt for custom device symlink name
echo -e "\n===================================================="
echo " STEP 2: Choose Target Symlink Name"
echo "===================================================="
read -rp "Enter desired dev name (e.g., u2can, my_motor, robot_arm): " CUSTOM_NAME

# Sanitize input (remove slashes or spaces)
CUSTOM_NAME=$(echo "$CUSTOM_NAME" | tr -cd '[:alnum:]_-')

if [ -z "$CUSTOM_NAME" ]; then
    echo "Defaulting to 'u2can_device' due to empty/invalid string."
    CUSTOM_NAME="u2can_device"
fi

TARGET_LINK="/dev/$CUSTOM_NAME"
echo "Your device will be linked at: $TARGET_LINK"

# 5. Generate and write the custom udev Rule
echo -e "\n===================================================="
echo " STEP 3: Writing Configuration Rules"
echo "===================================================="
RULE_FILE="/etc/udev/rules.d/99-interactive-usb.rules"
RULE_CONTENT="SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"$VID\", ATTRS{idProduct}==\"$PID\", MODE=\"0666\", SYMLINK+=\"$CUSTOM_NAME\""

echo "Writing rule to $RULE_FILE..."
echo "$RULE_CONTENT" | sudo tee "$RULE_FILE" > /dev/null

# 6. Reload the hardware management engine
echo -e "\n===================================================="
echo " STEP 4: Reloading Systems Engine"
echo "===================================================="
sudo udevadm control --reload-rules
sudo udevadm trigger
echo "udev rule matrix refreshed successfully!"

# 7. Check for dynamic device assignment status
echo -e "\n===================================================="
echo " STEP 5: Validating Hardware Mapping Engine"
echo "===================================================="
# Scan for an active device matching the IDs to extract the current tty path
TTY_MATCH=$(udevadm trigger --verbose --dry-run --subsystem-match=tty --attr-match=idVendor="$VID" --attr-match=idProduct="$PID" 2>/dev/null | head -n 1 || true)

if [ -n "$TTY_MATCH" ]; then
    NODE_NAME=$(basename "$TTY_MATCH")
    echo "System assigned hardware node: /dev/$NODE_NAME"
    echo "----------------------------------------------------"
    sudo udevadm info --query=property --name="/dev/$NODE_NAME" | grep -E "ID_VENDOR_ID|ID_MODEL_ID|DEVNAME|DEVLINKS"
    echo "----------------------------------------------------"
else
    echo "ℹ️ Device successfully registered in rule matrix."
    echo "If using WSL2, remember to detach and re-attach via Windows 'usbipd' to fully bind the custom link."
fi
echo "===================================================="