Skip to content

Commit 3a50412

Browse files
ChocolateLoverRajFlareCoding
authored andcommitted
xHCI - Check if command ring is full before enqueueing command
1 parent 2afeddc commit 3a50412

3 files changed

Lines changed: 45 additions & 8 deletions

File tree

kernel/include/drivers/usb/xhci/xhci_rings.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ class xhci_command_ring {
1414
inline uintptr_t get_physical_base() const { return m_physical_base; }
1515
inline uint8_t get_cycle_bit() const { return m_rcs_bit; }
1616

17-
void enqueue(xhci_trb_t* trb);
17+
bool enqueue(xhci_trb_t* trb);
18+
void process_event(xhci_command_completion_trb_t* event);
1819

1920
private:
20-
size_t m_max_trb_count; // Number of valid TRBs in the ring including the LINK_TRB
21-
size_t m_enqueue_ptr; // Index in the ring where to enqueue next TRB
22-
xhci_trb_t* m_trbs; // Base address of the ring buffer
23-
uintptr_t m_physical_base; // Physical base of the ring
24-
uint8_t m_rcs_bit; // Ring cycle state
21+
size_t m_max_trb_count; // Number of valid TRBs in the ring including the LINK_TRB
22+
size_t m_enqueue_ptr; // Index in the ring where to enqueue next TRB
23+
xhci_trb_t* m_trbs; // Base address of the ring buffer
24+
uintptr_t m_physical_base; // Physical base of the ring
25+
uint8_t m_rcs_bit; // Ring cycle state
26+
size_t m_dequeue_ptr; // The xHC's position as it reads the ring
27+
bool m_consumer_cycle_state; // The consumer (xHC)'s ring cycle state
2528
};
2629

2730
/*

kernel/src/drivers/usb/xhci/xhci.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,10 @@ xhci_command_completion_trb_t* xhci_driver::_send_command(xhci_trb_t* trb, uint3
472472
mutex_guard guard(s_xhc_command_lock);
473473

474474
// Enqueue the TRB
475-
m_command_ring->enqueue(trb);
475+
if (!m_command_ring->enqueue(trb)) {
476+
xhci_warn("Failed to enqueue command. Command ring is full.");
477+
return nullptr;
478+
};
476479

477480
// Ring the command doorbell
478481
m_doorbell_manager->ring_command_doorbell();
@@ -505,6 +508,9 @@ xhci_command_completion_trb_t* xhci_driver::_send_command(xhci_trb_t* trb, uint3
505508
return nullptr;
506509
}
507510

511+
// Update the command ring dequeue pointer
512+
m_command_ring->process_event(completion_trb);
513+
508514
return completion_trb;
509515
}
510516

kernel/src/drivers/usb/xhci/xhci_rings.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ xhci_command_ring::xhci_command_ring(size_t max_trbs) {
77
m_max_trb_count = max_trbs;
88
m_rcs_bit = XHCI_CRCR_RING_CYCLE_STATE;
99
m_enqueue_ptr = 0;
10+
m_dequeue_ptr = 0;
11+
m_consumer_cycle_state = true;
1012

1113
const uint64_t ring_size = max_trbs * sizeof(xhci_trb_t);
1214

@@ -25,7 +27,14 @@ xhci_command_ring::xhci_command_ring(size_t max_trbs) {
2527
(XHCI_TRB_TYPE_LINK << XHCI_TRB_TYPE_SHIFT) | XHCI_LINK_TRB_TC_BIT | m_rcs_bit;
2628
}
2729

28-
void xhci_command_ring::enqueue(xhci_trb_t* trb) {
30+
bool xhci_command_ring::enqueue(xhci_trb_t* trb) {
31+
auto can_enqueue = m_consumer_cycle_state == m_rcs_bit
32+
? m_enqueue_ptr >= m_dequeue_ptr
33+
: m_enqueue_ptr < m_dequeue_ptr;
34+
if (!can_enqueue) {
35+
return false;
36+
}
37+
2938
// Adjust the TRB's cycle bit to the current RCS
3039
trb->cycle_bit = m_rcs_bit;
3140

@@ -43,6 +52,25 @@ void xhci_command_ring::enqueue(xhci_trb_t* trb) {
4352
m_enqueue_ptr = 0;
4453
m_rcs_bit = !m_rcs_bit;
4554
}
55+
56+
return true;
57+
}
58+
59+
void xhci_command_ring::process_event(xhci_command_completion_trb_t* event) {
60+
// xHCI 4.9.3 Command Ring Management
61+
// > The location of the Command Ring Dequeue Pointer is reported on the Event Ring in Command Completion Events.
62+
// xHCI 3.3 Command Interface
63+
// > Commands are executed by the xHC in the order that they are placed on the Command Ring.
64+
auto command_index = (event->command_trb_pointer - m_physical_base) / sizeof(xhci_trb_t);
65+
// This could result in the dequeue pointer pointing to a Link TRB, which should be pretty instantly processed.
66+
// But we can't assume that the xHC processed the Link TRB and we shouldn't overwrite it until we're sure.
67+
// 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.
68+
auto new_dequeue_ptr = command_index + 1;
69+
// If the consumer (xHC) looped around, it must have toggled its consumer cycle state
70+
if (new_dequeue_ptr < m_dequeue_ptr) {
71+
m_consumer_cycle_state = !m_consumer_cycle_state;
72+
}
73+
m_dequeue_ptr = new_dequeue_ptr;
4674
}
4775

4876
xhci_event_ring::xhci_event_ring(

0 commit comments

Comments
 (0)