//! PCI header classification and bounded logical discovery inventory. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { Absent, InvalidHeaderType, FunctionOutOfScope, Duplicate, Full, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HeaderKind { Endpoint, Bridge, CardBus } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Header { pub vendor_id: u16, pub device_id: u16, pub kind: HeaderKind, pub multifunction: bool, } impl Header { pub fn parse(identity_dword: u32, class_dword: u32) -> Result { let vendor_id = identity_dword as u16; if vendor_id == 0xffff { return Err(Error::Absent); } let raw = (class_dword >> 16) as u8; let kind = match raw & 0x7f { 0 => HeaderKind::Endpoint, 1 => HeaderKind::Bridge, 2 => HeaderKind::CardBus, _ => return Err(Error::InvalidHeaderType), }; Ok(Self {vendor_id, device_id: (identity_dword >> 16) as u16, kind, multifunction: raw & 0x80 != 0}) } pub const fn function_count(self) -> u8 { if self.multifunction { 8 } else { 1 } } pub const fn allows(self, function: u8) -> bool { function < self.function_count() } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct FunctionIdentity { pub bus: u8, pub device: u8, pub function: u8, pub header: Header, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub entries: [Option; N], pub count: usize, } pub struct Inventory { entries: [Option; N], count: usize, } impl Inventory { pub const fn new() -> Self { Self {entries: [None; N], count: 0} } pub const fn count(&self) -> usize { self.count } pub fn snapshot(&self) -> Snapshot { Snapshot {entries: self.entries, count: self.count} } pub fn publish(&mut self, root: Header, bus: u8, device: u8, function: u8, identity_dword: u32, class_dword: u32) -> Result { if root.vendor_id == 0xffff { return Err(Error::Absent); } if !root.allows(function) { return Err(Error::FunctionOutOfScope); } let header = Header::parse(identity_dword, class_dword)?; let identity = FunctionIdentity {bus, device, function, header}; if self.entries.iter().flatten().any(|old| (old.bus, old.device, old.function) == (bus, device, function)) { return Err(Error::Duplicate); } let slot = self.entries.iter_mut().find(|slot| slot.is_none()).ok_or(Error::Full)?; *slot = Some(identity); self.count += 1; Ok(identity) } }