use core::sync::atomic::AtomicU32; pub const MAX_SQ_ENTRIES: u32 = 64; pub const MAX_CQ_ENTRIES: u32 = 64; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RingIndex(u32); impl RingIndex { pub const fn new(val: u32) -> Self { Self(val) } #[inline] pub const fn raw(self) -> u32 { self.0 } #[inline] pub const fn advance(self, n: u32) -> Self { Self(self.0.wrapping_add(n)) } #[inline] pub const fn distance(self, older: Self) -> u32 { self.0.wrapping_sub(older.0) } #[inline] pub const fn slot(self, capacity: u32) -> u32 { self.0 % capacity } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum RingOpcode { Nop = 0, CapCreate = 1, CapDerive = 2, IpcSend = 10, IpcRecv = 11, NotifySignal = 20, NotifyPoll = 21, } impl RingOpcode { pub const fn from_u8(val: u8) -> Option { match val { 0 => Some(Self::Nop), 1 => Some(Self::CapCreate), 2 => Some(Self::CapDerive), 10 => Some(Self::IpcSend), 11 => Some(Self::IpcRecv), 20 => Some(Self::NotifySignal), 21 => Some(Self::NotifyPoll), _ => None, } } } pub const RING_OP_NOP: u8 = RingOpcode::Nop as u8; pub const RING_OP_CAP_CREATE: u8 = RingOpcode::CapCreate as u8; pub const RING_OP_CAP_DERIVE: u8 = RingOpcode::CapDerive as u8; pub const RING_OP_IPC_SEND: u8 = RingOpcode::IpcSend as u8; pub const RING_OP_IPC_RECV: u8 = RingOpcode::IpcRecv as u8; pub const RING_OP_NOTIFY_SIGNAL: u8 = RingOpcode::NotifySignal as u8; pub const RING_OP_NOTIFY_POLL: u8 = RingOpcode::NotifyPoll as u8; #[repr(C)] pub struct RingHeader { pub sq_head: AtomicU32, pub sq_tail: AtomicU32, pub cq_head: AtomicU32, pub cq_tail: AtomicU32, pub sq_len: u32, pub cq_len: u32, } #[derive(Debug, Clone, Copy)] #[repr(C)] #[derive(zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::KnownLayout, zerocopy::Immutable)] pub struct SubmissionEntry { pub arg0: u64, pub arg1: u64, pub arg2: u64, pub user_data: u32, pub cap_slot: u8, pub opcode: u8, pub flags: u8, pub _pad: u8, } #[derive(Debug, Clone, Copy)] #[repr(C)] #[derive(zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::KnownLayout, zerocopy::Immutable)] pub struct CompletionEntry { pub result: i64, pub user_data: u64, pub extra: u64, } #[allow(dead_code)] impl SubmissionEntry { pub const fn zeroed() -> Self { Self { arg0: 0, arg1: 0, arg2: 0, user_data: 0, cap_slot: 0, opcode: 0, flags: 0, _pad: 0, } } } #[allow(dead_code)] impl CompletionEntry { pub const fn zeroed() -> Self { Self { result: 0, user_data: 0, extra: 0, } } } pub fn ring_sq_offset() -> usize { core::mem::size_of::() } pub fn ring_cq_offset() -> usize { ring_sq_offset() + (MAX_SQ_ENTRIES as usize) * core::mem::size_of::() } pub fn ring_total_size() -> usize { ring_cq_offset() + (MAX_CQ_ENTRIES as usize) * core::mem::size_of::() } const _: () = assert!( core::mem::align_of::() <= 4096, "RingHeader alignment must not exceed page size (ring buffer is page-aligned)" ); const _: () = assert!( core::mem::size_of::() == 32, "SubmissionEntry must be exactly 32 bytes" ); const _: () = assert!( core::mem::size_of::() == 24, "CompletionEntry must be exactly 24 bytes" ); const _: () = assert!( core::mem::size_of::() + MAX_SQ_ENTRIES as usize * core::mem::size_of::() + MAX_CQ_ENTRIES as usize * core::mem::size_of::() <= 4096, "ring layout must fit in a single 4KiB page" );