From 59497e756665c3576e2aa36e18bde14e3898d28a Mon Sep 17 00:00:00 2001 From: Rajas Paranjpe <52586855+ChocolateLoverRaj@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:06:23 -0700 Subject: [PATCH] xHCI - Check if command ring is full before enqueueing command --- kernel/include/drivers/usb/xhci/xhci_rings.h | 15 ++++++---- kernel/src/drivers/usb/xhci/xhci.cpp | 8 +++++- kernel/src/drivers/usb/xhci/xhci_rings.cpp | 30 +++++++++++++++++++- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/kernel/include/drivers/usb/xhci/xhci_rings.h b/kernel/include/drivers/usb/xhci/xhci_rings.h index 4421300b..9e8189c7 100644 --- a/kernel/include/drivers/usb/xhci/xhci_rings.h +++ b/kernel/include/drivers/usb/xhci/xhci_rings.h @@ -14,14 +14,17 @@ class xhci_command_ring { inline uintptr_t get_physical_base() const { return m_physical_base; } inline uint8_t get_cycle_bit() const { return m_rcs_bit; } - void enqueue(xhci_trb_t* trb); + bool enqueue(xhci_trb_t* trb); + void process_event(xhci_command_completion_trb_t* event); private: - size_t m_max_trb_count; // Number of valid TRBs in the ring including the LINK_TRB - size_t m_enqueue_ptr; // Index in the ring where to enqueue next TRB - xhci_trb_t* m_trbs; // Base address of the ring buffer - uintptr_t m_physical_base; // Physical base of the ring - uint8_t m_rcs_bit; // Ring cycle state + size_t m_max_trb_count; // Number of valid TRBs in the ring including the LINK_TRB + size_t m_enqueue_ptr; // Index in the ring where to enqueue next TRB + xhci_trb_t* m_trbs; // Base address of the ring buffer + uintptr_t m_physical_base; // Physical base of the ring + uint8_t m_rcs_bit; // Ring cycle state + size_t m_dequeue_ptr; // The xHC's position as it reads the ring + bool m_consumer_cycle_state; // The consumer (xHC)'s ring cycle state }; /* diff --git a/kernel/src/drivers/usb/xhci/xhci.cpp b/kernel/src/drivers/usb/xhci/xhci.cpp index 9456d912..b8c13786 100644 --- a/kernel/src/drivers/usb/xhci/xhci.cpp +++ b/kernel/src/drivers/usb/xhci/xhci.cpp @@ -472,7 +472,10 @@ xhci_command_completion_trb_t* xhci_driver::_send_command(xhci_trb_t* trb, uint3 mutex_guard guard(s_xhc_command_lock); // Enqueue the TRB - m_command_ring->enqueue(trb); + if (!m_command_ring->enqueue(trb)) { + xhci_warn("Failed to enqueue command. Command ring is full."); + return nullptr; + }; // Ring the command doorbell m_doorbell_manager->ring_command_doorbell(); @@ -505,6 +508,9 @@ xhci_command_completion_trb_t* xhci_driver::_send_command(xhci_trb_t* trb, uint3 return nullptr; } + // Update the command ring dequeue pointer + m_command_ring->process_event(completion_trb); + return completion_trb; } diff --git a/kernel/src/drivers/usb/xhci/xhci_rings.cpp b/kernel/src/drivers/usb/xhci/xhci_rings.cpp index 6054c4ed..f98b848e 100644 --- a/kernel/src/drivers/usb/xhci/xhci_rings.cpp +++ b/kernel/src/drivers/usb/xhci/xhci_rings.cpp @@ -7,6 +7,8 @@ xhci_command_ring::xhci_command_ring(size_t max_trbs) { m_max_trb_count = max_trbs; m_rcs_bit = XHCI_CRCR_RING_CYCLE_STATE; m_enqueue_ptr = 0; + m_dequeue_ptr = 0; + m_consumer_cycle_state = true; const uint64_t ring_size = max_trbs * sizeof(xhci_trb_t); @@ -25,7 +27,14 @@ xhci_command_ring::xhci_command_ring(size_t max_trbs) { (XHCI_TRB_TYPE_LINK << XHCI_TRB_TYPE_SHIFT) | XHCI_LINK_TRB_TC_BIT | m_rcs_bit; } -void xhci_command_ring::enqueue(xhci_trb_t* trb) { +bool xhci_command_ring::enqueue(xhci_trb_t* trb) { + auto can_enqueue = m_consumer_cycle_state == m_rcs_bit + ? m_enqueue_ptr >= m_dequeue_ptr + : m_enqueue_ptr < m_dequeue_ptr; + if (!can_enqueue) { + return false; + } + // Adjust the TRB's cycle bit to the current RCS trb->cycle_bit = m_rcs_bit; @@ -43,6 +52,25 @@ void xhci_command_ring::enqueue(xhci_trb_t* trb) { m_enqueue_ptr = 0; m_rcs_bit = !m_rcs_bit; } + + return true; +} + +void xhci_command_ring::process_event(xhci_command_completion_trb_t* event) { + // xHCI 4.9.3 Command Ring Management + // > The location of the Command Ring Dequeue Pointer is reported on the Event Ring in Command Completion Events. + // xHCI 3.3 Command Interface + // > Commands are executed by the xHC in the order that they are placed on the Command Ring. + auto command_index = (event->command_trb_pointer - m_physical_base) / sizeof(xhci_trb_t); + // This could result in the dequeue pointer pointing to a Link TRB, which should be pretty instantly processed. + // But we can't assume that the xHC processed the Link TRB and we shouldn't overwrite it until we're sure. + // Since commands are executed in order, we don't need to worry about the dequeue pointer getting moved back because of out-of-order events. + auto new_dequeue_ptr = command_index + 1; + // If the consumer (xHC) looped around, it must have toggled its consumer cycle state + if (new_dequeue_ptr < m_dequeue_ptr) { + m_consumer_cycle_state = !m_consumer_cycle_state; + } + m_dequeue_ptr = new_dequeue_ptr; } xhci_event_ring::xhci_event_ring(