1use byteorder::{ByteOrder, LittleEndian};
3use proc_bitfield::bitfield;
4use uom::si::electric_current::{self, centiampere};
5use uom::si::{self};
6
7use super::_20millivolts_mod::_20millivolts;
8use super::_50milliamperes_mod::_50milliamperes;
9use super::_250milliwatts_mod::_250milliwatts;
10use super::pdo;
11use super::units::{ElectricCurrent, ElectricPotential};
12
13bitfield! {
14 #[derive(Clone, Copy, PartialEq, Eq)]
15 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
16 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17 pub struct RawDataObject(pub u32): Debug, FromStorage, IntoStorage {
18 pub object_position: u8 @ 28..=31,
20 }
21}
22
23bitfield! {
24 #[derive(Clone, Copy, PartialEq, Eq)]
25 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
26 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27 pub struct FixedVariableSupply(pub u32): Debug, FromStorage, IntoStorage {
28 pub object_position: u8 @ 28..=31,
30 pub giveback_flag: bool @ 27,
31 pub capability_mismatch: bool @ 26,
32 pub usb_communications_capable: bool @ 25,
33 pub no_usb_suspend: bool @ 24,
34 pub unchunked_extended_messages_supported: bool @ 23,
35 pub epr_mode_capable: bool @ 22,
36 pub raw_operating_current: u16 @ 10..=19,
37 pub raw_max_operating_current: u16 @ 0..=9,
38 }
39}
40
41impl FixedVariableSupply {
42 pub fn to_bytes(self, buf: &mut [u8]) -> usize {
43 LittleEndian::write_u32(buf, self.0);
44 4
45 }
46
47 pub fn operating_current(&self) -> ElectricCurrent {
48 ElectricCurrent::new::<centiampere>(self.raw_operating_current().into())
49 }
50
51 pub fn max_operating_current(&self) -> ElectricCurrent {
52 ElectricCurrent::new::<centiampere>(self.raw_max_operating_current().into())
53 }
54}
55
56bitfield! {
57 #[derive(Clone, Copy, PartialEq, Eq)]
58 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
59 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60 pub struct Battery(pub u32): Debug, FromStorage, IntoStorage {
61 pub object_position: u8 @ 28..=31,
63 pub giveback_flag: bool @ 27,
65 pub capability_mismatch: bool @ 26,
67 pub usb_communications_capable: bool @ 25,
69 pub no_usb_suspend: bool @ 24,
71 pub unchunked_extended_messages_supported: bool @ 23,
73 pub epr_mode_capable: bool @ 22,
75 pub raw_operating_power: u16 @ 10..=19,
77 pub raw_max_operating_power: u16 @ 0..=9,
79 }
80}
81
82impl Battery {
83 pub fn to_bytes(self, buf: &mut [u8]) {
84 LittleEndian::write_u32(buf, self.0);
85 }
86
87 pub fn operating_power(&self) -> si::u32::Power {
88 si::u32::Power::new::<_250milliwatts>(self.raw_operating_power().into())
89 }
90
91 pub fn max_operating_power(&self) -> si::u32::Power {
92 si::u32::Power::new::<_250milliwatts>(self.raw_max_operating_power().into())
93 }
94}
95
96bitfield!(
97 #[derive(Clone, Copy, PartialEq, Eq)]
98 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
99 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100 pub struct Pps(pub u32): Debug, FromStorage, IntoStorage {
101 pub object_position: u8 @ 28..=31,
103 pub capability_mismatch: bool @ 26,
105 pub usb_communications_capable: bool @ 25,
107 pub no_usb_suspend: bool @ 24,
109 pub unchunked_extended_messages_supported: bool @ 23,
111 pub epr_mode_capable: bool @ 22,
113 pub raw_output_voltage: u16 @ 9..=20,
115 pub raw_operating_current: u16 @ 0..=6,
117 }
118);
119
120impl Pps {
121 pub fn to_bytes(self, buf: &mut [u8]) -> usize {
122 LittleEndian::write_u32(buf, self.0);
123 4
124 }
125
126 pub fn output_voltage(&self) -> ElectricPotential {
127 ElectricPotential::new::<_20millivolts>(self.raw_output_voltage().into())
128 }
129
130 pub fn operating_current(&self) -> ElectricCurrent {
131 ElectricCurrent::new::<_50milliamperes>(self.raw_operating_current().into())
132 }
133}
134
135bitfield!(
136 #[derive(Clone, Copy, PartialEq, Eq)]
137 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
138 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
139 pub struct Avs(pub u32): Debug, FromStorage, IntoStorage {
140 pub object_position: u8 @ 28..=31,
142 pub capability_mismatch: bool @ 26,
144 pub usb_communications_capable: bool @ 25,
146 pub no_usb_suspend: bool @ 24,
148 pub unchunked_extended_messages_supported: bool @ 23,
150 pub epr_mode_capable: bool @ 22,
152 pub raw_output_voltage: u16 @ 9..=20,
154 pub raw_operating_current: u16 @ 0..=6,
156 }
157);
158
159impl Avs {
160 pub fn to_bytes(self, buf: &mut [u8]) {
161 LittleEndian::write_u32(buf, self.0);
162 }
163
164 pub fn output_voltage(&self) -> ElectricPotential {
165 ElectricPotential::new::<_20millivolts>(self.raw_output_voltage().into())
166 }
167
168 pub fn operating_current(&self) -> ElectricCurrent {
169 ElectricCurrent::new::<_50milliamperes>(self.raw_operating_current().into())
170 }
171}
172
173#[derive(Debug, Clone, Copy)]
175#[cfg_attr(feature = "defmt", derive(defmt::Format))]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177#[allow(unused)] pub enum PowerSource {
179 FixedVariableSupply(FixedVariableSupply),
180 Battery(Battery),
181 Pps(Pps),
182 Avs(Avs),
183 Unknown(RawDataObject),
184}
185
186#[derive(Debug)]
188#[non_exhaustive]
189pub enum Error {
190 VoltageMismatch,
192}
193
194#[derive(Debug)]
196pub enum VoltageRequest {
197 Safe5V,
199 Highest,
201 Specific(ElectricPotential),
203}
204
205#[derive(Debug)]
207pub enum CurrentRequest {
208 Highest,
210 Specific(ElectricCurrent),
212}
213
214pub struct IndexedFixedSupply<'d>(pub &'d pdo::FixedSupply, usize);
216
217pub struct IndexedAugmented<'d>(pub &'d pdo::Augmented, usize);
219
220impl PowerSource {
221 pub fn object_position(&self) -> u8 {
222 match self {
223 PowerSource::FixedVariableSupply(p) => p.object_position(),
224 PowerSource::Battery(p) => p.object_position(),
225 PowerSource::Pps(p) => p.object_position(),
226 PowerSource::Avs(p) => p.object_position(),
227 PowerSource::Unknown(p) => p.object_position(),
228 }
229 }
230
231 pub fn find_highest_fixed_voltage(source_capabilities: &pdo::SourceCapabilities) -> Option<IndexedFixedSupply<'_>> {
235 let mut selected_pdo = None;
236
237 for (index, cap) in source_capabilities.pdos().iter().enumerate() {
238 if let pdo::PowerDataObject::FixedSupply(fixed_supply) = cap {
239 selected_pdo = match selected_pdo {
240 None => Some(IndexedFixedSupply(fixed_supply, index)),
241 Some(ref x) => {
242 if fixed_supply.voltage() > x.0.voltage() {
243 Some(IndexedFixedSupply(fixed_supply, index))
244 } else {
245 selected_pdo
246 }
247 }
248 };
249 }
250 }
251
252 selected_pdo
253 }
254
255 pub fn find_specific_fixed_voltage(
259 source_capabilities: &pdo::SourceCapabilities,
260 voltage: ElectricPotential,
261 ) -> Option<IndexedFixedSupply<'_>> {
262 for (index, cap) in source_capabilities.pdos().iter().enumerate() {
263 if let pdo::PowerDataObject::FixedSupply(fixed_supply) = cap
264 && (fixed_supply.voltage() == voltage)
265 {
266 return Some(IndexedFixedSupply(fixed_supply, index));
267 }
268 }
269
270 None
271 }
272
273 pub fn find_pps_voltage(
278 source_capabilities: &pdo::SourceCapabilities,
279 voltage: ElectricPotential,
280 ) -> Option<IndexedAugmented<'_>> {
281 for (index, cap) in source_capabilities.pdos().iter().enumerate() {
282 let pdo::PowerDataObject::Augmented(augmented) = cap else {
283 trace!("Skip non-augmented PDO {:?}", cap);
284 continue;
285 };
286
287 match augmented {
289 pdo::Augmented::Spr(spr) => {
290 if spr.min_voltage() <= voltage && spr.max_voltage() >= voltage {
291 return Some(IndexedAugmented(augmented, index));
292 } else {
293 trace!("Skip PDO, voltage out of range. {:?}", augmented);
294 }
295 }
296 _ => trace!("Skip PDO, only SPR is supported. {:?}", augmented),
297 };
298 }
299
300 trace!("Could not find suitable PPS voltage");
301 None
302 }
303
304 pub fn new_fixed_specific(supply: IndexedFixedSupply, current_request: CurrentRequest) -> Result<Self, Error> {
311 let IndexedFixedSupply(pdo, index) = supply;
312
313 let (current, mismatch) = match current_request {
314 CurrentRequest::Highest => (pdo.max_current(), false),
315 CurrentRequest::Specific(x) => (x, x > pdo.max_current()),
316 };
317
318 let mut raw_current = current.get::<electric_current::centiampere>() as u16;
319
320 if raw_current > 0x3ff {
321 error!("Clamping invalid current: {} mA", 10 * raw_current);
322 raw_current = 0x3ff;
323 }
324
325 let object_position = index + 1;
326 assert!(object_position > 0b0000 && object_position <= 0b1110);
327
328 Ok(Self::FixedVariableSupply(
329 FixedVariableSupply(0)
330 .with_raw_operating_current(raw_current)
331 .with_raw_max_operating_current(raw_current)
332 .with_object_position(object_position as u8)
333 .with_capability_mismatch(mismatch)
334 .with_no_usb_suspend(true)
335 .with_usb_communications_capable(true), ))
337 }
338
339 pub fn new_fixed(
343 current_request: CurrentRequest,
344 voltage_request: VoltageRequest,
345 source_capabilities: &pdo::SourceCapabilities,
346 ) -> Result<Self, Error> {
347 let selected = match voltage_request {
348 VoltageRequest::Safe5V => source_capabilities
349 .vsafe_5v()
350 .map(|supply| IndexedFixedSupply(supply, 0)),
351 VoltageRequest::Highest => Self::find_highest_fixed_voltage(source_capabilities),
352 VoltageRequest::Specific(x) => Self::find_specific_fixed_voltage(source_capabilities, x),
353 };
354
355 if selected.is_none() {
356 return Err(Error::VoltageMismatch);
357 }
358
359 Self::new_fixed_specific(selected.unwrap(), current_request)
360 }
361
362 pub fn new_pps(
367 current_request: CurrentRequest,
368 voltage: ElectricPotential,
369 source_capabilities: &pdo::SourceCapabilities,
370 ) -> Result<Self, Error> {
371 let selected = Self::find_pps_voltage(source_capabilities, voltage);
372
373 if selected.is_none() {
374 return Err(Error::VoltageMismatch);
375 }
376
377 let IndexedAugmented(pdo, index) = selected.unwrap();
378 let max_current = match pdo {
379 pdo::Augmented::Spr(spr) => spr.max_current(),
380 _ => unreachable!(),
381 };
382
383 let (current, mismatch) = match current_request {
384 CurrentRequest::Highest => (max_current, false),
385 CurrentRequest::Specific(x) => (x, x > max_current),
386 };
387
388 let mut raw_current = current.get::<_50milliamperes>() as u16;
389
390 if raw_current > 0x3ff {
391 error!("Clamping invalid current: {} mA", 10 * raw_current);
392 raw_current = 0x3ff;
393 }
394
395 let raw_voltage = voltage.get::<_20millivolts>() as u16;
396
397 let object_position = index + 1;
398 assert!(object_position > 0b0000 && object_position <= 0b1110);
399
400 Ok(Self::Pps(
401 Pps(0)
402 .with_raw_output_voltage(raw_voltage)
403 .with_raw_operating_current(raw_current)
404 .with_object_position(object_position as u8)
405 .with_capability_mismatch(mismatch)
406 .with_no_usb_suspend(true)
407 .with_usb_communications_capable(true),
408 ))
409 }
410}