Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions kernel/include/drivers/usb/xhci/xhci_rings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/*
Expand Down
8 changes: 7 additions & 1 deletion kernel/src/drivers/usb/xhci/xhci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}

Expand Down
30 changes: 29 additions & 1 deletion kernel/src/drivers/usb/xhci/xhci_rings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;

Expand All @@ -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(
Expand Down