use heterob::{
bit_numbering::Lsb,
endianness::{Le, LeBytesTryInto},
Seq, P12, P4, P6, P7, P8,
};
use snafu::Snafu;
pub use super::advanced_error_reporting::{HeaderLog, TlpPrefixLog};
#[derive(Snafu, Debug, Clone, PartialEq, Eq)]
pub enum DownstreamPortContainmentError {
#[snafu(display("can't read manadatory registers (8 bytes)"))]
Mandatory,
#[snafu(display("can't read RP Extensions registers (36 bytes)"))]
RpExtensions,
#[snafu(display("can't read RP PIO ImpSpec Log Register (4 bytes)"))]
RpPioImpspecLog,
#[snafu(display("can't read RP PIO TLP Prefix Log Register word#: {number}"))]
RpPioTlpPrefixLog { number: usize },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DownstreamPortContainment {
pub dpc_capability: DpcCapability,
pub dpc_control: DpcControl,
pub dpc_status: DpcStatus,
pub dpc_error_source_id: u16,
pub rp_extensions: Option<RpExtensions>,
}
impl TryFrom<&[u8]> for DownstreamPortContainment {
type Error = DownstreamPortContainmentError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let Seq {
head: Le((dpc_capability, dpc_control, dpc_status, dpc_error_source_id)),
tail,
} = P4(slice)
.try_into()
.map_err(|_| DownstreamPortContainmentError::Mandatory)?;
let dpc_capability: DpcCapability = From::<u16>::from(dpc_capability);
let rp_extensions = if dpc_capability.rp_extensions_for_dpc {
let Seq {
head:
Le((
rp_pio_status,
rp_pio_mask,
rp_pio_severity,
rp_pio_syserr,
rp_pio_exception,
rp_pio_header_log,
)),
tail,
} = P6(tail)
.try_into()
.map_err(|_| DownstreamPortContainmentError::RpExtensions)?;
let Le(rp_pio_header_log) = From::<[u8; 4 * 4]>::from(rp_pio_header_log);
let mut slice = tail;
let rp_pio_impspec_log = if dpc_capability.rp_pio_log_size >= 5 {
let Seq {
head: rp_pio_impspec_log,
tail,
} = slice
.le_bytes_try_into()
.map_err(|_| DownstreamPortContainmentError::RpPioImpspecLog)?;
slice = tail;
Some(rp_pio_impspec_log)
} else {
None
};
let rp_pio_tlp_prefix_log = if dpc_capability.rp_pio_log_size > 5 {
let mut rp_pio_tlp_prefix_log = [0u32; 4];
let min_rp_pio_log_size = ((dpc_capability.rp_pio_log_size - 5) as usize).min(4);
for (i, entry) in rp_pio_tlp_prefix_log.iter_mut().enumerate().take(min_rp_pio_log_size) {
let Seq { head, tail } = slice.le_bytes_try_into().map_err(|_| {
DownstreamPortContainmentError::RpPioTlpPrefixLog { number: i }
})?;
slice = tail;
*entry = u32::from_le_bytes(head);
}
Some(TlpPrefixLog(rp_pio_tlp_prefix_log))
} else {
None
};
Some(RpExtensions {
rp_pio_status: From::<u32>::from(rp_pio_status),
rp_pio_mask: From::<u32>::from(rp_pio_mask),
rp_pio_severity: From::<u32>::from(rp_pio_severity),
rp_pio_syserr: From::<u32>::from(rp_pio_syserr),
rp_pio_exception: From::<u32>::from(rp_pio_exception),
rp_pio_header_log: HeaderLog(rp_pio_header_log),
rp_pio_impspec_log,
rp_pio_tlp_prefix_log,
})
} else {
None
};
Ok(Self {
dpc_capability,
dpc_control: From::<u16>::from(dpc_control),
dpc_status: From::<u16>::from(dpc_status),
dpc_error_source_id,
rp_extensions,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RpExtensions {
pub rp_pio_status: RpPio,
pub rp_pio_mask: RpPio,
pub rp_pio_severity: RpPio,
pub rp_pio_syserr: RpPio,
pub rp_pio_exception: RpPio,
pub rp_pio_header_log: HeaderLog,
pub rp_pio_impspec_log: Option<u32>,
pub rp_pio_tlp_prefix_log: Option<TlpPrefixLog>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DpcCapability {
pub dpc_interrupt_message_number: u8,
pub rp_extensions_for_dpc: bool,
pub poisoned_tlp_egress_blocking_supported: bool,
pub dpc_software_triggering_supported: bool,
pub rp_pio_log_size: u8,
pub dl_active_err_cor_signaling_supported: bool,
}
impl From<u16> for DpcCapability {
fn from(word: u16) -> Self {
let Lsb((
dpc_interrupt_message_number,
rp_extensions_for_dpc,
poisoned_tlp_egress_blocking_supported,
dpc_software_triggering_supported,
rp_pio_log_size,
dl_active_err_cor_signaling_supported,
(),
)) = P7::<_, 5, 1, 1, 1, 4, 1, 3>(word).into();
Self {
dpc_interrupt_message_number,
rp_extensions_for_dpc,
poisoned_tlp_egress_blocking_supported,
dpc_software_triggering_supported,
rp_pio_log_size,
dl_active_err_cor_signaling_supported,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DpcControl {
pub dpc_trigger_enable: DpcTrigger,
pub dpc_completion_control: bool,
pub dpc_interrupt_enable: bool,
pub dpc_err_cor_enable: bool,
pub poisoned_tlp_egress_blocking_enable: bool,
pub dpc_software_trigger: bool,
pub dl_active_err_cor_enable: bool,
}
impl From<u16> for DpcControl {
fn from(word: u16) -> Self {
let Lsb((
dpc_trigger_enable,
dpc_completion_control,
dpc_interrupt_enable,
dpc_err_cor_enable,
poisoned_tlp_egress_blocking_enable,
dpc_software_trigger,
dl_active_err_cor_enable,
(),
)) = P8::<_, 2, 1, 1, 1, 1, 1, 1, 8>(word).into();
Self {
dpc_trigger_enable: From::<u8>::from(dpc_trigger_enable),
dpc_completion_control,
dpc_interrupt_enable,
dpc_err_cor_enable,
poisoned_tlp_egress_blocking_enable,
dpc_software_trigger,
dl_active_err_cor_enable,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DpcTrigger {
Disabled,
ErrFatalMessage,
ErrNonFatalMessage,
Reserved,
}
impl From<u8> for DpcTrigger {
fn from(data: u8) -> Self {
match data {
0b00 => Self::Disabled,
0b01 => Self::ErrFatalMessage,
0b10 => Self::ErrNonFatalMessage,
_ => Self::Reserved,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DpcStatus {
pub dpc_trigger_status: bool,
pub dpc_trigger_reason: DpcTriggerReason,
pub dpc_interrupt_status: bool,
pub dpc_rp_busy: bool,
pub rp_pio_first_error_pointer: u8,
}
impl From<u16> for DpcStatus {
fn from(word: u16) -> Self {
let Lsb((
dpc_trigger_status,
dpc_trigger_reason,
dpc_interrupt_status,
dpc_rp_busy,
dpc_trigger_reason_extension,
(),
rp_pio_first_error_pointer,
(),
)) = P8::<_, 1, 2, 1, 1, 2, 1, 5, 3>(word).into();
Self {
dpc_trigger_status,
dpc_trigger_reason: DpcTriggerReason::new(
dpc_trigger_reason,
dpc_trigger_reason_extension,
),
dpc_interrupt_status,
dpc_rp_busy,
rp_pio_first_error_pointer,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DpcTriggerReason {
UnmaskedUncorrectableError,
ReceivingAnErrNonFatal,
ReceivingAnErrFatal,
RpPioError,
DpcSoftwareTriggerBit,
Reserved(u8),
}
impl DpcTriggerReason {
pub fn new(reason: u8, reason_extension: u8) -> Self {
match (reason, reason_extension) {
(0b00, _) => Self::UnmaskedUncorrectableError,
(0b01, _) => Self::ReceivingAnErrNonFatal,
(0b10, _) => Self::ReceivingAnErrFatal,
(_, 0b00) => Self::RpPioError,
(_, 0b01) => Self::DpcSoftwareTriggerBit,
(_, v) => Self::Reserved(v),
}
}
pub fn value(&self) -> u8 {
match self {
Self::UnmaskedUncorrectableError => 0b00,
Self::ReceivingAnErrNonFatal => 0b01,
Self::ReceivingAnErrFatal => 0b10,
_ => 0b11,
}
}
pub fn extension_value(&self) -> u8 {
match self {
Self::RpPioError => 0b00,
Self::DpcSoftwareTriggerBit => 0b01,
Self::Reserved(v) => *v,
_ => 0b00,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RpPio {
pub cfg_ur_cpl: bool,
pub cfg_ca_cpl: bool,
pub cfg_cto: bool,
pub io_ur_cpl: bool,
pub io_ca_cpl: bool,
pub io_cto: bool,
pub mem_ur_cpl: bool,
pub mem_ca_cpl: bool,
pub mem_cto: bool,
}
impl From<u32> for RpPio {
fn from(dword: u32) -> Self {
let Lsb((
cfg_ur_cpl,
cfg_ca_cpl,
cfg_cto,
(),
io_ur_cpl,
io_ca_cpl,
io_cto,
(),
mem_ur_cpl,
mem_ca_cpl,
mem_cto,
(),
)) = P12::<_, 1, 1, 1, 5, 1, 1, 1, 5, 1, 1, 1, 13>(dword).into();
Self {
cfg_ur_cpl,
cfg_ca_cpl,
cfg_cto,
io_ur_cpl,
io_ca_cpl,
io_cto,
mem_ur_cpl,
mem_ca_cpl,
mem_cto,
}
}
}