use core::mem::{size_of, MaybeUninit}; use bytemuck::{Pod, Zeroable}; use device_api::dma::DmaAllocator; use libk::dma::{BusAddress, DmaBuffer}; use libk_util::sync::IrqSafeSpinlock; use ygg_driver_usb::error::UsbError; use yggdrasil_abi::define_bitfields; use super::{command::CommandReply, GenericRing}; pub enum Event { PortChange(usize), CommandCompletion { address: BusAddress, reply: CommandReply, }, Transfer { address: BusAddress, slot_id: u8, endpoint_id: u8, status: u32, }, } #[repr(C, align(16))] pub struct EventRingSegment { address: BusAddress, // Number of TRBs supported by the ring segment. Valid values are 16 to 4096 size: u16, _0: u16, _1: u32, } pub struct EventRingSegmentTable { entries: DmaBuffer<[EventRingSegment]>, } struct EventRingInner { trbs: DmaBuffer<[MaybeUninit]>, dequeue_index: usize, cycle_bit: bool, } pub struct EventRing { inner: IrqSafeSpinlock, capacity: usize, } impl EventRingSegmentTable { pub fn for_event_rings(dma: &dyn DmaAllocator, rings: &[&EventRing]) -> Result { let entries = DmaBuffer::new_slice_with( dma, |i| EventRingSegment { address: rings[i].bus_base(), size: rings[i].capacity.try_into().unwrap(), _0: 0, _1: 0, }, rings.len(), ) .map_err(UsbError::MemoryError)?; Ok(Self { entries }) } pub fn bus_address(&self) -> BusAddress { self.entries.bus_address() } pub fn capacity(&self) -> usize { self.entries.len() } } impl GenericRing for EventRing { fn bus_base(&self) -> BusAddress { self.inner.lock().trbs.bus_address() } } impl EventRingInner { fn try_dequeue(&mut self) -> Option { let trb = unsafe { self.trbs[self.dequeue_index].assume_init_ref() }; // TRB cannot be consumed -- its cycle bit not toggled let trb_cycle = trb.cycle_bit(); if trb_cycle != self.cycle_bit { return None; } self.dequeue_index += 1; if self.dequeue_index == self.trbs.len() { self.dequeue_index = 0; self.cycle_bit = !self.cycle_bit; } trb.into_event() } } impl EventRing { pub fn new(dma: &dyn DmaAllocator, capacity: usize) -> Result { let trbs = DmaBuffer::new_zeroed_slice(dma, capacity).map_err(UsbError::MemoryError)?; Ok(Self { inner: IrqSafeSpinlock::new(EventRingInner { trbs, dequeue_index: 0, cycle_bit: true, }), capacity, }) } pub fn try_dequeue(&self) -> Option { self.inner.lock().try_dequeue() } pub fn dequeue_pointer(&self) -> BusAddress { let i = self.inner.lock(); i.trbs .bus_address() .add(i.dequeue_index * size_of::()) } } // TRB implementations define_bitfields! { pub TransferEventStatus : u32 { (24..32) => completion_code, (0..24) => sub_length } } define_bitfields! { pub TransferEventFlags : u32 { (24..32) => slot_id, (16..20) => endpoint_id, } } define_bitfields! { pub CommandCompletionEventStatus : u32 { (24..32) => completion_code, (0..24) => completion_parameter } } define_bitfields! { pub CommandCompletionEventFlags : u32 { (24..32) => slot_id, } } define_bitfields! { pub PortStatusChangeEventAddress : u32 { (24..32) => port_id } } define_bitfields! { pub RawEventFlags : u32 { (10..16) => ty, 0 => cycle } } #[derive(Clone, Copy, Debug, Pod, Zeroable)] #[repr(C, align(16))] pub struct TransferEventTrb { pub address: BusAddress, pub status: TransferEventStatus, pub flags: TransferEventFlags, } #[derive(Clone, Copy, Debug, Pod, Zeroable)] #[repr(C, align(16))] pub struct CommandCompletionEventTrb { pub address: BusAddress, pub status: CommandCompletionEventStatus, pub flags: CommandCompletionEventFlags, } #[derive(Clone, Copy, Debug, Pod, Zeroable)] #[repr(C, align(16))] pub struct PortStatusChangeEventTrb { pub address: PortStatusChangeEventAddress, _0: [u32; 3], } #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C, align(16))] pub struct RawEventTrb { _0: [u32; 3], pub flags: RawEventFlags, } impl RawEventTrb { pub fn into_event(self) -> Option { match self.flags.ty() { 32 => { let transfer: TransferEventTrb = bytemuck::cast(self); Some(Event::Transfer { address: transfer.address, slot_id: transfer.flags.slot_id() as _, endpoint_id: transfer.flags.endpoint_id() as _, status: transfer.status.into_raw(), }) } 33 => { let command: CommandCompletionEventTrb = bytemuck::cast(self); Some(Event::CommandCompletion { address: command.address, reply: CommandReply { completion_code: command.status.completion_code() as _, completion_parameter: command.status.completion_parameter(), slot_id: command.flags.slot_id() as _, }, }) } 34 => { let port_status: PortStatusChangeEventTrb = bytemuck::cast(self); Some(Event::PortChange(port_status.address.port_id() as _)) } ty => { log::warn!("Unhandled event TRB with type: {}", ty); None } } } pub fn cycle_bit(&self) -> bool { self.flags.cycle() } }