Skip to content

fix(xhci): bounded EnableSlot/AddressDevice retry — transient timeout no longer kills the mouse for the whole session#412

Open
ryanbreen wants to merge 2 commits into
mainfrom
fix/xhci-enableslot-retry
Open

fix(xhci): bounded EnableSlot/AddressDevice retry — transient timeout no longer kills the mouse for the whole session#412
ryanbreen wants to merge 2 commits into
mainfrom
fix/xhci-enableslot-retry

Conversation

@ryanbreen

Copy link
Copy Markdown
Owner

The bug

xHCI port enumeration issued EnableSlot/AddressDevice single-shot, with no retry. A single transient timeout on port 1 (the mouse's port) meant the mouse silently enumerated onto slot 0 (or failed to enumerate at all) — the pointer would be frozen at 0,0 for the rest of the session, with no recovery path short of a reboot.

This was found live by the operator immediately after PR #411 landed, but xhci.rs was untouched by that PR — this is pre-existing latent fragility that PR #411 merely made visible (or was coincidental timing), not a regression it introduced.

The fix

Bounded 3-attempt retry around EnableSlot/AddressDevice, restricted to the cold enumeration path only (no changes to steady-state HID polling or any interrupt/syscall-hot-path code). A transient controller timeout on one attempt no longer permanently dooms that port's device for the session.

Harness assertion

scripts/parallels/launcher-smoke.sh now asserts on mouse=slotN where N != 0 — smoke runs FAIL if the mouse ends up bound to slot 0 (the failure signature this bug produces), so this class of regression can't silently reappear.

Validation — 3/3 smoke boots PASS with mouse enumerated

[
  {
    "boot": 1,
    "result": "PASS",
    "mouseEnumerated": true,
    "mouseEvidence": "[xhci] start_hid_polling: kbd=slot2/dci3 nkro=dci5 mouse=slot1/dci3 mouse2=dci5\n(no \"EnableSlot retry\" lines this run — all ports enumerated cleanly on first attempt: \"[xhci] port 1 EnableSlot -> slot 1\", \"[xhci] port 2 EnableSlot -> slot 2\", \"[xhci] port 3 EnableSlot -> slot 3\")",
    "evidencePath": "/Users/wrb/fun/code/breenix/logs/parallels-launcher-test/run-20260704-165209/"
  },
  {
    "boot": 2,
    "result": "PASS",
    "mouseEnumerated": true,
    "mouseEvidence": "[smoke 16:55:57] USB mouse enumerated: slot 1 ([xhci] start_hid_polling: kbd=slot2/dci3 nkro=dci5 mouse=slot1/dci3 mouse2=dci5)",
    "evidencePath": "/private/tmp/claude-501/-Users-wrb-fun-code-breenix/94deec68-57ce-4666-8cb5-544f45bb0f62/scratchpad/launcher-smoke-boot2.log"
  },
  {
    "boot": 3,
    "result": "PASS",
    "mouseEnumerated": true,
    "mouseEvidence": "[smoke 16:59:02] USB mouse enumerated: slot 1 ([xhci] start_hid_polling: kbd=slot2/dci3 nkro=dci5 mouse=slot1/dci3 mouse2=dci5)",
    "evidencePath": "/private/tmp/claude-501/-Users-wrb-fun-code-breenix/94deec68-57ce-4666-8cb5-544f45bb0f62/scratchpad/smoke-run3.log"
  }
]

Note boot 1 had no retry lines at all (clean first-attempt enumeration on every port) — the harness assertion (mouse != slot0) holds regardless of whether the retry path was actually exercised in a given boot, which is the correct behavior for a bounded-retry safety net.

🤖 Generated with Claude Code

ryanbreen and others added 2 commits July 4, 2026 16:46
… timeout no longer kills a port (mouse dead all session)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…in smoke runs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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