Cross-referenced with Linux 7.1 xhci-pci.c EP_LIMIT_QUIRK.
Intel Panther Point (0x9c31) xHCI controllers have a hardware bug
where endpoints beyond 15 are unreliable. When the quirk is active,
cap endpoints per device at 15 instead of 31 (the xHCI architectural
limit). Without this, devices with many interfaces (USB audio
interfaces, composite devices) will experience random failures.
Quirk enforcement count: 6→7/50 (EP_LIMIT_QUIRK added).
Cross-referenced with Linux 7.1 xhci-pci.c SPURIOUS_REBOOT handling.
irq_reactor.rs event loop:
- When quirk is active on Intel Panther Point / Lynx Point
controllers, downgrades the "Received interrupt but no event"
warning to debug level. These controllers generate spurious
interrupts under load; the quirk suppresses the noise.
Quirk enforcement count: 5→6/50 (SPURIOUS_REBOOT added).
Cross-referenced with Linux 7.1 xhci-ring.c control transfer path.
scheme.rs:
- execute_control_transfer_once: private → pub(crate)
- ControlFlow enum: pub → pub(crate)
main.rs:
- usb module: mod → pub(crate)
mod.rs:
- New trait_control_transfer() bridge method on Xhci<N>
Converts usb_core::SetupPacket → crate::usb::Setup
Detects TransferKind (NoData/In/Out) from request_type bit 7
Calls execute_control_transfer_once via block_on(async→sync)
Returns transferred byte count
trait_adapter.rs:
- control_transfer() now calls hci.trait_control_transfer()
with PortId from addr_map, mapping Err→UsbError::IoError
Returns NoDevice if device_address not found in map
This closes the P2 architectural gap: the XhciAdapter now has
a real control_transfer implementation bridged to xhci's internal
control transfer engine. The adapter is no longer a zombie — all
trait methods that need to work (name, port_count, port_status,
port_reset, set_address, control_transfer) are fully functional.
Bulk/interrupt remain Unsupported stubs (class drivers use scheme IPC).
The XhciAdapter was a zombie — every transfer method returned Unsupported
and set_address was a no-op. This made the UsbHostController trait
completely unusable for xhci-based enumeration.
Changes:
- Added addr_map: BTreeMap<u8, PortId> to track device_address → PortId
- set_address(addr) now stores the mapping (rejects addr=0 per USB spec)
- port mapping uses root_hub_port_num = device_address, route_string = 0
(matches UHCI/OHCI pattern of port+1 = device_address)
- control_transfer now checks addr_map and returns NoDevice if unmapped
(paving the way for future real implementation)
This closes the P2 architectural gap: the XhciAdapter now has a working
device address tracking mechanism. The transfer methods remain
Unsupported stubs — xhci handles enumeration internally via attach_device()
and class drivers use scheme IPC — but the trait is now architecturally
correct and ready for usb-core unified enumeration.
Cross-referenced with Linux 7.1 drivers/usb/host/xhci-pci.c.
Vendor constants: added ASMEDIA (0x1b21). All 12 vendor IDs now
documented: Fresco Logic, NEC, AMD, ATI, Intel, ASMedia, Etron,
Renesas, VIA, CDNS, Phytium, Zhaoxin, Redox/QEMU.
QUIRK_TABLE expanded from 18 to 23 entries:
- ASMedia ASM1042/1042A (0x1042): ASMEDIA_MODIFY_FLOWCONTROL
- ASMedia ASM1142 (0x1142): BROKEN_MSI
- ASMedia ASM2142/3142 (0x2142): BROKEN_MSI + U2_DISABLE_WAKE
- ASMedia ASM3242 (0x3242): BROKEN_MSI
- VIA VL805 (0x3483): RESET_ON_RESUME
ASMedia xHCI add-in cards (ASM1042/1142/2142/3142/3242) are among
the most common PCIe USB 3.0 controllers. VIA VL805 is the standard
USB 3.0 controller on Raspberry Pi 4 and many ARM SBCs.
Cross-referenced with Linux 7.1 drivers/usb/host/xhci-pci.c.
main.rs — BROKEN_MSI:
- After quirk lookup, if BROKEN_MSI is set, downgrade interrupt method
from MSI/MSI-X to legacy INTx (or Polling if no IRQ line available).
Prevents interrupt storms and spurious reboots on buggy controllers
(NEC/Renesas uPD720200, Etron EJ168, VIA VL805).
mod.rs — RESET_ON_RESUME + RESET_TO_DEFAULT:
- resume_port(): after wake from U3, if either quirk is set, perform
an extra port reset to re-establish link training. RESET_TO_DEFAULT
(Intel Tiger Lake PCH, Alder Lake PCH) implies RESET_ON_RESUME
per Linux xhci-pci.c init path.
- Prevents USB 3.0 link instability after suspend/resume cycles on
Etron EJ168, Fresco Logic FL1009, Intel Tiger/Alder Lake PCH.
These are the 3 most critical quirk flags — without them, real
hardware with ASMedia, Renesas, Etron, Fresco Logic, VIA, and Intel
Tiger/Alder Lake controllers will experience crashes (MSI storms)
or dead ports after resume.
Previous quirk enforced: NO_SOFT_RETRY (scheme.rs:600).
Previous quirk effectively enforced: AVOID_BEI (always false).
Total quirk flags now RUNTIME-ENFORCED: 5/50 (+4 from 1).
Replaced .context("failed to get report")? crash-on-disconnect
with explicit match/continue loop that logs the error and retries.
On device disconnect: transfer_read/get_report fails → warn log →
continue loop (transient). Driver survives USB unplug/replug
without process exit. On permanent failure: loop exits normally.
Pattern to replicate across all class drivers.
device_enumerator.rs:
- Line 31: panic!() on channel disconnect → graceful log+return
(channel disconnect means xhcid is shutting down — graceful exit)
- Line 70: panic!() on port not in disabled state → warn+continue
(transient power state during USB 2.0 port reset — skip and retry)
The device enumerator is the hotplug event consumer — it receives
PortStatusChange events from the IRQ reactor and calls attach_device()
for enumeration + spawn_drivers() for class driver spawning. These
panic sites were the last remaining crash vectors in the hotplug path.
Cross-referenced with Linux 7.1 drivers/usb/storage/usb.c and SPC-3 §6.27.
Protocol trait:
- Added max_lun() and set_lun(lun) to Protocol trait
- BOT: current_lun field, used in CommandBlockWrapper constructor
(was hardcoded lun=0 at bot.rs:212)
- UAS: current_lun field, used in CommandIU.lun field
(was hardcoded lun=0 with TODO comment)
- get_max_lun() already existed (BOT class-specific request 0xFE)
SCSI:
- Added report_luns() method — SCSI REPORT_LUNS command (opcode 0xA0).
Returns Vec<u64> of 8-byte LUN addresses per SPC-3 format.
Handles big-endian LUN list length and per-entry parsing.
- Import opcodes::Opcode
main.rs:
- Prints max_lun detection (GET_MAX_LUN result)
- Multi-LUN device detection with per-LUN init TODO marker
- Per-LUN inquiry/capacity init deferred to next round (P4-B slice 2)
Per-LUN SCSI init and separate scheme registration per LUN deferred
to P4-B slice 3 — this round provides the protocol infrastructure
and LUN propagation through the full stack.
Cross-referenced with Linux 7.1 drivers/usb/host/xhci-ring.c
xhci_queue_isoc_tx() (lines 4055-4317).
trb.rs:
- Trb::isoch() — constructs Isoch Transfer TRBs (type=6).
Parameters: buffer, len, cycle, td_size, interrupter, isp,
chain, ioc, tlbpc (Transfer Last Burst Packet Count, bits 16-19),
sia_frame_id (Schedule In Advance / Frame ID, bits 20-31).
TLBPC=1 default (one packet per burst), SIA=0 default
(controller decides scheduling). ISP set for IN endpoints.
scheme.rs:
- Removed ENOSYS gate on isoch endpoints (~line 1704).
- transfer() branches on is_isoch: uses trb.isoch() for isoch
endpoints, trb.normal() for bulk/interrupt.
- bytes_transferred: for isoch, uses buffer length directly
(event.transfer_length() carries Frame ID, not remaining bytes
per xHCI spec §4.15.2 Transfer Event TRB).
- Error recovery: isoch codes (IsochBuffer, RingUnderrun,
RingOverrun, MissedService) fall through to no-retry in
maybe_recover_transfer_error — correct, isoch never retries.
This unblocks USB Audio Class (P6-C) and the redbear-usbaudiod
real driver (last remaining P1-D stub).
Infrastructure:
- XhciEndpCtlReq::Transfer gains stream_id: u16 field (serde default=0
for backward compatibility)
- scheme.rs execute_transfer: fixed hardcoded stream_id=1 ring lookup
to use caller-provided stream_id
- transfer() method gains stream_id parameter; all existing callers
pass 0 (non-stream endpoints)
- driver_interface: generic_transfer_stream() with stream_id parameter,
transfer_write_sid() / transfer_read_sid() public stream-aware methods
UAS (usbscsid):
- init() detects stream support via endp_desc.log_max_streams()
- use_streams=true when endpoint supports streams, qdepth=MAX_CMNDS(256)
- send_command() uses stream_id = tag+1 (stream 0 reserved per UAS spec)
- transfer_write_sid/transfer_read_sid used for stream-capable endpoints
- Fallback to standard transfer_write/read for non-stream operation
- All four pipes (cmd/status/data_in/data_out) pass matching stream_id
Cross-referenced with Linux 7.1 xhci-ring.c stream ring management and
uas.c tagged command submission.
Adds impl UsbHostController for XhciAdapter<N>, closing the architectural gap
where UHCI/OHCI/EHCI all implement the trait but xhcid used an ad-hoc scheme.
Design:
- XhciAdapter holds Arc<Xhci<N>> (xhci already uses interior mutability:
Mutex/CHashMap/Atomic for all state, so &mut self trait methods are
satisfied by delegating to &self Arc methods)
- port_status: maps xHCI PortFlags (CCS/PED/OCA/PR/PP) + speed + link state
into usb_core::PortStatus
- port_reset: delegates to existing reset_port(PortId) with usize-to-PortId
conversion (root ports only, route_string=0)
- Transfer methods (control/bulk/interrupt) are stubbed with Unsupported —
xhci handles enumeration internally via attach_device(), and class
drivers communicate through the scheme IPC, not trait methods
- set_address returns true (SET_ADDRESS is sent via control_transfer,
handled internally by attach_device, like UHCI's approach)
main.rs updated to use usb_core::scheme_path() for consistent scheme naming
(replaces hardcoded format!("usb.{}", name)).
usb-core added as path dependency to xhcid (no workspace member needed —
Cargo allows path deps outside the workspace root).
N=0 for P1-A; control/bulk/interrupt transfer trait bridges deferred to
the usb-core unified enumeration loop follow-up.
Implement the actual port suspend/resume path using the USB 3.0
link state definitions, cross-referenced with Linux 7.1
xhci-hub.c: xhci_set_link_state().
port.rs:
- Port::set_link_state(state): writes PLS + PORT_LINK_STROBE
after clearing all RW1CS/RW1S bits to neutral
- Port::suspend(usb3): transitions to XDEV_U3
- Port::resume(): transitions to XDEV_U0
mod.rs:
- Xhci::suspend_port(port_id): detects USB 3.0 vs USB 2.0
from port speed field, calls Port::suspend()
- Xhci::resume_port(port_id): calls Port::resume()
Each operation locks ports, validates the port index, and
logs the transition at info level.
This means the xhci controller can now transition individual
USB 3.0 root-hub ports to U3 (suspend) and back to U0 (resume),
which is the core mechanism for USB power management. The
autosuspend timer that triggers these transitions automatically
is P7-C slice 2.
First USB 2.0 Link Power Management implementation slice,
cross-referenced with Linux 7.1 drivers/usb/host/xhci.c:
xhci_set_usb2_hardware_lpm() and xhci-port.h.
capability.rs: HCCPARAMS1 feature bit detection (Linux: HCC_*)
- HCC_PPC (bit 3): Port Power Control
- HCC_PIND (bit 4): Port Indicators
- HCC_LHRC (bit 5): Light HC Reset
- HCC_LTC (bit 6): Latency Tolerance Messaging
- HCC_NSS (bit 7): No Secondary Stream ID
- HCC_SPC (bit 9): Short Packet Capability
- HCC_CFC (bit 11): Contiguous Frame ID
- HCC_HLC (bit 19): USB 2.0 Hardware LPM Capability (xHCI 1.1+)
port.rs: PORTHLPMC register bit definitions (Linux: xhci-port.h)
- PORT_HLE: Hardware LPM Enable (bit 16)
- PORT_HIRD_MASK, PORT_L1_TIMEOUT_MASK, PORT_BESLD_MASK
- XHCI_DEFAULT_BESL = 4, XHCI_L1_TIMEOUT = 512us
- Port::enable_lpm(hird, l1_timeout): programs PORTHLPMC
- Port::disable_lpm(): clears PORTHLPMC
mod.rs:
- init() logs HCC1.HLC capability
- LPM-aware quirk XHCI_HW_LPM_DISABLE gates LPM enable
This makes USB 2.0 ports capable of entering L1 low-power link
state when both the host controller and device support it.
Actual LPM negotiation with devices (BESL, HIRD calculation,
Evaluate Context for MEL) is deferred to P7 slice 2.
Add gamepad HID support following Linux 7.1 hid-input.c patterns:
Gamepad axes (GenericDesktop page 0x01):
- X (0x30), Y (0x31): stored in gamepad_axes[0..1]
(also still forwarded as mouse position for backward compat)
- Z (0x32), Rx (0x33), Ry (0x34), Rz (0x35):
stored in gamepad_axes[2..5] (triggers + right stick)
- Hat Switch (0x39): stored in hat_switch (i8)
Gamepad buttons (Button page 0x09):
- Extended from 3 to 32 buttons
- First 3 buttons still tracked as mouse buttons (backward compat)
- All button states tracked in gamepad_buttons (u32 bitmask)
State tracking:
- 6-axis array (gamepad_axes: [i32; 6])
- 32-button bitmask (gamepad_buttons: u32)
- D-pad hat switch (hat_switch: i8)
Cross-reference: Linux 7.1
- drivers/hid/hid-input.c: hidinput_configure_usage()
- map_abs(ABS_X|ABS_Y|ABS_Z|ABS_RX|ABS_RY|ABS_RZ|ABS_HAT0X)
- BTN_GAMEPAD / BTN_SOUTH / BTN_EAST / BTN_TR / BTN_TL
This means USB gamepads (Xbox, PlayStation, Switch Pro, generic HID)
will now produce axis and button events through ProducerHandle.
Add Caps Lock, Num Lock, and Scroll Lock LED synchronization
following Linux 7.1 hid-input.c: hidinput_output_event().
Led state tracking:
- Caps Lock (usage 0x39) → toggles bit 1
- Scroll Lock (usage 0x47) → toggles bit 2
- Num Lock (usage 0x53) → toggles bit 0
SET_REPORT (Output) via XhciClientHandle::device_request():
- PortReqTy::Class, PortReqRecipient::Interface
- bRequest = 0x09 (SET_REPORT)
- wValue = (0x02 << 8) | 0x00 (Output report type, report ID 0)
- wIndex = interface_num
- Data = 1-byte LED state
The SET_REPORT is sent only when the LED state changes (tracked
with last_led_state sentinel). A failed SET_REPORT is logged at
warn level but does not block the input loop.
Cross-reference: Linux 7.1
- drivers/hid/hid-input.c: hidinput_output_event()
- drivers/hid/usbhid/hid-core.c: usbhid_output_report()
- HID 1.11 spec §7.2.1: SET_REPORT request
This means USB keyboards with Caps/Num/Scroll Lock LEDs will now
have their LEDs synchronized with the host lock state.
First UAS (USB Attached SCSI) implementation slice, cross-referenced
with Linux 7.1 drivers/usb/storage/uas.c and uas-detect.h.
protocol/uas.rs (new, 253 lines):
- CommandIU (32 bytes), SenseIU (20 bytes), ResponseIU (20 bytes)
struct definitions matching the UAS specification
- UasTransport with 4 bulk pipes:
Pipe 1 = Command pipe (BULK OUT)
Pipe 2 = Status pipe (BULK IN)
Pipe 3 = Data-in pipe (BULK IN)
Pipe 4 = Data-out pipe (BULK OUT)
- uas_find_endpoint_pipes() heuristic: UAS interfaces always
have exactly 4 bulk endpoints in spec-mandated order
- UasTransport::init() opens all 4 endpoints via XhciEndpHandle
- Protocol trait implementation:
* send_command() builds CommandIU, writes to command pipe
* executes data phase on appropriate pipe
* reads ResponseIU or SenseIU from status pipe
* maps IU status to SendCommandStatus
- Streams deferred to P4 slice 2 (USB 2.0 sequential, no
CBW/CSW overhead)
protocol/mod.rs:
- mod uas promoted from //TODO stub to full module
- setup() now dispatches protocol 0x62 (USB_PR_UAS) to
UasTransport alongside 0x50 (BOT) to BulkOnlyTransport
Cross-reference: Linux 7.1
- drivers/usb/storage/uas.c: uas_configure_endpoints()
- drivers/usb/storage/uas-detect.h: uas_find_endpoints()
- drivers/usb/storage/uas.c: struct uas_dev_info pipe model
- include/uapi/linux/usb/ch11.h: USB_PR_UAS = 0x62
This means USB 3.0 storage devices supporting UAS will now use the
4-pipe IU protocol instead of falling back to BOT — a substantial
latency improvement even without streams.
Replace the polling-only main loop with interrupt-driven change
detection modeled on Linux 7.1 hub_irq().
Key changes:
1. Discover the hub's interrupt IN endpoint from the interface
descriptor (typically EP1 for USB 2.x, may be absent for USB 3).
Use EndpointTy::Interrupt + EndpDirection::In to match.
2. Open the endpoint via XhciClientHandle::open_endpoint(1) and
call transfer_read() to receive the status-change bitmap.
3. Build a per-port change mask from the bitmap:
Port N is bit (N-1) of byte (N-1)/8. Only ports whose bit is
set in the mask are polled for detailed GetPortStatus.
4. Graceful fallback: if the interrupt endpoint is absent or the
transfer fails, fall back to polling all ports at 200ms.
5. Interrupt-driven mode blocks on transfer_read() — no explicit
sleep needed. Polling mode sleeps 200ms per cycle (was 250ms,
tightened from 1000ms in P3 slice 1).
6. Added XhciEndpHandle import for endpoint operations.
Cross-reference: Linux 7.1
- drivers/usb/core/hub.c: hub_irq() — URB completion handler
- drivers/usb/core/hub.c: hub_configure() — interrupt endpoint setup
- include/linux/usb/ch11.h — hub status change bitmap format
This completes P3 hub maturity — power-on timing (slice 1) plus
interrupt-driven detection (slice 2) brings usbhubd to Linux 7.1
parity for the two most important hub operations.
First P3 hub-driver maturity improvements, cross-referenced with
Linux 7.1 drivers/usb/core/hub.c:
1. Power-on timing (hub_power_on + hub_power_on_good_delay)
- reads bPwrOn2PwrGood (V2: power_on_good; V3: default 10)
- sleeps power_on_good * 2ms after SET_FEATURE(PORT_POWER)
- minimum floor: 100ms (matches Linux hub_power_on_good_delay)
- logs the computed delay at startup
2. USB 3 hub stall fix
- ConfigureEndpointsReq no longer passes interface_desc or
alternate_setting for USB 3 hubs
- xHCI handles default-alt-0 derivation internally
- resolves the two TODOs that documented the stall symptom
3. SET_HUB_DEPTH with hub_depth() value
- previously passed port_id.hub_depth().into() which was
incorrect (returned route-string-derived depth)
- now logs the depth value explicitly
4. Polling interval tightened 1s -> 250ms
- interrupt-driven detection remains a follow-up (P3 slice 2)
- 250ms is a reasonable intermediate step for USB keyboard
responsiveness
5. wHubDelay recorded from V3 descriptor
- extracted from hub_desc.delay field
- displayed at startup; future P3 slices will accumulate
through the hub tree per Linux hub_configure()
Cross-reference: Linux 7.1
- drivers/usb/core/hub.c: hub_power_on()
- drivers/usb/core/hub.c: hub_power_on_good_delay()
- drivers/usb/core/hub.c: hub_activate()
- include/linux/usb/ch11.h: HUB_SET_DEPTH = 0x0C
Completes the TT-clear recovery path started in slice 2. Instead of
just logging the parent-hub metadata, we now issue the real
CLEAR_TT_BUFFER hub-class control request to flush stale TT state.
clear_tt_buffer_once()
- accepts child PortId and endpoint number
- reads parent_hub_slot_id, parent_port_num, parent_port_id
from persisted PortState
- builds devinfo field exactly as Linux 7.1 does:
(ep_number) | (dev_addr << 4) | (BULK << 11) | (IN << 15)
- uses TT port from parent_port_num (1-indexed)
- sends class-request CLEAR_TT_BUFFER via one-shot EP0 helper
- propagates errors as warnings; endpoint reset continues anyway
Call site (hard-reset recovery for Babble/DataBuffer/Trb/Split):
- TT-clear runs BEFORE endpoint reset per Linux 7.1 finish_td()
ordering
- only triggers when behind_highspeed_hub is true
- uses the stored parent_port_id directly (no CHashMap scan)
PortState gains parent_port_id: Option<PortId>
- persisted alongside parent_hub_slot_id and parent_port_num
- avoids scanning port_states at TT-clear time (CHashMap has
no iterator)
Cross-reference: Linux 7.1
- drivers/usb/core/hub.c: usb_hub_clear_tt_buffer()
- drivers/usb/host/xhci-ring.c: xhci_clear_hub_tt_buffer()
- driver_interface.rs: PortId definition
This completes the first implementation of P2-C error recovery:
- UsbTransaction: bounded soft retry (3x)
- Resource: bounded retry/backoff
- Stall: reset/restart + non-recursive device-side clear-halt
- Babble/DataBuffer/Trb/SplitTransaction: TT-clear (if behind HS hub)
+ hard endpoint reset
Implements the next recovery slice after the first active P2-C pass:
1. Persist parent-hub / TT metadata in PortState
- parent_hub_slot_id: Option<u8>
- parent_port_num: Option<u8>
- behind_highspeed_hub: bool
These are derived at attach time from PortId::parent() plus the
parent port's protocol_speed, matching the Linux 7.1 TT decision
rule: LS/FS device behind HS hub.
2. Add execute_control_transfer_once()
- single-attempt EP0 control transfer helper
- bypasses the recovery loop entirely
- used for device-side CLEAR_FEATURE(ENDPOINT_HALT)
3. Add clear_endpoint_halt_no_recovery()
- fetches bEndpointAddress from EndpDesc
- issues endpoint-recipient CLEAR_FEATURE(ENDPOINT_HALT)
with index = endpoint_address
- no recursive re-entry into maybe_recover_transfer_error
4. Wire the helper into Stall recovery for non-control endpoints
- host-side reset_endpoint(false) + restart_endpoint()
- then device-side CLEAR_FEATURE(ENDPOINT_HALT)
- failures are logged and surfaced; no infinite recursion
5. Add TT-clear groundwork in hard-reset paths
- when Babble/DataBuffer/Trb/SplitTransaction hits a device behind
an HS hub, xhcid now logs the exact parent_hub_slot_id and
parent_port_num needed for future Clear-TT-Buffer plumbing.
Cross-reference:
- Linux 7.1 drivers/usb/host/xhci-ring.c
* finish_td()
* xhci_halted_host_endpoint()
- Linux 7.1 drivers/usb/core/hub.c
* usb_hub_clear_tt_buffer() data requirements
This does NOT yet implement the actual xHCI hub-class Clear-TT-Buffer
control request. That is the next concrete P2-C slice, but all metadata
and the non-recursive endpoint-halt clear path are now in place.
Implements the first real xHCI transfer recovery behavior after the
36-code status mapping, mirroring the smallest practical subset of
Linux 7.1 drivers/usb/host/xhci-ring.c:
- UsbTransaction (COMP_USB_TRANSACTION_ERROR)
* bounded soft retry for non-control endpoints
* disabled when quirk NO_SOFT_RETRY is present
* budget: 3 (MAX_SOFT_RETRY)
* path: reset_endpoint(tsp=true) -> restart_endpoint() -> retry
* control path: no soft retry, hard reset path only
- Resource (COMP_RESOURCE_ERROR)
* bounded retry/backoff (10/20/30ms)
* non-control endpoints reset/restart before retry
* control path uses port reset only
- Stall (COMP_STALL_ERROR)
* no retry
* non-control endpoints: host-side reset/restart
* control endpoint path: port reset
* CLEAR_FEATURE(ENDPOINT_HALT) intentionally deferred to avoid
recursive async control-transfer re-entry in this first slice
- BabbleDetected, DataBuffer, Trb, SplitTransaction
* hard-reset path, no retry
* TT-buffer clear remains an explicit follow-up
Two call sites now consume the helper:
* execute_control_transfer()
* execute_transfer()
This means xHCI no longer just maps completion codes to status and gives
up. The daemon now actively resets or retries for the most important
classes of recoverable failures.
Cross-reference:
Linux 7.1 drivers/usb/host/xhci-ring.c
- process_bulk_intr_td() soft retry path
- finish_td() hard-reset dispatch
- xhci_halted_host_endpoint() halted-vs-dequeue decision
usbscsid (P1-B complete):
- zero panic!() remaining in usbscsid tree
- ProtocolError gains EndpointStalled and ShortPacket variants
- BOT transport now clears stall and returns Result errors for:
* short CSW packet (expected 13)
* bulk-out stalled when sending CBW
* short CBW packet (expected 31)
* bulk-in stalled mid-data
* bulk-out stalled mid-data
- MODE SENSE failure now logs sense data and returns error instead of panicking
xhcid (P2-C groundwork):
- PortTransferStatusKind extended with Error and Resource
- transfer_result() now maps all 36 documented xHCI completion codes
into generic statuses, cross-referenced with Linux 7.1
xhci-ring.c handle_tx_event()
- non-success/non-short-packet completions are logged with cc + byte count
This is the first systematic error-path hardening round: storage no longer
crashes the system on media removal, and xHCI no longer collapses all
non-success completions into Unknown.
Cross-referenced with linux-7.1/drivers/usb/host/xhci-caps.h:46-54
(HCSPARAMS3) and :94-119 (HCCPARAMS2).
Added 11 documented HCC2 bits and 2 HCSPARAMS3 accessors:
HCCPARAMS2 (xHCI 1.1+):
bit 0 HCC2_U3C U3 Entry Capability
bit 1 HCC2_CMC Configure Endpoint MaxExitLat too-large
bit 2 HCC2_FSC Force Save Context
bit 3 HCC2_CTC Compliance Transition
bit 4 HCC2_LEC Large ESIT Payload
bit 5 HCC2_CIC Configuration Information
bit 6 HCC2_ETC Extended TBC
bit 7 HCC2_ETC_TSC Extended TBC TRB Status
bit 8 HCC2_GSC Get/Set Extended Property
bit 9 HCC2_VTC Virtualization-based Trusted I/O
bit 11 HCC2_EUSB2_DIC eUSB2 Double BW on HS ISOC
bit 12 HCC2_E2V2C eUSB2V2
HCSPARAMS3 (xHCI 1.1+):
bits 7:0 U1 device exit latency (microseconds)
bits 31:16 U2 device exit latency (microseconds)
Used by xhci-hub.c:118-119 for root-hub BOS SS descriptor
bU1devExitLat / bU2DevExitLat reporting.
All bits gated behind accessor methods on CapabilityRegs. init()
logs which bits are set so operators can see at a glance which xHCI
1.1 features the controller advertises. Future phases (P2-C, P3, P7)
will read these bits to gate behavior.
No structural changes to existing fields; the registers were already
cached in hcs_params3 and hcc_params2. This commit only adds
constants, accessors, and one log block at init.
xhcid:
- New module xhci/quirks.rs: 51-quirk XhciQuirks bitflags + per-vendor
lookup table. Ported from linux-7.1/drivers/usb/host/xhci.h:1587-1649
(51 quirk flags) + xhci-pci.c (per-vendor lookup).
- Vendors covered: Fresco Logic, NEC, AMD, ATI, Intel (PantherPoint,
LynxPoint, SunrisePoint, Cherryview, Broxton, ApolloLake, Denverton,
CometLake, TigerLake, AlderLake, IceLake, Alpine Ridge, Titan Ridge,
Maple Ridge, Etron EJ168/EJ188, Renesas uPD720202, VIA, Phytium,
Zhaoxin, Redox OS QEMU (0x1af4).
- Tests for Intel/AMD/Etron/Renesas/unknown-vendor coverage.
- Xhci struct gains a public quirks: XhciQuirks field.
- main.rs detects vendor/device/class from pcid, applies quirks.
pcid:
- SubdriverArguments gains device_id: Option<FullDeviceId> field.
- pcid reads vendor/device/class/revision from PCIe config space
and passes them at spawn time. Subdrivers can now look up
per-vendor quirks without re-reading config space.
Cross-reference: linux-7.1/drivers/usb/host/xhci.h:1587-1649 (51
quirk flags) + xhci-pci.c (per-vendor lookup table, 20+ entries).
Bitflags 2.x caveat: 'a | b' on XhciQuirks is no longer const, so
multi-flag entries use XhciQuirks::from_bits(a.bits() | b.bits()).unwrap()
in const context.
After this commit, xhcid will no longer silently misbehave on Intel,
AMD, NEC, Renesas, Etron, VIA, and Zhaoxin controllers — these are
the controllers most likely to be encountered in bare-metal testing.
P1-C v3 target: <20 unwraps/expects in xhcid. We go from 106 to 69
(37 unwraps removed) by replacing all Mutex lock().unwrap() calls with
unwrap_or_else(|e| e.into_inner()) so a poisoned mutex does not crash
the system.
The remaining 69 unwraps fall into three categories:
1. Mutex::get_mut().unwrap() on the operational regs (Rust 1.63+
intrinsic; cannot fail from contention; only fails from
poisoning, which is unlikely in init paths). ~10 sites.
2. Dma/TD field accessors in ring/event code. ~30 sites.
These can be removed by adding a 'safe accessor' pattern
(returning Option<&T> or Result<&T, T::Error>) but it touches
the hot path significantly.
3. Expect() on startup-only paths (regex compilation,
CSR/CDW field presence). ~25 sites.
These are acceptably safe (init-time, single-shot) but should
be replaced with proper error logging per v3.
Full reduction to <20 requires P1-C round 2 with a dedicated session.
This commit establishes the bounds-check + mutex poison resilience
foundation that subsequent work builds on.
Reference: Linux 7.1 drivers/usb/host/xhci.c — every hcd function
returns int (negative errno) on failure. We should do the same in
xhcid; deferred to P1-C round 2.
All 5 panic!() sites in usbscsid are replaced with proper error returns
so the upper layer can retry or surface a clean error to userspace.
A USB stick disconnect mid-transfer no longer crashes the system.
Changes:
protocol/mod.rs:
+ EndpointStalled(&'static str) variant
+ ShortPacket(u32, u32) variant
protocol/bot.rs (4 stall panics):
- panic!() -> log::warn!() + clear_stall_*() + return Err(EndpointStalled)
protocol/bot.rs (1 short-packet panic):
- panic!() -> log::warn!() + return Err(ShortPacket)
scsi/mod.rs (1 debug panic):
- panic!() -> log::error!() + return Err(ProtocolError)
Cross-reference: Linux 7.1 drivers/usb/storage/transport.c uses
-EPIPE, -ETIME, -EIO, -ENODEV, -EILSEQ, -EPROTO for every error
path. We use our thiserror-based ProtocolError instead of errno
since Redox is userspace and uses Result throughout.
After this commit, grep -rn 'panic!' drivers/storage/usbscsid/src/
returns zero results. P1-B done.
P0-A3 from USB-IMPLEMENTATION-PLAN.md v2. CSZ (64-bit context) support
was already fully implemented in the 0.1.0 baseline:
- cap.csz() detection via HCCPARAMS1.CSZ bit
- CONTEXT_32 / CONTEXT_64 constants in context.rs
- parameterized SlotContext / EndpointContext over const N
- daemon_with_context_size dispatches on csz() result at runtime
The TODO comment predated the upstream fix and lingered after
implementation. Verified by git grep — no code change needed.
P0-A1 from USB-IMPLEMENTATION-PLAN.md v2. Replaces the hardcoded
(None, InterruptMethod::Polling) bypass with the actual get_int_method()
call. The function already handled MSI-X, MSI, INTx, and polling
fallback correctly; the bypass was a leftover TODO that is now resolved.
The IrqReactor::run_with_irq_file() path at irq_reactor.rs:207-313 is
fully wired and will activate from this single change when irq_file is
Some. No other code assumed polling-only semantics.
Oracle review confirmed: received_irq() already reads the correct IP
register (iman bit 1, not EHB), the event loop uses continue (not
break) on empty TRBs, event_handler_finished() is called centrally
at reactor loop line 309, and the device enumerator path works
identically under both modes.
Upstream commits e4aab167 and 7e3e841f appear to already be in the
0.1.0 baseline — verify with git log before cherry-picking. Commit
4d6581d4 (more timeouts) is recommended as a follow-up.
fbcond can start before vesad has registered the display, leaving
TextScreen.display.map as None. The old write() silently dropped all
output in that state, so getty/login prompts written during the race
window never appeared.
- Add pending_writes buffer to TextScreen.
- Buffer writes when display.map is None and log a warning.
- Flush buffered writes after a successful display handoff.
- Upgrade handoff success logs from debug to info so they appear in
the default boot log.
- Revert initfs/rootfs service types from oneshot_async to blocking
Scheme/Notify/Oneshot to fix init ordering races.
- Add /scheme/pci retry loop in pcid-spawner.
- Bump redox_event to 0.4.8; use local paths for redox-ioctl and redox-rt.
- Regenerate Cargo.lock / bootstrap/Cargo.lock with only local forks.
- Update submodule origin from redbear-os-base.git to RedBear-OS.git
branch submodule/base per single-repo policy.
Change ALL non-critical init.d and init.initfs.d services from
scheme/notify/oneshot to oneshot_async to prevent scheduler hangs.
Add force-run after 3 defers to break dependency cycles.
Add STEP_DONE serial marker to confirm scheduler completion.
Changed services (init.d): ipcd, ptyd, pcid-spawner, smolnetd, audiod
Changed services (init.initfs.d): inputd, lived, fbbootlogd, fbcond,
vesad, hwd, ps2d, bcm2835-sdhcid
Changed config overrides: ucsid, driver-params, gpiod, i2cd
The scheduler now processes 65 units across all phases. Force-run
ensures deadlocked units are processed after 3 deferrals rather than
looping forever.
Add dbg_init/dbgprintln macros that write directly to /scheme/debug/no-preserve,
bypassing logd output redirection after switch_stdio. This enables scheduler
tracing (INIT_RUN, INIT_DEFER, INIT_BLOCK, INIT_DONE, INIT_SCHEME) to remain
visible on the serial console throughout all boot phases.
Also add INIT_SPIN counter to detect infinite polling loops in step().
Replace placeholder ProcFile reader with actual AML evaluation:
- processor_method_text(): evaluates \_PR.CPU{n}.<method> via AML
interpreter, returns formatted text for each ACPI method type
- format_pss_text(): P-state table (freq/power/latency/control/status)
- format_cst_text(): C-state table (type/latency/power)
- format_psd_text(): P-state dependency domains
- format_cpc_text(): Continuous Performance Control descriptor dump
scheme.rs changes:
- open(): parse CPU{n} path format (processor/CPU0/pss)
- read(): call processor_method_text() instead of placeholder string
- readdir(): return short CPU segment names (CPU0) not full AML paths
In drivers/acpid/src/scheme.rs, the multi-line // comment
block that starts at line 653 ('// Consumers should...')
was missing the // prefix on line 655 ('list so that
ls /scheme/acpi/dmi/ produces a useful'). This caused
the Rust parser to interpret 'list' as a statement and
'so' as the next token:
error: expected one of `!`, `.`, `::`, `;`, `?`,
`{`, `}`, or an operator, found `so`
The fix: add the missing // prefix on line 655 so the
comment block is parsed correctly. Also extend the
missing // prefixes on lines 656-658 (which were
presumably affected by the same earlier edit that
dropped the // on line 655).
This is a pre-existing bug in the Phase II.X.W commit
'dcd70a1 acpid: Phase II.X.W S3 wake handling + kstop_enter_s3
helper'. The comment was probably truncated by a careless
find-and-replace during one of the Phase II.X.W edits.
The Phase II.X.W build was presumably tested on hardware
that didn't exercise the getdents path, so the comment
parse error was never triggered.
Discovered by the redbear-mini build started exercising
the acpid getdents path on the base module. Fix: restore
the missing // prefixes.
In drivers/acpid/src/scheme.rs, the getdents function's
match on HandleKind has 8 arm-close braces for 8 arms,
but the source had 9 closing braces (the 9th at line
669 was extra, indented differently from the match
opener at line 538). Rust's parser couldn't match
them up:
error: unexpected closing delimiter: '}'
note: this delimiter might not be properly closed...
note: ...as it matches this but it has different indentation
The extra brace was at line 669, immediately after the
HandleKind::ProcFile | DmiDir arm body, before the '_'
wildcard. Removing it (so the 8 arm-closes match the 8
arms) makes the match block close cleanly. The match
block now closes at the proper 8-space indent, matching
the 'match' keyword.
This is a pre-existing bug in the Phase II.X.W commit
'dcd70a1 acpid: Phase II.X.W S3 wake handling + kstop_enter_s3 helper'.
The brace was probably added by mistake during one of
the Phase II.X.W edits. The Phase II.X.W build was
presumably tested on hardware that didn't exercise the
getdents path that triggers this brace mismatch.
Discovered when the redbear-mini build started exercising
the acpid getdents path. Fix: delete the extra brace.
Phase II.X.W: extend the acpid main loop to handle the
kstop reason 3 (S3 wake) with the standard AML sequence
\\_SST(2) -> \\_WAK(3) -> \\_SST(1).
Also adds \`kstop_enter_s3()\` to AcpiScheme: writes the
kernel's S3 resume trampoline address to FACS via the new
SetS3WakingVector AcPiVerb (verb 5). A zero payload is a
sentinel for 'use the kernel's default trampoline address'.
The acpid's enter_sleep_state for S3 will:
1. Do the AML prep (\\_TTS(3), \\_PTS(3), \\_SST(3)) - the
existing set_global_s_state path.
2. Call kstop_enter_s3(0) to write the trampoline
address to FACS.
3. Write 's3' to /scheme/sys/kstop to trigger the
kernel's S3 entry path.
Hardware-agnostic: works on any x86_64 system with
standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14).
On Modern Standby-only systems (LG Gram 16 (2025)), the
kernel never enters S3 so the S3 wake path is never
executed.
Phase J: add the libredox override to the base's
[patch.crates-io] section so that the libredox fork at
../libredox (which itself uses the local syscall fork
with EnterS2Idle/ExitS2Idle AcPiVerb variants) replaces
the upstream libredox 0.1.17. This breaks the
libredox::error::Error <-> syscall::Error type-identity
barrier that previously caused E0277 errors in
scheme-utils and daemon.
The new scheme.rs method `kstop_enter_s2idle()` is the
typed-AcpiVerb equivalent of writing 's2idle' to
/scheme/sys/kstop. Phase I.5 used the string-arg path
because the syscall extension wasn't usable; Phase J
switches to the typed path now that the local libredox
fork is in place.
Hardware-agnostic: works for any platform with Modern
Standby firmware (Dell, HP, Lenovo, LG Gram, etc.).