use heterob::{bit_numbering::Lsb, endianness::Le, Seq, P3};
use snafu::prelude::*;
use super::ExtendedCapabilityHeaderPlaceholder;
#[derive(Snafu, Debug, Clone, PartialEq, Eq)]
pub enum DesignatedVendorSpecificExtendedCapabilityError {
#[snafu(display("mandatory fields are unreadable"))]
Mandatory,
#[snafu(display(
"Vendor-specific registers (VID: {:04x}, rev: {:02x}, ID: {:04x}) are unreadable. Length expected: {}, real: {}",
dvsec_vendor_id,
dvsec_revision,
dvsec_id,
dvsec_length,
real,
))]
VendorSpecificRegisters {
dvsec_vendor_id: u16,
dvsec_revision: u8,
dvsec_length: u16,
dvsec_id: u16,
real: usize,
},
#[snafu(display("Compute Express Link error: {source}"))]
ComputeExpressLink {
source: compute_express_link::ComputeExpressLinkError,
},
}
pub type DvsecError = DesignatedVendorSpecificExtendedCapabilityError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DesignatedVendorSpecificExtendedCapability<'a> {
pub dvsec_vendor_id: u16,
pub dvsec_revision: u8,
pub dvsec_length: u16,
pub dvsec_id: u16,
pub dvsec_type: DvsecType<'a>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DvsecType<'a> {
Unspecified(&'a [u8]),
ComputeExpressLink(ComputeExpressLink),
}
pub type Dvsec<'a> = DesignatedVendorSpecificExtendedCapability<'a>;
impl<'a> Dvsec<'a> {
pub const MIN_SIZE: usize = 0x0a;
pub const MAX_SIZE: usize = 0xfff - 0x100;
}
impl<'a> TryFrom<&'a [u8]> for Dvsec<'a> {
type Error = DvsecError;
fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
let Seq {
head: Le((ExtendedCapabilityHeaderPlaceholder, dvsec_header_1, dvsec_id)),
..
} = P3(slice).try_into().map_err(|_| DvsecError::Mandatory)?;
let Lsb((dvsec_vendor_id, dvsec_revision, dvsec_length)) =
P3::<u32, 16, 4, 12>(dvsec_header_1).into();
let dvsec_vendor_specific_registers = slice
.get(Self::MIN_SIZE..dvsec_length as usize)
.ok_or(DvsecError::VendorSpecificRegisters {
dvsec_vendor_id,
dvsec_revision,
dvsec_length,
dvsec_id,
real: slice.len(),
})?;
let dvsec_type = match dvsec_vendor_id {
0x1e98 => ComputeExpressLink::try_new(dvsec_vendor_specific_registers, dvsec_id)
.map(DvsecType::ComputeExpressLink)
.context(ComputeExpressLinkSnafu)?,
_ => DvsecType::Unspecified(dvsec_vendor_specific_registers),
};
Ok(Dvsec {
dvsec_vendor_id,
dvsec_revision,
dvsec_length,
dvsec_id,
dvsec_type,
})
}
}
pub mod compute_express_link;
pub use compute_express_link::ComputeExpressLink;