Skip to content

usb: let the user set the gadget identity - #82

Merged
dormancygrace merged 1 commit into
mainfrom
claude/usb-identity
Oct 11, 2026
Merged

dormancygrace merged 1 commit into
mainfrom
claude/usb-identity

Conversation

@dormancygrace

Copy link
Copy Markdown
Owner

Upstream asks for this in five threads (sipeed/NanoKVM#829, #745, #574, #356, #104): corporate USB policies and endpoint security refuse a device called "sipeed NanoKVM", and the identity was hard-coded in S03usbdev and S03usbhid.

What the user gets

Settings → USB, below the manual section: USB identity with a preset select and four fields.

  • NanoKVM (factory): 0x3346:0x1009, sipeed / NanoKVM.
  • Generic composite device: the factory IDs with the names Generic / USB Composite Device — enough for the "it says NanoKVM" blocks.
  • Custom: vendor ID, product ID, manufacturer, product by hand. No preset carries another vendor's ID; a custom one is the user's own, and the page shows a warning that says what it breaks (below).
  • The serial number is shown read-only: it is the chip UID, and the Linux console helper's udev rule is bound to it.
  • bcdDevice is not exposed: hid.GetMode reads it as the mode flag.

The identity is part of the composition draft: Apply writes it with the rest and reconnects USB, and it is in the revision, so a stale tab cannot change it unseen.

How it is stored and applied

  • S03usbdev and S03usbhid already read /boot/usb.vid and /boot/usb.pid; they now also read /boot/usb.manufacturer and /boot/usb.product (first line, a missing or empty file is the factory value). The recovery init (platform/boot/init) already reads the manufacturer and product files but takes the IDs from its own usb.idVendor / usb.idProduct; it is inside initramfs.cpio.zst, which expected.sha256 pins, so aligning it waits for a platform build. Until then a recovery boot presents the factory IDs unless those two files are set by hand; the docs say so.
  • usb: a console helper for headless Linux hosts (script, helper disk, console test) #76 also edits S03usbdev (the lun.1 block around line 218); this change is in the identity block around line 101, so whichever merges first, the other rebases trivially.
  • The four files join usbCompositionFlags and the apply transaction: snapshotted with the ten flags, written by content (absent for a factory value), restored on failure. The flags keep their by-presence semantics, so usb.disk0's image path is untouched.
  • verifyUSBCompositionAt reads idVendor, idProduct and the two strings back from configfs after the start and compares them with the request.
  • GET /api/vm/device/virtual adds identity (vendorId, productId, manufacturer, product, serialNumber, default); PUT takes an optional identity object, and a request without it (an older client) keeps the identity in place. IDs 0x0001–0xffff, names 1–126 printable ASCII characters without leading or trailing spaces.

What a custom ID breaks, and what it does not

  • The serial console helper (usb: a console helper for headless Linux hosts (script, helper disk, console test) #76) and the hand-written udev rules in docs/usb-composition.md match 3346:1009 together with the serial number: a host set up with them stops matching a NanoKVM with other IDs. The warning on the page says so.
  • The Windows absolute-pointer profile binds through the ContainerID (a UUID in /etc/kvm/usb_container_id), not the IDs: unaffected.

Checked

  • Go: normalizeUSBID (forms, bounds, 0x0000), string rules, defaults (the zero identity is the factory one), the files an identity needs, reading /boot with a damaged file, verification against a fake configfs (old identity refused, new one passes), the transaction writing the files, keeping them when the apply back to the default fails, removing them when it succeeds, the revision; the existing transaction and rollback tests unchanged. go vet, go test -tags teststub ./service/vm/ ./proto/, go build ./....

  • Shell: tools/test-usb-profiles.sh, tools/test-usb-rebind.sh, shellcheck on the three scripts.

  • Web: presets, matching and validation in tests/usb-composition.test.mjs; tsc --noEmit, eslint, prettier, the i18n key test.

  • On the stand (script only, no server build): /boot/usb.manufacturer = Generic, /boot/usb.product = USB Composite Device, then the new S03usbdev stop + start → configfs strings/0x409/manufacturer = Generic, product = USB Composite Device, IDs unchanged, all six functions linked; on the Windows host Get-PnpDeviceProperty … DEVPKEY_Device_BusReportedDeviceDesc = USB Composite Device. Files removed, the installed script's stop + start → sipeed / NanoKVM, Windows reports NanoKVM again, the helper LUN (lun.1) back in place. Note for the reader: the script's restart action only rebinds the UDC and does not rewrite the descriptor; the server's apply uses stop + start, which does.

Not covered

  • S03usbhid carries the same two hunks but was grep- and syntax-checked only; the stand is in the normal profile.

🤖 Generated with Claude Code

Upstream asks for this five times (sipeed/NanoKVM#829, #745, #574, #356,
#104): corporate USB policies and endpoint security refuse a device called
"sipeed NanoKVM", and the identity was hard-coded in S03usbdev and
S03usbhid. The USB settings page gets a USB identity section: the factory
identity, a "generic composite device" preset (the factory IDs with the
names Generic / USB Composite Device), or the four fields by hand. No
preset carries another vendor's ID; a custom ID is the user's own, and the
page says what it breaks: the serial console helper's udev rule on Linux
hosts names the IDs. The serial number stays the chip UID (that rule is
bound to it), bcdDevice stays the mode flag.

The identity is part of the composition. The scripts read /boot/usb.vid,
usb.pid (as before), usb.manufacturer and usb.product (new) at every start.
The recovery init reads the manufacturer and product files already; it takes
the IDs from its own usb.idVendor/usb.idProduct, and it lives in the
checksummed initramfs, so aligning it waits for a platform build (the docs
say so). The four files join the apply transaction: snapshotted, written by content
(absent for the factory value), restored on failure, and the identity the
gadget presents is read back from configfs in the verification. It is in
the revision, so a stale tab cannot change it unseen. PUT takes an optional
identity object; a client that sends none keeps the one in place.

Tests: ID and string validation, defaults and the files an identity needs,
reading /boot (a damaged file is the factory value), verification against a
fake configfs, the transaction writing, keeping on rollback and removing the
files; the web presets, matching and validation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dormancygrace
dormancygrace merged commit 4dad283 into main Oct 11, 2026
13 checks passed
@dormancygrace
dormancygrace deleted the claude/usb-identity branch October 12, 2026 00:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant