1use crate::{Status, battery::BatteryStatus, ids::prop, pui, sint, uint};
3
4pub const KEYS: [u32; 13] = [
5 prop::BATTERY_CURRENT,
6 prop::BATTERY_REMAINING_CAPACITY,
7 prop::BATTERY_FULL_CAPACITY,
8 prop::BATTERY_DESIGN_CAPACITY,
9 prop::BATTERY_EXT_POWER_PRESENT,
10 prop::BATTERY_PRESENT,
11 prop::BATTERY_GAUGE_FULL,
12 prop::BATTERY_GAUGE_INITIALIZED,
13 prop::BATTERY_GAUGE_SMOOTHING,
14 prop::BATTERY_CHARGE_VOLTAGE_REQUEST,
15 prop::BATTERY_GAUGE_FORMAT,
16 prop::BATTERY_GAUGE_STATUS,
17 prop::BATTERY_GAUGE_OPERATION_STATUS,
18];
19pub const BQ27220: u32 = 1;
20
21pub const fn unit(key: u32) -> Option<&'static str> {
22 match key {
23 prop::BATTERY_CURRENT => Some("mA"),
24 prop::BATTERY_REMAINING_CAPACITY..=prop::BATTERY_DESIGN_CAPACITY => Some("mAh"),
25 prop::BATTERY_CHARGE_VOLTAGE_REQUEST => Some("mV"),
26 _ => None,
27 }
28}
29
30impl core::fmt::Display for Value {
31 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
32 match self {
33 Self::Current(v) => write!(f, "{v}"),
34 Self::Unsigned(v) | Self::Format(v) => write!(f, "{v}"),
35 Self::Bool(v) => write!(f, "{v}"),
36 Self::Voltage(VoltageRequest::Millivolts(v)) => write!(f, "{v}"),
37 Self::Voltage(VoltageRequest::Maximum) => f.write_str("maximum"),
38 }
39 }
40}
41
42pub const fn index(key: u32) -> Option<usize> {
43 if key >= prop::BATTERY_CURRENT && key <= prop::BATTERY_GAUGE_OPERATION_STATUS {
44 Some((key - prop::BATTERY_CURRENT) as usize)
45 } else {
46 None
47 }
48}
49
50#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
52pub struct Fields(u16);
53impl Fields {
54 pub const NONE: Self = Self(0);
55 pub const SNAPSHOT: Self = Self(1);
56 pub const DIAGNOSTICS: Self = Self(0x3ffe);
57 pub const ALL: Self = Self(0x3fff);
58 pub const GAUGE_CONFIG: Self = Self(0x4000);
60 pub const GAUGE_TELEMETRY: Self = Self(0x8000);
62 pub const fn for_key(key: u32) -> Self {
63 if key == prop::BATTERY {
64 Self::SNAPSHOT
65 } else if key == prop::BATTERY_GAUGE_CONFIG {
66 Self::GAUGE_CONFIG
67 } else if key == prop::BATTERY_GAUGE_TELEMETRY {
68 Self::GAUGE_TELEMETRY
69 } else if let Some(index) = index(key) {
70 Self(1 << (index + 1))
71 } else {
72 Self::NONE
73 }
74 }
75 pub const fn contains(self, key: u32) -> bool {
76 self.0 & Self::for_key(key).0 != 0
77 }
78 pub const fn union(self, other: Self) -> Self {
79 Self(self.0 | other.0)
80 }
81 pub const fn intersection(self, other: Self) -> Self {
82 Self(self.0 & other.0)
83 }
84 pub const fn is_empty(self) -> bool {
85 self.0 == 0
86 }
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub enum VoltageRequest {
91 Millivolts(u32),
92 Maximum,
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub enum Value {
97 Current(i32),
98 Unsigned(u32),
99 Bool(bool),
100 Voltage(VoltageRequest),
101 Format(u32),
102}
103
104impl Value {
105 pub fn decode(key: u32, bytes: &[u8]) -> Result<Option<Self>, Status> {
107 if index(key).is_none() {
108 return Err(Status::PROP_NOT_FOUND);
109 }
110 if bytes.is_empty() {
111 return Ok(None);
112 }
113 let bad = || Status::PARSE_ERROR;
114 Ok(Some(match key {
115 prop::BATTERY_CURRENT => Self::Current(sint::decode(bytes).map_err(|_| bad())?),
116 prop::BATTERY_EXT_POWER_PRESENT..=prop::BATTERY_GAUGE_SMOOTHING => {
117 Self::Bool(match bytes {
118 [0] => false,
119 [1] => true,
120 _ => return Err(bad()),
121 })
122 }
123 prop::BATTERY_CHARGE_VOLTAGE_REQUEST => {
124 let (tag, used) = pui::decode(bytes).map_err(|_| bad())?;
125 Self::Voltage(match (tag, &bytes[used..]) {
126 (0, rest) => VoltageRequest::Millivolts(uint::decode(rest).map_err(|_| bad())?),
127 (1, []) => VoltageRequest::Maximum,
128 _ => return Err(bad()),
129 })
130 }
131 prop::BATTERY_GAUGE_FORMAT => {
132 let (format, used) = pui::decode(bytes).map_err(|_| bad())?;
133 if format == 0 || used != bytes.len() {
134 return Err(bad());
135 }
136 Self::Format(format)
137 }
138 _ => Self::Unsigned(uint::decode(bytes).map_err(|_| bad())?),
139 }))
140 }
141
142 pub fn encode(self, key: u32, out: &mut [u8]) -> Result<usize, Status> {
143 let len = match self {
144 Self::Current(value) => sint::encode(value, out).map_err(|_| Status::NOMEM)?,
145 Self::Unsigned(value) => uint::encode(value, out).map_err(|_| Status::NOMEM)?,
146 Self::Bool(value) => {
147 *out.first_mut().ok_or(Status::NOMEM)? = u8::from(value);
148 1
149 }
150 Self::Format(value) => pui::encode(value, out).map_err(|_| Status::NOMEM)?,
151 Self::Voltage(value) => {
152 *out.first_mut().ok_or(Status::NOMEM)? = match value {
153 VoltageRequest::Millivolts(_) => 0,
154 VoltageRequest::Maximum => 1,
155 };
156 match value {
157 VoltageRequest::Millivolts(mv) => {
158 1 + uint::encode(mv, &mut out[1..]).map_err(|_| Status::NOMEM)?
159 }
160 VoltageRequest::Maximum => 1,
161 }
162 }
163 };
164 if Self::decode(key, &out[..len]) != Ok(Some(self)) {
165 return Err(Status::FAILURE);
166 }
167 Ok(len)
168 }
169}
170
171#[derive(Clone, Copy, Debug)]
173pub struct Sample {
174 pub snapshot: Result<BatteryStatus, ()>,
175 pub gauge_config: Result<crate::battery_gauge_config::Config, Status>,
176 pub gauge_telemetry: Result<crate::battery_gauge_telemetry::Telemetry, Status>,
177 values: [Result<Option<Value>, Status>; 13],
178}
179impl Default for Sample {
180 fn default() -> Self {
181 Self {
182 snapshot: Err(()),
183 gauge_config: Err(Status::FAILURE),
184 gauge_telemetry: Err(Status::FAILURE),
185 values: [Err(Status::FAILURE); 13],
186 }
187 }
188}
189impl Sample {
190 pub fn set(&mut self, key: u32, value: Result<Option<Value>, Status>) {
191 if let Some(i) = index(key) {
192 self.values[i] = value;
193 }
194 }
195 pub fn get(&self, key: u32) -> Result<Option<Value>, Status> {
196 self.values[index(key).ok_or(Status::PROP_NOT_FOUND)?]
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 #[test]
204 fn complete_reply_matches_the_documented_compact_budget() {
205 let values = [
206 Value::Current(-100),
207 Value::Unsigned(1500),
208 Value::Unsigned(1500),
209 Value::Unsigned(1500),
210 Value::Bool(true),
211 Value::Bool(true),
212 Value::Bool(true),
213 Value::Bool(true),
214 Value::Bool(false),
215 Value::Voltage(VoltageRequest::Millivolts(4200)),
216 Value::Format(1),
217 Value::Unsigned(0x4268),
218 Value::Unsigned(0xa6),
219 ];
220 let mut bytes = [0; 100];
221 let mut reply = crate::frame::prop_are(&mut bytes, 1).unwrap();
222 reply
223 .write_entry(prop::BATTERY, &[7, 0x68, 0x10, 100, 0])
224 .unwrap();
225 for (key, value) in KEYS.into_iter().zip(values) {
226 let mut encoded = [0; 5];
227 let len = value.encode(key, &mut encoded).unwrap();
228 reply.write_entry(key, &encoded[..len]).unwrap();
229 }
230 assert_eq!(reply.finish(), 67);
231 }
232 #[test]
233 fn signed_and_tagged_values_use_the_shortest_encoding() {
234 for (value, expected) in [
235 (-100, &[0x9c][..]),
236 (-200, &[0x38, 0xff][..]),
237 (-128, &[0x80][..]),
238 (-129, &[0x7f, 0xff][..]),
239 (127, &[0x7f][..]),
240 (128, &[0x80, 0][..]),
241 ] {
242 let mut bytes = [0; 5];
243 let len = Value::Current(value)
244 .encode(prop::BATTERY_CURRENT, &mut bytes)
245 .unwrap();
246 assert_eq!(&bytes[..len], expected);
247 assert_eq!(
248 Value::decode(prop::BATTERY_CURRENT, expected),
249 Ok(Some(Value::Current(value)))
250 );
251 }
252 assert_eq!(
253 Value::decode(prop::BATTERY_CURRENT, &[0x9c, 0xff, 0xff, 0xff]),
254 Ok(Some(Value::Current(-100)))
255 );
256 let mut bytes = [0; 5];
257 let len = Value::Voltage(VoltageRequest::Millivolts(4200))
258 .encode(prop::BATTERY_CHARGE_VOLTAGE_REQUEST, &mut bytes)
259 .unwrap();
260 assert_eq!(&bytes[..len], &[0, 0x68, 0x10]);
261 assert_eq!(
262 Value::decode(prop::BATTERY_CHARGE_VOLTAGE_REQUEST, &[1]),
263 Ok(Some(Value::Voltage(VoltageRequest::Maximum)))
264 );
265 for bytes in [&[0][..], &[1, 0][..], &[2][..], &[0, 0, 0, 0, 0, 0][..]] {
266 assert!(Value::decode(prop::BATTERY_CHARGE_VOLTAGE_REQUEST, bytes).is_err());
267 }
268 assert_eq!(Value::decode(prop::BATTERY_CURRENT, &[]), Ok(None));
269 assert_eq!(
270 Value::decode(prop::BATTERY_PRESENT, &[0]),
271 Ok(Some(Value::Bool(false)))
272 );
273 assert!(Value::decode(prop::BATTERY_PRESENT, &[0, 0]).is_err());
274 assert!(Value::decode(prop::BATTERY_CURRENT, &[0; 5]).is_err());
275 assert!(Value::decode(prop::BATTERY_GAUGE_FORMAT, &[0]).is_err());
276 assert_eq!(
277 Value::decode(prop::BATTERY_GAUGE_FORMAT, &[99]),
278 Ok(Some(Value::Format(99)))
279 );
280 }
281}