use heapless::Vec;
use proc_bitfield::bitfield;
use uom::si::electric_current::centiampere;
use uom::si::electric_potential::{decivolt, volt};
use uom::si::power::watt;
use super::PdoKind;
use crate::_50milliamperes_mod::_50milliamperes;
use crate::_50millivolts_mod::_50millivolts;
use crate::_250milliwatts_mod::_250milliwatts;
use crate::units::{ElectricCurrent, ElectricPotential, Power};
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Kind {
FixedSupply,
Battery,
VariableSupply,
Pps,
Avs,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PowerDataObject {
FixedSupply(FixedSupply),
Battery(Battery),
VariableSupply(VariableSupply),
Augmented(Augmented),
Unknown(RawPowerDataObject),
}
impl PowerDataObject {
pub fn is_zero_padding(&self) -> bool {
(match self {
PowerDataObject::FixedSupply(f) => f.0,
PowerDataObject::Battery(b) => b.0,
PowerDataObject::VariableSupply(v) => v.0,
PowerDataObject::Augmented(a) => match a {
Augmented::Spr(s) => s.0,
Augmented::Epr(e) => e.0,
Augmented::Unknown(u) => *u,
},
PowerDataObject::Unknown(u) => u.0,
}) == 0
}
}
bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RawPowerDataObject(pub u32): Debug, FromStorage, IntoStorage {
pub kind: u8 @ 30..=31,
}
}
bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FixedSupply(pub u32): Debug, FromStorage, IntoStorage {
pub kind: u8 @ 30..=31,
pub dual_role_power: bool @ 29,
pub usb_suspend_supported: bool @ 28,
pub unconstrained_power: bool @ 27,
pub usb_communications_capable: bool @ 26,
pub dual_role_data: bool @ 25,
pub unchunked_extended_messages_supported: bool @ 24,
pub epr_mode_capable: bool @ 23,
pub peak_current: u8 @ 20..=21,
pub raw_voltage: u16 @ 10..=19,
pub raw_max_current: u16 @ 0..=9,
}
}
#[allow(clippy::derivable_impls)]
impl Default for FixedSupply {
fn default() -> Self {
Self(0)
}
}
impl FixedSupply {
pub fn voltage(&self) -> ElectricPotential {
ElectricPotential::new::<_50millivolts>(self.raw_voltage().into())
}
pub fn max_current(&self) -> ElectricCurrent {
ElectricCurrent::new::<centiampere>(self.raw_max_current().into())
}
}
bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Battery(pub u32): Debug, FromStorage, IntoStorage {
pub kind: u8 @ 30..=31,
pub raw_max_voltage: u16 @ 20..=29,
pub raw_min_voltage: u16 @ 10..=19,
pub raw_max_power: u16 @ 0..=9,
}
}
impl Battery {
pub fn max_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<_50millivolts>(self.raw_max_voltage().into())
}
pub fn min_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<_50millivolts>(self.raw_min_voltage().into())
}
pub fn max_power(&self) -> Power {
Power::new::<_250milliwatts>(self.raw_max_power().into())
}
}
bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VariableSupply(pub u32): Debug, FromStorage, IntoStorage {
pub kind: u8 @ 30..=31,
pub raw_max_voltage: u16 @ 20..=29,
pub raw_min_voltage: u16 @ 10..=19,
pub raw_max_current: u16 @ 0..=9,
}
}
impl VariableSupply {
pub fn max_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<_50millivolts>(self.raw_max_voltage().into())
}
pub fn min_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<_50millivolts>(self.raw_min_voltage().into())
}
pub fn max_current(&self) -> ElectricCurrent {
ElectricCurrent::new::<centiampere>(self.raw_max_current().into())
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Augmented {
Spr(SprProgrammablePowerSupply),
Epr(EprAdjustableVoltageSupply),
Unknown(u32),
}
bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AugmentedRaw(pub u32): Debug, FromStorage, IntoStorage {
pub kind: u8 @ 30..=31,
pub supply: u8 @ 28..=29,
pub power_capabilities: u32 @ 0..=27,
}
}
bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SprProgrammablePowerSupply(pub u32): Debug, FromStorage, IntoStorage {
pub kind: u8 @ 30..=31,
pub supply: u8 @ 28..=29,
pub pps_power_limited: bool @ 27,
pub raw_max_voltage: u8 @ 17..=24,
pub raw_min_voltage: u8 @ 8..=15,
pub raw_max_current: u8 @ 0..=6,
}
}
impl Default for SprProgrammablePowerSupply {
fn default() -> Self {
Self(0).with_kind(0b11).with_supply(0b00)
}
}
impl SprProgrammablePowerSupply {
pub fn max_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<decivolt>(self.raw_max_voltage().into())
}
pub fn min_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<decivolt>(self.raw_min_voltage().into())
}
pub fn max_current(&self) -> ElectricCurrent {
ElectricCurrent::new::<_50milliamperes>(self.raw_max_current().into())
}
}
bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EprAdjustableVoltageSupply(pub u32): Debug, FromStorage, IntoStorage {
pub kind: u8 @ 30..=31,
pub supply: u8 @ 28..=29,
pub peak_current: u8 @ 26..=27,
pub raw_max_voltage: u16 @ 17..=25,
pub raw_min_voltage: u8 @ 8..=15,
pub raw_pd_power: u8 @ 0..=7,
}
}
impl EprAdjustableVoltageSupply {
pub fn max_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<decivolt>(self.raw_max_voltage().into())
}
pub fn min_voltage(&self) -> ElectricPotential {
ElectricPotential::new::<decivolt>(self.raw_min_voltage().into())
}
pub fn pd_power(&self) -> Power {
Power::new::<watt>(self.raw_pd_power().into())
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SourceCapabilities(pub(crate) Vec<PowerDataObject, 16>);
impl SourceCapabilities {
pub fn vsafe_5v(&self) -> Option<&FixedSupply> {
self.0.first().and_then(|supply| {
if let PowerDataObject::FixedSupply(supply) = supply {
Some(supply)
} else {
None
}
})
}
pub fn dual_role_power(&self) -> bool {
self.vsafe_5v().map(FixedSupply::dual_role_power).unwrap_or_default()
}
pub fn usb_suspend_supported(&self) -> bool {
self.vsafe_5v()
.map(FixedSupply::usb_suspend_supported)
.unwrap_or_default()
}
pub fn unconstrained_power(&self) -> bool {
self.vsafe_5v()
.map(FixedSupply::unconstrained_power)
.unwrap_or_default()
}
pub fn dual_role_data(&self) -> bool {
self.vsafe_5v().map(FixedSupply::dual_role_data).unwrap_or_default()
}
pub fn unchunked_extended_messages_supported(&self) -> bool {
self.vsafe_5v()
.map(FixedSupply::unchunked_extended_messages_supported)
.unwrap_or_default()
}
pub fn epr_mode_capable(&self) -> bool {
self.vsafe_5v().map(FixedSupply::epr_mode_capable).unwrap_or_default()
}
pub fn pdos(&self) -> &[PowerDataObject] {
&self.0
}
pub fn is_epr_capabilities(&self) -> bool {
self.0.len() > 7
}
pub fn spr_pdos(&self) -> impl Iterator<Item = (u8, &PowerDataObject)> {
self.0
.iter()
.take(7)
.enumerate()
.filter(|(_, pdo)| !pdo.is_zero_padding())
.map(|(i, pdo)| ((i + 1) as u8, pdo))
}
pub fn epr_pdos(&self) -> impl Iterator<Item = (u8, &PowerDataObject)> {
self.0.iter().skip(7).enumerate().map(|(i, pdo)| ((i + 8) as u8, pdo))
}
pub fn has_epr_pdo_in_spr_positions(&self) -> bool {
let max_spr_voltage = ElectricPotential::new::<volt>(20);
self.0.iter().take(7).any(|pdo| match pdo {
PowerDataObject::FixedSupply(f) => f.voltage() > max_spr_voltage,
PowerDataObject::Augmented(Augmented::Epr(_)) => true,
_ => false,
})
}
}
impl PdoKind for SourceCapabilities {
fn at_object_position(&self, position: u8) -> Option<Kind> {
self.pdos()
.get(position.saturating_sub(1) as usize)
.and_then(|pdo| match pdo {
PowerDataObject::FixedSupply(_) => Some(Kind::FixedSupply),
PowerDataObject::Battery(_) => Some(Kind::Battery),
PowerDataObject::VariableSupply(_) => Some(Kind::VariableSupply),
PowerDataObject::Augmented(augmented) => match augmented {
Augmented::Spr(_) => Some(Kind::Pps),
Augmented::Epr(_) => Some(Kind::Avs),
Augmented::Unknown(_) => None,
},
PowerDataObject::Unknown(_) => None,
})
}
}
impl PdoKind for Option<SourceCapabilities> {
fn at_object_position(&self, position: u8) -> Option<Kind> {
self.as_ref().at_object_position(position)
}
}
impl PdoKind for Option<&SourceCapabilities> {
fn at_object_position(&self, position: u8) -> Option<Kind> {
self.and_then(|s| s.at_object_position(position))
}
}
pub fn parse_raw_pdo(raw: u32) -> PowerDataObject {
let pdo = RawPowerDataObject(raw);
match pdo.kind() {
0b00 => PowerDataObject::FixedSupply(FixedSupply(raw)),
0b01 => PowerDataObject::Battery(Battery(raw)),
0b10 => PowerDataObject::VariableSupply(VariableSupply(raw)),
0b11 => PowerDataObject::Augmented(match AugmentedRaw(raw).supply() {
0b00 => Augmented::Spr(SprProgrammablePowerSupply(raw)),
0b01 => Augmented::Epr(EprAdjustableVoltageSupply(raw)),
x => {
warn!("Unknown AugmentedPowerDataObject supply {}", x);
Augmented::Unknown(raw)
}
}),
_ => {
warn!("Unknown PowerDataObject kind");
PowerDataObject::Unknown(pdo)
}
}
}