From c0f1d91b389fe849cfd6dc19f03f6281147811ab Mon Sep 17 00:00:00 2001 From: Robert Zieba Date: Thu, 6 Feb 2025 16:54:57 -0700 Subject: [PATCH] Create PDO definitions --- Cargo.toml | 1 + src/pdo/mod.rs | 109 ++++++++++++ src/pdo/sink.rs | 385 ++++++++++++++++++++++++++++++++++++++++ src/pdo/source.rs | 444 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 939 insertions(+) create mode 100644 src/pdo/sink.rs create mode 100644 src/pdo/source.rs diff --git a/Cargo.toml b/Cargo.toml index a7503d3..b096e6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] defmt = { version = "0.3", optional = true } embedded-hal-async = "1.0.0" +bitfield = "0.17.0" [features] default = [] diff --git a/src/pdo/mod.rs b/src/pdo/mod.rs index 8b13789..60177e4 100644 --- a/src/pdo/mod.rs +++ b/src/pdo/mod.rs @@ -1 +1,110 @@ +//! Power data object (PDO) definitions +//! This module defines source and sink PDOs. Each PDO type has a corresponding *Raw and *Data struct. +//! The raw struct just provides a structured version of the raw PDO data, while the data struct provides +//! a type-safe version. +use crate::PdError; +pub mod sink; +pub mod source; + +/// 10 mA unit +const MA10_UNIT: u16 = 10; +/// 50 mV unit +const MV50_UNIT: u16 = 50; +/// 250 mV unit +const MW250_UNIT: u16 = 250; +/// 50 mA unit +const MA50_UNIT: u16 = 50; +/// 100 mV unit +const MV100_UNIT: u16 = 100; +/// 1000 mW unit +const MW1000_UNIT: u16 = 1000; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PdoKind { + Fixed, + Battery, + Variable, + Augmented, +} + +impl From for PdoKind { + fn from(pdo: u32) -> Self { + const PDO_KIND_SHIFT: u8 = 30; + PdoKind::from((pdo >> PDO_KIND_SHIFT) as u8) + } +} + +impl From for PdoKind { + fn from(value: u8) -> Self { + const PDO_KIND_MASK: u8 = 0x3; + match value & PDO_KIND_MASK { + 0x0 => PdoKind::Fixed, + 0x1 => PdoKind::Battery, + 0x2 => PdoKind::Variable, + 0x3 => PdoKind::Augmented, + _ => unreachable!(), + } + } +} + +impl From for u8 { + fn from(value: PdoKind) -> Self { + match value { + PdoKind::Fixed => 0x0, + PdoKind::Battery => 0x1, + PdoKind::Variable => 0x2, + PdoKind::Augmented => 0x3, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ApdoKind { + /// SPR Programable power supply + SprPps, + /// EPR Adjustable voltage supply + EprAvs, + /// SPR Adjustable voltage supply + SprAvs, +} + +impl Into for ApdoKind { + fn into(self) -> u8 { + match self { + ApdoKind::SprPps => 0x0, + ApdoKind::EprAvs => 0x1, + ApdoKind::SprAvs => 0x2, + } + } +} + +impl TryFrom for ApdoKind { + type Error = PdError; + + fn try_from(value: u8) -> Result { + match value { + 0x0 => Ok(ApdoKind::SprPps), + 0x1 => Ok(ApdoKind::EprAvs), + 0x2 => Ok(ApdoKind::SprAvs), + _ => Err(PdError::InvalidParams), + } + } +} + +impl TryFrom for ApdoKind { + type Error = PdError; + + fn try_from(value: u32) -> Result { + const APDO_KIND_SHIFT: u8 = 28; + const APDO_KIND_MASK: u32 = 0x3; + match (value >> APDO_KIND_SHIFT) & APDO_KIND_MASK { + 0x0 => Ok(ApdoKind::SprPps), + 0x1 => Ok(ApdoKind::EprAvs), + 0x2 => Ok(ApdoKind::SprAvs), + _ => Err(PdError::InvalidParams), + } + } +} diff --git a/src/pdo/sink.rs b/src/pdo/sink.rs new file mode 100644 index 0000000..ce93478 --- /dev/null +++ b/src/pdo/sink.rs @@ -0,0 +1,385 @@ +//! Sink PDOs as defined in USB Power Delivery specification rev 3.2 section 6.4.1.3 +use bitfield::bitfield; + +use super::*; +use crate::PdError; + +/// Sink PDO +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Pdo { + /// Fixed + Fixed(FixedData), + /// Battery supply + Battery(BatteryData), + /// Variable supply + Variable(VariableData), + /// Augmented supply + Augmented(Apdo), +} + +impl TryFrom for Pdo { + type Error = PdError; + + fn try_from(value: u32) -> Result { + match PdoKind::from(value) { + PdoKind::Fixed => FixedData::try_from(value).map(Pdo::Fixed), + PdoKind::Battery => BatteryData::try_from(value).map(Pdo::Battery), + PdoKind::Variable => VariableData::try_from(value).map(Pdo::Variable), + PdoKind::Augmented => Apdo::try_from(value).map(Pdo::Augmented), + } + } +} + +/// FRS required current +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FrsRequiredCurrent { + /// Not supported + None, + /// USB default current + Default, + /// 1.5A @ 5V + Current1A5, + /// 3A @ 5V + Current3A, +} + +impl From for FrsRequiredCurrent { + fn from(value: u8) -> Self { + const FRS_REQUIRED_CURRENT_MASK: u8 = 0x3; + match value & FRS_REQUIRED_CURRENT_MASK { + 0 => FrsRequiredCurrent::None, + 1 => FrsRequiredCurrent::Default, + 2 => FrsRequiredCurrent::Current1A5, + 3 => FrsRequiredCurrent::Current3A, + _ => unreachable!(), + } + } +} + +bitfield! { + /// Fixed PDO raw data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct FixedRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// Dual role power capable + pub u8, dual_role_power, set_dual_role_power: 29, 29; + /// Higher capability + pub u8, higher_capability, set_higher_capability: 28, 28; + /// Unconstrained power + pub u8, unconstrained_power, set_unconstrained_power: 27, 27; + /// USB comms capable + pub u8, usb_comms_capable, set_usb_comms_capable: 26, 26; + /// Dual role data capable + pub u8, dual_role_data, set_dual_role_data: 25, 25; + /// Required FRS current + pub u8, frs_required_current, set_frs_required_current: 24, 23; + /// Voltage in 50mV units + pub u16, voltage, set_voltage: 19, 10; + /// Operating current in 10mA units + pub u16, operational_current, set_operational_current: 9, 0; +} + +/// Fixed supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct FixedData { + /// Dual role power + pub dual_role_power: bool, + /// Higher capability + pub higher_capability: bool, + /// Unconstrained power + pub unconstrained_power: bool, + /// USB comms capable + pub usb_comms_capable: bool, + /// Dual role data capable + pub dual_role_data: bool, + /// FRS required current + pub frs_required_current: FrsRequiredCurrent, + /// Voltage in mV + pub voltage_mv: u16, + /// Operational current in mA + pub operational_current_ma: u16, +} + +impl TryFrom for FixedData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Fixed { + return Err(PdError::InvalidParams); + } + + let raw = FixedRaw(value); + Ok(FixedData { + dual_role_power: raw.dual_role_power() != 0, + higher_capability: raw.higher_capability() != 0, + unconstrained_power: raw.unconstrained_power() != 0, + usb_comms_capable: raw.usb_comms_capable() != 0, + dual_role_data: raw.dual_role_data() != 0, + frs_required_current: raw.frs_required_current().into(), + voltage_mv: raw.voltage() * MV50_UNIT, + operational_current_ma: raw.operational_current() * MA10_UNIT, + }) + } +} + +bitfield! { + /// Raw battery PDO data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BatteryRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// Maximum voltage in 50 mV units + pub u16, max_voltage, set_max_voltage: 29, 20; + /// Minimum voltage in 50 mV units + pub u16, min_voltage, set_min_voltage: 19, 10; + /// Operational power in 250 mW units + pub u16, operational_power, set_operational_power: 9, 0; +} + +/// Battery supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct BatteryData { + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// Operational power in mW + pub operational_power_mw: u16, +} + +impl TryFrom for BatteryData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Battery { + return Err(PdError::InvalidParams); + } + + let raw = BatteryRaw(value); + Ok(BatteryData { + max_voltage_mv: raw.max_voltage() * MV50_UNIT, + min_voltage_mv: raw.min_voltage() * MV50_UNIT, + operational_power_mw: raw.operational_power() * MW250_UNIT, + }) + } +} + +bitfield! { + /// Raw variable supply PDO data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct VariableRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// Maximum voltage in 50 mV units + pub u16, max_voltage, set_max_voltage: 29, 20; + /// Minimum voltage in 50 mV units + pub u16, min_voltage, set_min_voltage: 19, 10; + /// current in 10 mA units + pub u16, operational_current, set_operational_current: 9, 0; +} + +/// Variable supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct VariableData { + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// Operational current in mA + pub operational_current_ma: u16, +} + +impl TryFrom for VariableData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Variable { + return Err(PdError::InvalidParams); + } + + let raw = VariableRaw(value); + Ok(VariableData { + max_voltage_mv: raw.max_voltage() * MV50_UNIT, + min_voltage_mv: raw.min_voltage() * MV50_UNIT, + operational_current_ma: raw.operational_current() * MA10_UNIT, + }) + } +} + +/// Augmented PDO +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Apdo { + /// SPR Programable power supply + SprPps(SprPpsData), + /// EPR Adjustable voltage supply + EprAvs(EprAvsData), + /// SPR Adjustable voltage supply + SprAvs(SprAvsData), +} + +impl TryFrom for Apdo { + type Error = PdError; + + fn try_from(value: u32) -> Result { + match ApdoKind::try_from(value)? { + ApdoKind::SprPps => SprPpsData::try_from(value).map(Apdo::SprPps), + ApdoKind::EprAvs => EprAvsData::try_from(value).map(Apdo::EprAvs), + ApdoKind::SprAvs => SprAvsData::try_from(value).map(Apdo::SprAvs), + } + } +} + +bitfield! { + /// Raw SPR Programable power supply data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SprPpsRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// APDO kind + pub u8, apdo_kind, set_apdo_kind: 29, 28; + /// Maximum voltage in 100mV units + pub u16, max_voltage, set_max_voltage: 24, 17; + /// Minimum voltage in 100mV units + pub u16, min_voltage, set_min_voltage: 15, 8; + /// Maximum current in 50mA units + pub u16, max_current, set_max_current: 6, 0; +} + +/// ADO SPR Programable power supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SprPpsData { + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// Maximum current in mA + pub max_current_ma: u16, +} + +impl TryFrom for SprPpsData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Augmented || ApdoKind::try_from(value)? != ApdoKind::SprPps { + return Err(PdError::InvalidParams); + } + + let raw = SprPpsRaw(value); + Ok(SprPpsData { + max_voltage_mv: raw.max_voltage() * MV100_UNIT, + min_voltage_mv: raw.min_voltage() * MV100_UNIT, + max_current_ma: raw.max_current() * MA50_UNIT, + }) + } +} + +bitfield! { + /// Raw EPR Adjustable voltage supply data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct EprAvsRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// APDO kind + pub u8, apdo_kind, set_apdo_kind: 29, 28; + /// Maximum voltage in 100mV units + pub u16, max_voltage, set_max_voltage: 25, 17; + /// Minimum voltage in 100mV units + pub u16, min_voltage, set_min_voltage: 15, 8; + /// PDP in 1W units + pub u16, pdp, set_pdp: 7, 0; +} + +/// EPR Adjustable voltage supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EprAvsData { + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// PDP in mW + pub pdp_mw: u16, +} + +impl TryFrom for EprAvsData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Augmented || ApdoKind::try_from(value)? != ApdoKind::EprAvs { + return Err(PdError::InvalidParams); + } + + let raw = EprAvsRaw(value); + Ok(EprAvsData { + max_voltage_mv: raw.max_voltage() * MV100_UNIT, + min_voltage_mv: raw.min_voltage() * MV100_UNIT, + pdp_mw: raw.pdp() * MW1000_UNIT, + }) + } +} + +bitfield! { + /// Raw SPR adjustable voltage supply + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SprAvsRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// APDO kind + pub u8, apdo_kind, set_apdo_kind: 29, 28; + /// Maximum current for 9-15 V range in 10mA units + pub u16, max_current_15v, set_max_current_15v: 19, 10; + /// Maximum current for 15-20 V range in 10mA units + pub u16, max_current_20v, set_max_current_20v: 9, 0; +} + +/// SPR Adjustable voltage supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SprAvsData { + /// Maximum current for 9-15 V range in mA + pub max_current_15v_ma: u16, + /// Maximum current for 15-20 V range in mA + pub max_current_20v_ma: u16, +} + +impl TryFrom for SprAvsData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Augmented || ApdoKind::try_from(value)? != ApdoKind::SprAvs { + return Err(PdError::InvalidParams); + } + + let raw = SprAvsRaw(value); + Ok(SprAvsData { + max_current_15v_ma: raw.max_current_15v() * MA10_UNIT, + max_current_20v_ma: raw.max_current_20v() * MA10_UNIT, + }) + } +} diff --git a/src/pdo/source.rs b/src/pdo/source.rs new file mode 100644 index 0000000..7d16703 --- /dev/null +++ b/src/pdo/source.rs @@ -0,0 +1,444 @@ +//! Source PDOs as defined in USB Power Delivery specification rev 3.2 section 6.4.1.2 +use bitfield::bitfield; + +use super::*; +use crate::PdError; + +/// Power data object type +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Pdo { + /// Fixed supply + Fixed(FixedData), + /// Battery + Battery(BatteryData), + /// Variable supply + Variable(VariableData), + /// Augmented fixed supply + Augmented(Apdo), +} + +impl TryFrom for Pdo { + type Error = PdError; + + fn try_from(value: u32) -> Result { + match PdoKind::from(value) { + PdoKind::Fixed => FixedData::try_from(value).map(Pdo::Fixed), + PdoKind::Battery => BatteryData::try_from(value).map(Pdo::Battery), + PdoKind::Variable => VariableData::try_from(value).map(Pdo::Variable), + PdoKind::Augmented => Apdo::try_from(value).map(Pdo::Augmented), + } + } +} + +/// Fixed supply peak current, names based on 10 ms @ 50% duty cycle values +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PeakCurrent { + Pct100, + Pct110, + Pct125, + Pct150, +} + +const PEAK_CURRENT_MASK: u8 = 0x3; +impl From for PeakCurrent { + fn from(value: u8) -> Self { + match value & PEAK_CURRENT_MASK { + 0x0 => PeakCurrent::Pct100, + 0x1 => PeakCurrent::Pct110, + 0x2 => PeakCurrent::Pct125, + 0x3 => PeakCurrent::Pct150, + _ => unreachable!(), + } + } +} + +impl From for u8 { + fn from(value: PeakCurrent) -> Self { + match value { + PeakCurrent::Pct100 => 0x0, + PeakCurrent::Pct110 => 0x1, + PeakCurrent::Pct125 => 0x2, + PeakCurrent::Pct150 => 0x3, + } + } +} + +bitfield! { + /// Raw fixed supply PDO data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct FixedRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// Dual role power capable + pub u8, dual_role_power, set_dual_role_power: 29, 29; + /// USB suspend supported + pub u8, usb_suspend_supported, set_usb_suspend_supported: 28, 28; + /// Unconstrained power + pub u8, unconstrained_power, set_unconstrained_power: 27, 27; + /// USB comms capable + pub u8, usb_comms_capable, set_usb_comms_capable: 26, 26; + /// Dual role data capable + pub u8, dual_role_data, set_dual_role_data: 25, 25; + /// Unchunked extended messages support + pub u8, unchunked_extended_messages_support, set_unchunked_extended_messages_support: 24, 24; + /// EPR capable + pub u8, epr_capable, set_epr_capable: 23, 23; + /// Peak current + pub u8, peak_current, set_peak_current: 21, 20; + /// Voltage in 50 mV units + pub u16, voltage, set_voltage: 19, 10; + /// Peak current in 10 mA units + pub u16, current, set_current: 9, 0; +} + +/// Fixed supply PDO data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct FixedData { + /// Dual role power capable + pub dual_role_power: bool, + /// USB suspend supported + pub usb_suspend_supported: bool, + /// Unconstrained power + pub unconstrained_power: bool, + /// USB comms capable + pub usb_comms_capable: bool, + /// Dual role data capable + pub dual_role_data: bool, + /// Unchunked extended messages support + pub unchunked_extended_messages_support: bool, + /// EPR capable + pub epr_capable: bool, + /// Peak current + pub peak_current: PeakCurrent, + /// Voltage in mV + pub voltage_mv: u16, + /// Current in mA + pub current_ma: u16, +} + +impl From for FixedData { + fn from(raw: FixedRaw) -> Self { + FixedData { + dual_role_power: raw.dual_role_power() != 0, + usb_suspend_supported: raw.usb_suspend_supported() != 0, + unconstrained_power: raw.unconstrained_power() != 0, + usb_comms_capable: raw.usb_comms_capable() != 0, + dual_role_data: raw.dual_role_data() != 0, + unchunked_extended_messages_support: raw.unchunked_extended_messages_support() != 0, + epr_capable: raw.epr_capable() != 0, + peak_current: raw.peak_current().into(), + voltage_mv: raw.voltage() * MV50_UNIT, + current_ma: raw.current() * MA10_UNIT, + } + } +} + +impl TryFrom for FixedData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Fixed { + return Err(PdError::InvalidParams); + } + Ok(FixedRaw(value).into()) + } +} + +bitfield! { + /// Raw battery PDO data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BatteryRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// Maximum voltage in 50 mV units + pub u16, max_voltage, set_max_voltage: 29, 20; + /// Minimum voltage in 50 mV units + pub u16, min_voltage, set_min_voltage: 19, 10; + /// Maximum power in 250 mW units + pub u16, max_power, set_max_power: 9, 0; +} + +/// Battery PDO data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct BatteryData { + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// Maximum power in mW + pub max_power_mw: u16, +} + +impl From for BatteryData { + fn from(raw: BatteryRaw) -> Self { + BatteryData { + max_voltage_mv: raw.max_voltage() * MV50_UNIT, + min_voltage_mv: raw.min_voltage() * MV50_UNIT, + max_power_mw: raw.max_power() * MW250_UNIT, + } + } +} + +impl TryFrom for BatteryData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Battery { + return Err(PdError::InvalidParams); + } + Ok(BatteryRaw(value).into()) + } +} + +bitfield! { + /// Raw variable supply PDO data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct VariableRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// Maximum voltage in 50 mV units + pub u16, max_voltage, set_max_voltage: 29, 20; + /// Minimum voltage in 50 mV units + pub u16, min_voltage, set_min_voltage: 19, 10; + /// Maximum current in 10 mA units + pub u16, max_current, set_max_current: 9, 0; +} + +/// Variable supply PDO data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct VariableData { + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// Maximum current in mA + pub max_current_ma: u16, +} + +impl From for VariableData { + fn from(raw: VariableRaw) -> Self { + VariableData { + max_voltage_mv: raw.max_voltage() * MV50_UNIT, + min_voltage_mv: raw.min_voltage() * MA50_UNIT, + max_current_ma: raw.max_current() * MA10_UNIT, + } + } +} + +impl TryFrom for VariableData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Variable { + return Err(PdError::InvalidParams); + } + Ok(VariableRaw(value).into()) + } +} + +/// Augmented PDO +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Apdo { + /// SPR Programable power supply + SprPps(SprPpsData), + /// EPR Adjustable voltage supply + EprAvs(EprAvsData), + /// SPR Adjustable voltage supply + SprAvs(SprAvsData), +} + +impl TryFrom for Apdo { + type Error = PdError; + + fn try_from(value: u32) -> Result { + match ApdoKind::try_from(value)? { + ApdoKind::SprPps => SprPpsData::try_from(value).map(Apdo::SprPps), + ApdoKind::EprAvs => EprAvsData::try_from(value).map(Apdo::EprAvs), + ApdoKind::SprAvs => SprAvsData::try_from(value).map(Apdo::SprAvs), + } + } +} + +bitfield! { + /// Raw SPR Programable power supply data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SprPpsRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// APDO kind + pub u8, apdo_kind, set_apdo_kind: 29, 28; + /// PPS power limited + pub u8, pps_power_limited, set_pps_power_limited: 27, 27; + /// Maximum voltage in 100mV units + pub u16, max_voltage, set_max_voltage: 24, 17; + /// Minimum voltage in 100mV units + pub u16, min_voltage, set_min_voltage: 15, 8; + /// Maximum current in 50mA units + pub u16, max_current, set_max_current: 6, 0; +} + +/// SPR Programable power supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SprPpsData { + /// PPS power limited + pub pps_power_limited: bool, + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// Maximum current in mA + pub max_current_ma: u16, +} + +impl From for SprPpsData { + fn from(raw: SprPpsRaw) -> Self { + SprPpsData { + pps_power_limited: raw.pps_power_limited() != 0, + max_voltage_mv: raw.max_voltage() * MV100_UNIT, + min_voltage_mv: raw.min_voltage() * MV100_UNIT, + max_current_ma: raw.max_current() * MA50_UNIT, + } + } +} + +impl TryFrom for SprPpsData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Augmented || ApdoKind::try_from(value)? != ApdoKind::SprPps { + return Err(PdError::InvalidParams); + } + + Ok(SprPpsRaw(value).into()) + } +} + +bitfield! { + /// Raw EPR adjustable voltage supply data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct EprAvsRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// APDO kind + pub u8, apdo_kind, set_apdo_kind: 29, 28; + /// Peak current + pub u8, peak_current, set_peak_current: 27, 26; + /// Maximum voltage in 100mV units + pub u16, max_voltage, set_max_voltage: 25, 17; + /// Minimum voltage in 100mV units + pub u16, min_voltage, set_min_voltage: 15, 8; + /// PDP in 1W units + pub u16, pdp, set_pdp: 7, 0; +} + +/// EPR Adjustable voltage supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EprAvsData { + /// Peak current + pub peak_current: PeakCurrent, + /// Maximum voltage in mV + pub max_voltage_mv: u16, + /// Minimum voltage in mV + pub min_voltage_mv: u16, + /// PDP in mW + pub pdp_mw: u16, +} + +impl From for EprAvsData { + fn from(raw: EprAvsRaw) -> Self { + EprAvsData { + peak_current: raw.peak_current().into(), + max_voltage_mv: raw.max_voltage() * MV100_UNIT, + min_voltage_mv: raw.min_voltage() * MV100_UNIT, + pdp_mw: raw.pdp() * MW1000_UNIT, + } + } +} + +impl TryFrom for EprAvsData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Augmented || ApdoKind::try_from(value)? != ApdoKind::EprAvs { + return Err(PdError::InvalidParams); + } + + Ok(EprAvsRaw(value).into()) + } +} + +bitfield! { + /// Raw SPR adjustable voltage supply data + #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SprAvsRaw(u32); + impl Debug; + + /// PDO kind + pub u8, kind, set_kind: 31, 30; + /// APDO kind + pub u8, apdo_kind, set_apdo_kind: 29, 28; + /// Peak current + pub u8, peak_current, set_peak_current: 27, 26; + /// Maximum current for 9-15 V range in 10mA units + pub u16, max_current_15v, set_max_current_15v: 19, 10; + /// Maximum current for 15-20 V range in 10mA units + pub u16, max_current_20v, set_max_current_20v: 9, 0; +} + +/// SPR Adjustable voltage supply data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SprAvsData { + /// Peak current + pub peak_current: PeakCurrent, + /// Maximum current for 9-15 V range in mA + pub max_current_15v_ma: u16, + /// Maximum current for 15-20 V range in mA + pub max_current_20v_ma: u16, +} + +impl From for SprAvsData { + fn from(raw: SprAvsRaw) -> Self { + SprAvsData { + peak_current: raw.peak_current().into(), + max_current_15v_ma: raw.max_current_15v() * MA10_UNIT, + max_current_20v_ma: raw.max_current_20v() * MA10_UNIT, + } + } +} + +impl TryFrom for SprAvsData { + type Error = PdError; + + fn try_from(value: u32) -> Result { + if PdoKind::from(value) != PdoKind::Augmented || ApdoKind::try_from(value)? != ApdoKind::SprAvs { + return Err(PdError::InvalidParams); + } + + Ok(SprAvsRaw(value).into()) + } +}