はじめに
Windowsで組み込み開発をするとき、どうしてもLinuxが必要になることがあるとおもいます。
こちらの記事にある通り、USB-IPD Winを使えばいけます。
usbipd list # usb deviceリスト取得
usbipd attach --busid 2-4 # attach, 管理者権限必要
usbipd bind --busid 2-4 # WSLにbind
が、毎回コマンドを打つのが面倒です。
特に何回も抜き差ししたり、Rebootを繰り返すと、とても面倒です。
というわけで自動化するPythonスクリプトを作ってみました。
スクリプトの使い方
Powershellから下記のようにコマンドを叩くと
usbipd list
下記のように、VID:PIDが出てきます。
Connected:
BUSID VID:PID DEVICE STATE
2-4 2fe3:0100 USB シリアル デバイス (COM5) Shared
その中から、WSL2に自動で接続させたいデバイスをスクリプトの中の、
conditions = [
["2fe3:0100"], # USB-CDC
]
の部分に記載します。
デスクトップに*.pyとして保存し、必要な時にダブルクリックして実行すればOKです。
bindが必要なときは管理者権限を要求しますのでOKしてください。
*ちなみに、上記のVID:PIDはZephyr RTOSのデフォルトのものです。
こちらの記事で使いました。(宣伝)
スクリプト本体
内容としては、無限ループして該当するbus-idのデバイスにbind/attachするだけです。
"""usbipd_attacher.py."""
import subprocess
import time
import ctypes
from ctypes import windll
"""
Run this script on Windows to automatically attach USB serial devices to WSL.
It uses usbipd-win.
"""
# Define the target USB devices (VendorID:ProductID)
conditions = [
["2fe3:0100"], # Example: USB-CDC
]
# Define excluded bus IDs if necessary
exclude_conditions_bus = []
# Input host IP address if you need to specify host address manually
host_ip = ""
def run_command_as_admin(executable, arguments):
"""
Execute a command with Administrator privileges using ShellExecuteW.
This triggers the UAC prompt.
"""
# 1 = SW_SHOWNORMAL (Show window normally)
# 0 = SW_HIDE (Hide window - useful if you don't want to see the popup console)
ret = windll.shell32.ShellExecuteW(
None, "runas", executable, arguments, None, 1
)
return ret > 32 # Returns True if execution was successful (started)
print("Starting usbipd auto-attacher...")
i = 0
while True:
# Get the list of usbipd devices
try:
ret = subprocess.run(["usbipd", "list"], capture_output=True)
strs = ret.stdout.decode("utf-8")
except FileNotFoundError:
print("Error: 'usbipd' command not found. Please install usbipd-win.")
time.sleep(5)
continue
# Initialize exclusion list
bus_is_excluded = [False] * 20
# Print list periodically for monitoring
if i % 5 == 0:
print("")
print("-------------------- usbipd list -----------------")
print(strs)
lines = strs.split("\n")
# Update exclusion list based on bus IDs
for s in lines:
try:
for c in exclude_conditions_bus:
if c in s:
# Parse bus ID (assumes single digit for simplicity, adjust if needed)
bus_idx = int(s.strip().split(" ")[0].split("-")[0])
bus_is_excluded[bus_idx] = True
except Exception:
pass
# Check devices and perform Bind/Attach
for s in lines:
try:
if not s or len(s.split(" ")) < 1:
continue
# Parse bus ID to check exclusion
try:
# Extracts "1" from "1-2"
bus_id_str = s.strip().split(" ")[0]
bus_root = int(bus_id_str.split("-")[0])
if bus_is_excluded[bus_root]:
continue
except:
pass # Skip if parsing fails (headers, etc.)
except Exception:
continue
# Check against target conditions
for cond in conditions:
is_ok = True
for c in cond:
if c not in s:
is_ok = False
if not is_ok:
continue
# --- Bind Logic (Requires Admin) ---
if "Not shared" in s:
print(f"[Action Required] Binding device: {s.strip()}")
device_id = s.split(" ")[0]
# Execute 'usbipd bind' with Admin privileges
success = run_command_as_admin("usbipd", f"bind --busid {device_id}")
if success:
print(" -> Admin prompt triggered. Waiting for bind to complete...")
# Wait for the user to click "Yes" and the bind to finish
time.sleep(3)
else:
print(" -> Failed to trigger admin prompt.")
# --- Attach Logic (Runs as current user) ---
elif "Shared" in s:
# Check if already attached (Depends on usbipd version output)
if "Attached" in s:
continue
print(f"[Action Required] Attaching device: {s.strip()}")
device_id = s.split(" ")[0]
cmd = ["usbipd", "attach", "--wsl", "--busid", device_id]
if host_ip != "":
cmd.append("--host-ip")
cmd.append(host_ip)
# Run attach command (standard subprocess is usually sufficient for attach)
subprocess.run(cmd)
time.sleep(1)
i += 1