Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

devusb

CI

Agent-friendly USB power control for the embedded bench, built for the Agentic Firmware Workbench. Switch, monitor, and power-cycle USB devices — from the command line or from an AI agent via MCP.

Typical use: an agent flashing and testing firmware hits a hung debug probe or wedged dev board. Instead of asking a human to unplug it, the agent runs power_cycle and carries on.

Supported hardware

devusb drives two kinds of hub through one interface.

Yepkit YKUSH family — dedicated bench hardware, spoken to directly over USB HID (hidapi). No vendor utility needed. Addressed by serial number.

Hub Switched ports Status
YKUSH3 3 verified on hardware
YKUSH 3 same protocol, untested
YKUSH XS 1 same protocol, untested

Generic power-switchable hubs — any hub advertising per-port power switching (ppps), driven through uhubctl. Many ordinary hubs and docks qualify, so this often works with hardware you already own. Addressed by uhubctl location (e.g. 35-2.1.4), since ordinary hubs rarely carry unique serial numbers. Verified on Realtek RTS5411 and GenesysLogic hubs.

Install

pip install .

Requires Python 3.10+. For generic hub support, also install uhubctl:

brew install uhubctl        # macOS
sudo apt install uhubctl    # Debian/Ubuntu

YKUSH control never depends on uhubctl — if it is missing, generic hubs are simply absent from the listing.

Linux: udev rules

On Linux, non-root access to the hub's HID interface needs a udev rule:

sudo cp udev/99-devusb.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules && sudo udevadm trigger

Then unplug and replug the hub. macOS and Windows need no extra setup.

CLI

devusb list                      # every controllable hub, both backends
devusb list --ykush-only         # skip generic hubs
devusb status                    # power state of each port
devusb off 1                     # cut power to port 1
devusb on 1                      # restore power
devusb cycle 1 --hold 10         # power-cycle: off 10 s, back on
devusb on all                    # every port
devusb --json status             # machine-readable output

--hub picks which hub to talk to — a YKUSH serial or a generic hub location:

devusb --hub Y3N14127 status     # YKUSH by serial
devusb --hub 35-2.1.4 status     # generic hub by location
devusb --hub 35-2.1.4 cycle 2    # cycle a port on that hub

With exactly one YKUSH attached, --hub is optional. Generic hubs must always be named: they share the listing with the rest of the machine, so devusb will not pick one for you.

Safety

Cutting power to the wrong port has real consequences — an unmounted disk, a dark monitor, a dropped network link. Two rules apply everywhere:

  • Never cut power to a device mid-flash.
  • After power returns, give the device a few seconds to re-enumerate before talking to it.

The protected-port guard

Generic hubs get an extra guard, because a typical machine lists a lot of hubs that have nothing to do with the bench. devusb refuses to switch off a port whose downstream subtree contains storage, a display, input hardware, or networking:

$ devusb --hub 35-4.1 off 1
error: refusing to cut power to 35-4.1 port 1: network device attached
(0bda:8153 Realtek USB 10/100/1000 LAN 1010000CD). Pass force to override.

The check follows the whole subtree, not just the directly attached device — cutting a port kills everything below it, including devices on a downstream hub. devusb list marks guarded ports, and devusb status shows what is attached to each port so you can see what a port number really means:

$ devusb --hub 35-4.1 status
port 1: on  [0bda:8153 Realtek USB 10/100/1000 LAN 1010000CD]  (protected)
port 2: on  [05e3:0620 GenesysLogic USB3.2 Hub, USB 3.20, 4 ports, ppps]  (protected)
port 3: on
port 4: on  [0bda:0411 Generic 4-Port USB 3.0 Hub, USB 3.00, 4 ports, ppps]

Pass --force (CLI) or force=True (MCP) to override. Detection is best-effort keyword matching on what uhubctl reports, so treat it as a seatbelt, not a proof: check status before switching an unfamiliar port.

YKUSH hubs have no guard — every port is a dedicated bench port.

Interoperability with uhubctl

The YKUSH3 is built on Microchip hub silicon (USB5744/USB2744) that also supports generic per-port power switching, so a YKUSH can appear under both backends. Don't drive the same hub both ways: the YKUSH firmware doesn't track changes made behind its back, and port numbering differs between the two paths (e.g. YKUSH port 1 is internal hub port 3). Pick one controller per hub — prefer the ykush backend, which is purpose-built.

MCP server

devusb-mcp runs an MCP server over stdio exposing list_hubs, port_status, port_on, port_off, and power_cycle — with agent-facing safety guidance baked into the tool descriptions, including the instruction to call port_status before switching a generic hub port and to confirm with the user before forcing past the guard.

Register with Claude Code:

claude mcp add --scope user devusb devusb-mcp

Any MCP-capable agent host works the same way — point it at the devusb-mcp executable.

Roadmap

  • Acroname programmable hubs — the premium lab option.
  • Paired USB 2.0 / 3.x port switching for generic hubs: a physical port often appears as separate hub instances per speed, and cutting only one leaves the device half-powered. uhubctl handles this pairing; devusb currently exposes each instance separately.

Development

pip install -e ".[dev]"
pytest

The suite runs against fake HID and uhubctl layers — no hardware required. The uhubctl fake renders real uhubctl output so the parser is exercised.

Before pushing, run it once with the host's tools hidden:

env PATH=/usr/bin:/bin "$(command -v python3)" -m pytest -q

The fakes are meant to be hermetic, but a developer machine has uhubctl installed and CI does not — so a test that quietly reaches past the fake to the real binary still passes locally and fails in CI. Stripping PATH catches that before you push. ($(command -v python3) resolves the interpreter before PATH is emptied; a bare pytest would no longer be findable.)

License

MIT

About

USB power control for the embedded bench — CLI + MCP server for AI agents (Yepkit YKUSH hubs)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages