use heterob::{bit_numbering::Lsb, endianness::Le, Seq, P2, P4, P8};
use snafu::Snafu;
use super::ExtendedCapabilityHeader;
#[derive(Snafu, Debug, Clone, PartialEq, Eq)]
pub enum DynamicPowerAllocationError {
#[snafu(display("capability, latency indicator, status and control fields are unreadable"))]
Mandatory,
#[snafu(display("number of entries must be equal to the Substate_Max plus one (expected: {expected}, found: {found})"))]
DpaAllocationArray { expected: usize, found: usize },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DynamicPowerAllocation<'a> {
pub dpa_capability: DpaCapability,
pub dpa_latency_indicator: u32,
pub dpa_status: DpaStatus,
pub dpa_control: DpaControl,
pub dpa_power_allocation_array: DpaPowerAllocationArray<'a>,
}
impl<'a> TryFrom<&'a [u8]> for DynamicPowerAllocation<'a> {
type Error = DynamicPowerAllocationError;
fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
let slice = slice
.get(ExtendedCapabilityHeader::SIZE..)
.unwrap_or_default();
let Seq {
head: Le((dpa_capability, dpa_latency_indicator, dpa_status, dpa_control)),
tail,
} = P4(slice)
.try_into()
.map_err(|_| DynamicPowerAllocationError::Mandatory)?;
let dpa_capability @ DpaCapability { substate_max, .. } = From::<u32>::from(dpa_capability);
let substate_max = substate_max as usize;
tail.get(..substate_max + 1)
.ok_or(DynamicPowerAllocationError::DpaAllocationArray {
expected: substate_max,
found: tail.len(),
})
.map(|slice| Self {
dpa_capability,
dpa_latency_indicator,
dpa_control: From::<u16>::from(dpa_control),
dpa_status: From::<u16>::from(dpa_status),
dpa_power_allocation_array: DpaPowerAllocationArray(slice),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DpaCapability {
pub substate_max: u8,
pub transition_latency_unit: TransitionLatencyUnit,
pub power_allocation_scale: PowerAllocationScale,
pub transition_latency_value_0: u8,
pub transition_latency_value_1: u8,
}
impl From<u32> for DpaCapability {
fn from(dword: u32) -> Self {
let Lsb((
substate_max,
(),
transition_latency_unit,
(),
power_allocation_scale,
(),
transition_latency_value_0,
transition_latency_value_1,
)) = P8::<_, 5, 3, 2, 2, 2, 2, 8, 8>(dword).into();
Self {
substate_max,
transition_latency_unit: From::<u8>::from(transition_latency_unit),
power_allocation_scale: From::<u8>::from(power_allocation_scale),
transition_latency_value_0,
transition_latency_value_1,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransitionLatencyUnit {
Unit1ms,
Unit10ms,
Unit100ms,
Reserved,
}
impl From<u8> for TransitionLatencyUnit {
fn from(byte: u8) -> Self {
match byte {
0b00 => Self::Unit1ms,
0b01 => Self::Unit10ms,
0b10 => Self::Unit100ms,
0b11 => Self::Reserved,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PowerAllocationScale {
Mul10,
Mul1_0,
Mul0_1,
Mul0_01,
}
impl From<u8> for PowerAllocationScale {
fn from(byte: u8) -> Self {
match byte {
0b00 => Self::Mul10,
0b01 => Self::Mul1_0,
0b10 => Self::Mul0_1,
0b11 => Self::Mul0_01,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DpaStatus {
pub substate_status: u8,
pub substate_control_enabled: bool,
}
impl From<u16> for DpaStatus {
fn from(word: u16) -> Self {
let Lsb((substate_status, (), substate_control_enabled, ())) =
P4::<_, 5, 3, 1, 7>(word).into();
Self {
substate_status,
substate_control_enabled,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DpaControl {
pub substate_control: u8,
}
impl From<u16> for DpaControl {
fn from(word: u16) -> Self {
let Lsb((substate_control, ())) = P2::<_, 5, 11>(word).into();
Self { substate_control }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DpaPowerAllocationArray<'a>(pub &'a [u8]);