Fix/xhci USB absolute mouse issues#25
Merged
Kelsidavis merged 10 commits intoKelsidavis:mainfrom Feb 14, 2026
Merged
Conversation
Each xHCI doorbell register is 32 bits wide, so doorbell N lives at
offset DBOFF + N*4 from the BAR base. The code was using DBOFF + N,
which addressed the wrong register for every slot > 0.
Affects all doorbell writes: command ring (slot 0), EP0 control
transfers, HID interrupt submissions, MSC bulk transfers, and UASP
command/data/status transfers.
Ref: xHCI spec §5.6, OSDev xHCI wiki ("Each doorbell register is
32-bits long").
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When updating the Event Ring Dequeue Pointer, the xHCI spec (§5.5.2.3.3) requires bit 3 (EHB) to be set in the write to acknowledge the event and clear the busy flag. Without this, the controller considers the event ring still busy after the first event, stalling further delivery. The pointer is also masked to 16-byte alignment (& ~0xF) as required by the spec, since the low 4 bits are flag fields, not address bits. Affects xhci_poll_cmd_complete, xhci_poll_transfer_complete, and xhci_poll_transfer_event. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Multiple interrelated fixes to the xHCI context data structures per the spec (§6.2): - Input Control Context: Add flags go in DWord1, not DWord0. DWord0 is the Drop flags field, which was being set instead. - Slot Context: Context Entries must be 2 (covering slot + EP0), not 1. Root Hub Port Number is in bits [23:16] of DWord1, not the raw port value. - Endpoint Context: EP Type, CErr, and Max Packet Size all belong in DWord1, not split across DWord0/1. CErr is set to 3 (retry up to 3 times on error). The TR Dequeue Pointer must have the Dequeue Cycle State (DCS) bit set in bit 0. - Configure Endpoint: the Add flags and Slot Context entries are updated to cover the highest endpoint being configured. - Device context output buffer widened from 32 to 256 dwords per slot. The Device Context has up to 32 entries (§6.2.1), each 8 dwords (CSZ=0) or 16 dwords (CSZ=1). The old 32 dwords (128 bytes) could only hold 4 entries at CSZ=0, causing the controller to write past the buffer once endpoints beyond EP0 are configured. 256 dwords (1 KB) covers all 32 entries at CSZ=0; 32 KB total for 32 slots. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The EP0 transfer ring index advances naturally as TRBs are enqueued, so resetting it before every control transfer was unnecessary and could cause the controller to re-process stale TRBs if the ring hadn't been fully consumed. Also removes the Interrupt On Completion (IOC) flag from data-stage TRBs. Only the status-stage TRB needs IOC — setting it on the data stage caused a spurious transfer event before the transfer was actually complete. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Link TRB must be written before the Normal (data) TRB when the ring is full. The old code wrote data at index 31, then tried to write a link at index 32 — past the end of the 32-entry ring. Now checks for wrap first: if ring_index >= 31, writes the Link TRB at the last slot with the Toggle Cycle bit set, resets the index to 0 and flips the cycle, then enqueues the Normal TRB at the start. Also fixes the Link TRB's cycle bit to use the current dev->cycle rather than hardcoding it to always-set, which would cause the controller to process stale link TRBs on the second lap. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ot context The old code issued SET_INTERFACE and SET_PROTOCOL control transfers inside the descriptor parsing loop, before the full configuration was known. This could interfere with the ongoing descriptor read and caused the endpoint to be configured without the device being in the correct state. Defer both requests until after descriptor parsing completes and SET_CONFIGURATION has been sent. SET_PROTOCOL (boot protocol) is only sent for keyboards, after SET_INTERFACE. Also copies the output Slot Context (written by the controller during Address Device) back into the input context before Configure Endpoint. Without this, the input context has zeroed speed/port fields and the controller rejects the command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The old polling model processed only 4 events per call, used tick- based throttling to limit submit rate, and cleared pending flags on timeout. This caused the interrupt pipe to drain and stop delivering events, especially under load. Replace with an event-driven approach: process up to 32 events per poll call, and re-submit the interrupt IN transfer immediately after each completion event. This keeps the pipe continuously armed so the controller always has a TRB to write the next report into. Remove the last_submit_tick throttle from xhci_hid_submit and the pending timeout loop from xhci_hid_poll_events — the host controller now drives the cadence via completion events. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
QEMU presents both a PS/2 mouse and a USB tablet simultaneously. The PS/2 relative deltas fight the tablet's absolute coordinates, causing erratic cursor behavior. Add a HAL mouse-source selector (MouseSource enum in input.h) so PS/2 mouse bytes are discarded when a USB tablet is present. The kernel checks for an absolute pointer device after xHCI enumeration and switches the source accordingly. Note: this approach is QEMU-specific — both devices mirror the same host mouse, so processing both causes per-frame jitter. On real hardware with genuinely separate devices (e.g., PS/2 mouse + Wacom tablet), a "last writer wins" model would be preferable. Simplify the xHCI HID mouse report parser: remove the heuristic that guessed whether byte 0 was a report ID based on value ranges. The heuristic failed when the tablet reported zero coordinates with no buttons pressed, causing the first byte to be misinterpreted. Fix absolute coordinate rounding: add half-divisor before integer division to round to nearest pixel instead of truncating. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add diagnostic logging throughout the xHCI driver to aid debugging during enumeration and HID polling: - Transfer poll timeout with slot and event index - HID interface classification (class/subclass/protocol) - Mouse endpoint configuration success and failure - Port status when no device is connected - Port power settle delay after power-on Add %04x format handler to SysLogFormatAndSend and register the XHCI log tag at warn level. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lower the cursor redraw interval from every 50th to every 2nd main-loop iteration. With event-driven HID polling (commit 7), mouse reports arrive promptly and the cursor should track them without perceptible lag. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary