use tracing::{debug, trace};
use crate::device::{
bus::{BusDeviceRef, Request, RequestSize},
pci::constants::xhci::rings::{
event_ring::segments_table_entry_offsets::{SEGMENT_BASE, SIZE},
TRB_SIZE,
},
xhci::trb::EventTrb,
};
#[derive(Debug)]
pub struct EventRing {
dma_bus: BusDeviceRef,
enqueue_pointer: u64,
trb_count: u32,
erst_count: u32,
cycle_state: bool,
}
impl EventRing {
pub fn new(dma_bus: BusDeviceRef) -> Self {
Self {
dma_bus,
enqueue_pointer: 0,
trb_count: 0,
erst_count: 0,
cycle_state: false,
}
}
pub fn configure(&mut self, base_address: u64, erst_size: u32) {
assert_eq!(base_address & 0x3f, 0, "unaligned event ring base address");
assert!(
erst_size > 0,
"ERSTSZ must be set before ERSTBA; misconfigured driver"
);
self.enqueue_pointer = self.dma_bus.read(Request::new(
base_address.wrapping_add(SEGMENT_BASE),
RequestSize::Size8,
));
self.trb_count = self.dma_bus.read(Request::new(
base_address.wrapping_add(SIZE),
RequestSize::Size4,
)) as u32;
self.cycle_state = true;
debug!("event ring segment table is at {:#x}", base_address);
debug!(
"initializing event ring enqueue pointer from ERST[0] base: {:#x}",
self.enqueue_pointer
);
debug!(
"retrieving TRB count of the first event ring segment from the segment table: {}",
self.trb_count
);
}
pub fn enqueue(
&mut self,
trb: &EventTrb,
base_address: u64,
erst_size: u32,
dequeue_pointer: u64,
) {
if self.check_event_ring_full(base_address, erst_size, dequeue_pointer) {
todo!("The Event Ring is full!");
}
self.dma_bus
.write_bulk(self.enqueue_pointer, &trb.to_bytes(self.cycle_state));
self.trb_count -= 1;
trace!(
"enqueued TRB in segment {} (total_segments={}) of event ring at address {:#x}. Space for {} more TRBs left in segment; cycle={}; (TRB: {:?})",
self.erst_count, erst_size, self.enqueue_pointer, self.trb_count, self.cycle_state, trb
);
self.advance_enqueue_pointer(base_address, erst_size);
}
fn advance_enqueue_pointer(&mut self, base_address: u64, erst_size: u32) {
if self.trb_count == 0 {
self.advance_segment_or_wrap(base_address, erst_size);
} else {
self.enqueue_pointer = self.enqueue_pointer.wrapping_add(TRB_SIZE as u64);
}
}
fn check_event_ring_full(
&self,
base_address: u64,
erst_size: u32,
dequeue_pointer: u64,
) -> bool {
if self.trb_count == 1 {
let next_seg = (self.erst_count + 1) % erst_size;
let entry_addr = base_address.wrapping_add((next_seg as u64) * 16);
let next_seg_pointer = self.dma_bus.read(Request::new(
entry_addr.wrapping_add(SEGMENT_BASE),
RequestSize::Size8,
));
dequeue_pointer == next_seg_pointer
} else {
dequeue_pointer == self.enqueue_pointer.wrapping_add(TRB_SIZE as u64)
}
}
fn advance_segment_or_wrap(&mut self, base_address: u64, erst_size: u32) {
self.erst_count += 1;
let wrapped = self.erst_count == erst_size;
if wrapped {
self.cycle_state = !self.cycle_state;
self.erst_count = 0;
}
let entry_addr = base_address.wrapping_add((self.erst_count as u64) * 16);
self.enqueue_pointer = self.dma_bus.read(Request::new(
entry_addr.wrapping_add(SEGMENT_BASE),
RequestSize::Size8,
));
self.trb_count = self.dma_bus.read(Request::new(
entry_addr.wrapping_add(SIZE),
RequestSize::Size4,
)) as u32;
if wrapped {
trace!(
"wrapped to segment 0; base={:#x}, trb_count={}, cycle={}, total_segments={}",
self.enqueue_pointer,
self.trb_count,
self.cycle_state,
erst_size
);
} else {
trace!(
"advanced to segment {}; base={:#x}, trb_count={}, cycle={}, total_segments={}",
self.erst_count,
self.enqueue_pointer,
self.trb_count,
self.cycle_state,
erst_size
);
}
}
}
#[cfg(test)]
mod tests {
use crate::device::bus::testutils::TestBusDevice;
use crate::device::xhci::trb::CompletionCode;
use std::sync::Arc;
use super::*;
struct EventRingRegistersDummy {
erstsz: u32,
erstba: u64,
erdp: u64,
}
fn init_ram_and_ring_and_registers() -> (Arc<TestBusDevice>, EventRing, EventRingRegistersDummy)
{
let erste = [
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
let ram = Arc::new(TestBusDevice::new(&[0; 0x90]));
ram.write_bulk(0x0, &erste);
let mut ring = EventRing::new(ram.clone());
let reg = EventRingRegistersDummy {
erstsz: 3,
erstba: 0x0,
erdp: 0x30,
};
ring.configure(0x0, reg.erstsz);
(ram, ring, reg)
}
fn dummy_trb() -> EventTrb {
EventTrb::new_transfer_event_trb(
0, 0, CompletionCode::Success, false, 1, 1, )
}
fn assert_trb_written(ram: &TestBusDevice, addr: u64, cycle_state: bool) {
let mut buf = [0u8; 16];
ram.read_bulk(addr, &mut buf);
let cycle_bit = buf[12] & 0x1 != 0;
assert_eq!(
cycle_bit, cycle_state,
"TRB not written at address {addr:#x}"
);
}
#[test]
fn event_ring_start_empty_enqueue_fill_then_wraparound_after_dequeue_pointer_move() {
let (ram, mut ring, mut reg) = init_ram_and_ring_and_registers();
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x30, true);
assert_trb_written(&ram, 0x30 + 16, true);
assert_trb_written(&ram, 0x30 + 32, true);
reg.erdp = 0x30 + 32;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x60, true);
reg.erdp = 0x60;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x70, true);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x70 + 16, true);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x30, false);
}
#[test]
#[should_panic(expected = "Event Ring is full")]
fn event_ring_panics_on_wraparound_mid_segment_full() {
let (_ram, mut ring, mut reg) = init_ram_and_ring_and_registers();
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
reg.erdp = 0x30 + 16;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
}
#[test]
fn event_ring_multiple_wraparound() {
let (ram, mut ring, mut reg) = init_ram_and_ring_and_registers();
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); reg.erdp = 0x30 + 16;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x80, true);
reg.erdp = 0x30 + 16 * 5;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); reg.erdp = 0x30 + 32;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x70, false);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x80, false);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x30, true);
}
#[test]
#[should_panic(expected = "ERSTSZ must be set before ERSTBA")]
fn configure_requires_erstsz_first() {
let erste = [
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let ram = Arc::new(TestBusDevice::new(&[0; 0x90]));
ram.write_bulk(0x0, &erste);
let mut ring = EventRing::new(ram);
ring.configure(0x0, 0x0);
}
#[test]
fn event_ring_dynamic_grow_from_1_to_3() {
let erste = [
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let ram = Arc::new(TestBusDevice::new(&[0; 0x90]));
ram.write_bulk(0x0, &erste);
let mut ring = EventRing::new(ram.clone());
let mut reg = EventRingRegistersDummy {
erstsz: 1,
erstba: 0x0,
erdp: 0x30,
};
ring.configure(reg.erstba, reg.erstsz);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
reg.erdp = 0x30 + 16;
reg.erstsz = 3;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x30 + 32, true);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x60, true);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x70, true);
assert_trb_written(&ram, 0x70 + 16, true);
reg.erdp = 0x30 + 32;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x30, false);
}
#[test]
fn event_ring_dynamic_shrink_to_1() {
let (ram, mut ring, mut reg) = init_ram_and_ring_and_registers();
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
reg.erdp = 0x30 + 16;
reg.erstsz = 1;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x50, true);
reg.erdp = 0x30 + 32;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x30, false);
}
#[test]
fn event_ring_dynamic_overwrite() {
let (ram, mut ring, mut reg) = init_ram_and_ring_and_registers();
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
let erste_new = [
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
ram.write_bulk(0x0, &erste_new);
reg.erstsz = 2;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); reg.erdp = 0x30 + 32;
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp); assert_trb_written(&ram, 0x60 + 16, true);
ring.enqueue(&dummy_trb(), reg.erstba, reg.erstsz, reg.erdp);
assert_trb_written(&ram, 0x30, false);
}
}