use heterob::{bit_numbering::Lsb, endianness::Le, Seq, P12, P3, P4, P8};
use super::CapabilityDataError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PowerManagementInterface {
pub capabilities: Capabilities,
pub control: Control,
pub bridge: Bridge,
pub data: u8,
}
impl PowerManagementInterface {
pub fn data(&self) -> Option<Data> {
if self.data == 0 {
None
} else {
Some(Data {
value: self.data,
select: self.control.data_select,
scale: self.control.data_scale,
})
}
}
}
impl TryFrom<&[u8]> for PowerManagementInterface {
type Error = CapabilityDataError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let Seq {
head: Le((capabilities, control, bridge, data)),
..
} = P4(slice).try_into().map_err(|_| CapabilityDataError {
name: "Power Management Interface",
size: 6,
})?;
let Lsb((
version,
pme_clock,
immediate_readiness_on_return_to_d0,
device_specific_initialization,
aux_current,
d1_support,
d2_support,
d0,
d1,
d2,
d3_hot,
d3_cold,
)) = P12::<u16, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1>(capabilities).into();
let Lsb((
power_state,
(),
no_soft_reset,
(),
pme_enabled,
data_select,
data_scale,
pme_status,
)) = P8::<u16, 2, 1, 1, 4, 1, 4, 2, 1>(control).into();
let Lsb((reserved, b2_b3, bpcc_enabled)) = P3::<u8, 6, 1, 1>(bridge).into();
Ok(Self {
capabilities: Capabilities {
version,
pme_clock,
immediate_readiness_on_return_to_d0,
device_specific_initialization,
aux_current: From::<u8>::from(aux_current),
d1_support,
d2_support,
pme_support: PmeSupport {
d0,
d1,
d2,
d3_hot,
d3_cold,
},
},
control: Control {
power_state: From::<u8>::from(power_state),
no_soft_reset,
pme_enabled,
data_select: From::<u8>::from(data_select),
data_scale: From::<u8>::from(data_scale),
pme_status,
},
bridge: Bridge {
reserved,
b2_b3,
bpcc_enabled,
},
data,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Capabilities {
pub version: u8,
pub pme_clock: bool,
pub immediate_readiness_on_return_to_d0: bool,
pub device_specific_initialization: bool,
pub aux_current: AuxCurrent,
pub d1_support: bool,
pub d2_support: bool,
pub pme_support: PmeSupport,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuxCurrent {
SelfPowered,
MaxCurrent55mA,
MaxCurrent100mA,
MaxCurrent160mA,
MaxCurrent220mA,
MaxCurrent270mA,
MaxCurrent320mA,
MaxCurrent375mA,
}
impl From<u8> for AuxCurrent {
fn from(byte: u8) -> Self {
match byte {
0 => Self::SelfPowered,
1 => Self::MaxCurrent55mA,
2 => Self::MaxCurrent100mA,
3 => Self::MaxCurrent160mA,
4 => Self::MaxCurrent220mA,
5 => Self::MaxCurrent270mA,
6 => Self::MaxCurrent320mA,
7 => Self::MaxCurrent375mA,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PmeSupport {
pub d0: bool,
pub d1: bool,
pub d2: bool,
pub d3_hot: bool,
pub d3_cold: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Control {
pub power_state: PowerState,
pub no_soft_reset: bool,
pub pme_enabled: bool,
pub data_select: DataSelect,
pub data_scale: DataScale,
pub pme_status: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PowerState {
D0,
D1,
D2,
D3Hot,
}
impl From<u8> for PowerState {
fn from(byte: u8) -> Self {
match byte {
0b00 => Self::D0,
0b01 => Self::D1,
0b10 => Self::D2,
0b11 => Self::D3Hot,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bridge {
pub reserved: u8,
pub b2_b3: bool,
pub bpcc_enabled: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Data {
pub value: u8,
pub select: DataSelect,
pub scale: DataScale,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataSelect {
PowerConsumedD0,
PowerConsumedD1,
PowerConsumedD2,
PowerConsumedD3,
PowerDissipatedD0,
PowerDissipatedD1,
PowerDissipatedD2,
PowerDissipatedD3,
CommonLogic,
Reserved(u8),
}
impl From<u8> for DataSelect {
fn from(byte: u8) -> Self {
match byte {
0 => Self::PowerConsumedD0,
1 => Self::PowerConsumedD1,
2 => Self::PowerConsumedD2,
3 => Self::PowerConsumedD3,
4 => Self::PowerDissipatedD0,
5 => Self::PowerDissipatedD1,
6 => Self::PowerDissipatedD2,
7 => Self::PowerDissipatedD3,
8 => Self::CommonLogic,
v => Self::Reserved(v),
}
}
}
impl From<DataSelect> for u8 {
fn from(data: DataSelect) -> Self {
match data {
DataSelect::PowerConsumedD0 => 0,
DataSelect::PowerConsumedD1 => 1,
DataSelect::PowerConsumedD2 => 2,
DataSelect::PowerConsumedD3 => 3,
DataSelect::PowerDissipatedD0 => 4,
DataSelect::PowerDissipatedD1 => 5,
DataSelect::PowerDissipatedD2 => 6,
DataSelect::PowerDissipatedD3 => 7,
DataSelect::CommonLogic => 8,
DataSelect::Reserved(v) => v,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataScale {
Unknown,
Tenth,
Hundredth,
Thousandth,
}
impl From<u8> for DataScale {
fn from(byte: u8) -> Self {
match byte {
0b00 => Self::Unknown,
0b01 => Self::Tenth,
0b10 => Self::Hundredth,
0b11 => Self::Thousandth,
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn power_management_interface() {
let data = [0x02, 0x7e, 0x00, 0x00, 0x40, 0x00];
let result = data.as_slice().try_into().unwrap();
let sample = PowerManagementInterface {
capabilities: Capabilities {
version: 0b10,
pme_clock: false,
immediate_readiness_on_return_to_d0: false,
device_specific_initialization: false,
aux_current: AuxCurrent::SelfPowered,
d1_support: true,
d2_support: true,
pme_support: PmeSupport {
d0: true,
d1: true,
d2: true,
d3_hot: true,
d3_cold: false,
},
},
control: Control {
power_state: PowerState::D0,
no_soft_reset: false,
pme_enabled: false,
data_select: DataSelect::PowerConsumedD0,
data_scale: DataScale::Unknown,
pme_status: false,
},
bridge: Bridge {
reserved: 0,
b2_b3: true,
bpcc_enabled: false,
},
data: 0,
};
assert_eq!(sample, result);
}
}