1#![cfg_attr(not(any(feature = "std", test)), no_std)]
2
3pub mod decryption;
4
5#[cfg(feature = "serde")]
9pub mod serde_hex {
10 use core::fmt;
11
12 struct HexSlice<'a>(&'a [u8]);
13
14 impl fmt::Display for HexSlice<'_> {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 for byte in self.0 {
17 write!(f, "{:02X}", byte)?;
18 }
19 Ok(())
20 }
21 }
22
23 pub fn serialize<S: serde::Serializer>(data: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
24 serializer.collect_str(&HexSlice(data))
25 }
26}
27
28#[cfg(feature = "std")]
29use std::fmt::{self, Display};
30
31#[cfg(not(feature = "std"))]
32use core::fmt;
33
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35#[derive(Debug, Clone, Copy, PartialEq)]
36#[cfg_attr(feature = "defmt", derive(defmt::Format))]
37pub struct ManufacturerCode {
38 pub code: [char; 3],
39}
40
41impl ManufacturerCode {
42 pub const CODE_MASK: u16 = 0x7FFF;
47
48 pub const fn from_id(id: u16) -> Result<Self, ApplicationLayerError> {
54 let letters = id & Self::CODE_MASK;
55 let first_letter = ((letters / (32 * 32)) + 64) as u8 as char;
56 let second_letter = (((letters % (32 * 32)) / 32) + 64) as u8 as char;
57 let third_letter = ((letters % 32) + 64) as u8 as char;
58
59 if first_letter.is_ascii_uppercase()
60 && second_letter.is_ascii_uppercase()
61 && third_letter.is_ascii_uppercase()
62 {
63 Ok(Self {
64 code: [first_letter, second_letter, third_letter],
65 })
66 } else {
67 Err(ApplicationLayerError::InvalidManufacturerCode { code: id })
68 }
69 }
70
71 #[must_use]
72 pub const fn to_id(&self) -> u16 {
73 (self.code[0] as u16 - 64) * 32 * 32
74 + (self.code[1] as u16 - 64) * 32
75 + (self.code[2] as u16 - 64)
76 }
77}
78
79#[cfg(feature = "std")]
80impl fmt::Display for ManufacturerCode {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{}{}{}", self.code[0], self.code[1], self.code[2])
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89#[non_exhaustive]
90pub enum ApplicationLayerError {
91 MissingControlInformation,
92 InvalidControlInformation { byte: u8 },
93 IdentificationNumberError { digits: [u8; 4], number: u32 },
94 InvalidManufacturerCode { code: u16 },
95 InsufficientData,
96 Unimplemented { feature: &'static str },
97}
98
99#[cfg(feature = "std")]
100impl fmt::Display for ApplicationLayerError {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 match self {
103 ApplicationLayerError::MissingControlInformation => {
104 write!(f, "Missing control information")
105 }
106 ApplicationLayerError::InvalidControlInformation { byte } => {
107 write!(f, "Invalid control information: {}", byte)
108 }
109 ApplicationLayerError::InvalidManufacturerCode { code } => {
110 write!(f, "Invalid manufacturer code: {}", code)
111 }
112 ApplicationLayerError::IdentificationNumberError { digits, number } => {
113 write!(
114 f,
115 "Invalid identification number: {:?}, number: {}",
116 digits, number
117 )
118 }
119 ApplicationLayerError::InsufficientData => {
120 write!(f, "Insufficient data")
121 }
122 ApplicationLayerError::Unimplemented { feature } => {
123 write!(f, "Unimplemented feature: {}", feature)
124 }
125 }
126 }
127}
128
129#[cfg(feature = "std")]
130impl std::error::Error for ApplicationLayerError {}
131
132pub fn bcd_hex_digits_to_u32(digits: [u8; 4]) -> Result<u32, ApplicationLayerError> {
133 let mut number = 0u32;
134
135 for &digit in digits.iter().rev() {
136 let lower = digit & 0x0F;
137 let upper = digit >> 4;
138 if lower > 9 || upper > 9 {
139 return Err(ApplicationLayerError::IdentificationNumberError { digits, number });
140 }
141 number = number * 100 + (u32::from(upper) * 10) + u32::from(lower);
142 }
143
144 Ok(number)
145}
146
147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
148#[derive(Debug, Clone, Copy, PartialEq)]
149#[cfg_attr(feature = "defmt", derive(defmt::Format))]
150pub struct IdentificationNumber {
151 pub number: u32,
152}
153
154impl core::fmt::Display for IdentificationNumber {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 write!(f, "{:08}", self.number)
157 }
158}
159
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
163#[cfg_attr(feature = "defmt", derive(defmt::Format))]
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum DeviceType {
166 Other,
167 OilMeter,
168 ElectricityMeter,
169 GasMeter,
170 HeatMeterReturn,
171 SteamMeter,
172 WarmWaterMeter,
173 WaterMeter,
174 HeatCostAllocator,
175 CompressedAir,
176 CoolingMeterReturn,
177 CoolingMeterFlow,
178 HeatMeterFlow,
179 CombinedHeatCoolingMeter,
180 BusSystemComponent,
181 UnknownDevice,
182 IrrigationWaterMeter,
183 WaterDataLogger,
184 GasDataLogger,
185 GasConverter,
186 CalorificValue,
187 HotWaterMeter,
188 ColdWaterMeter,
189 DualRegisterWaterMeter,
190 PressureMeter,
191 AdConverter,
192 SmokeDetector,
193 RoomSensor,
194 GasDetector,
195 ReservedSensor(u8),
196 ElectricityBreaker,
197 Valve,
198 ReservedSwitch(u8),
199 CustomerUnit,
200 ReservedCustomer(u8),
201 WasteWaterMeter,
202 Garbage,
203 ReservedCO2,
204 ReservedEnvironmental(u8),
205 ServiceTool,
206 CommunicationController,
207 UnidirectionalRepeater,
208 BidirectionalRepeater,
209 ReservedSystem(u8),
210 RadioConverterSystemSide,
211 RadioConverterMeterSide,
212 BusConverterMeterSide,
213 Reserved(u8),
214 Wildcard,
215}
216
217#[cfg(feature = "std")]
218impl Display for DeviceType {
219 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220 match self {
221 DeviceType::Other => write!(f, "Other"),
222 DeviceType::OilMeter => write!(f, "Oil Meter"),
223 DeviceType::ElectricityMeter => write!(f, "Electricity Meter"),
224 DeviceType::GasMeter => write!(f, "Gas Meter"),
225 DeviceType::HeatMeterReturn => write!(f, "Heat Meter (Return)"),
226 DeviceType::SteamMeter => write!(f, "Steam Meter"),
227 DeviceType::WarmWaterMeter => write!(f, "Warm Water Meter (30-90°C)"),
228 DeviceType::WaterMeter => write!(f, "Water Meter"),
229 DeviceType::HeatCostAllocator => write!(f, "Heat Cost Allocator"),
230 DeviceType::CompressedAir => write!(f, "Compressed Air"),
231 DeviceType::CoolingMeterReturn => write!(f, "Cooling Meter (Return)"),
232 DeviceType::CoolingMeterFlow => write!(f, "Cooling Meter (Flow)"),
233 DeviceType::HeatMeterFlow => write!(f, "Heat Meter (Flow)"),
234 DeviceType::CombinedHeatCoolingMeter => write!(f, "Combined Heat/Cooling Meter"),
235 DeviceType::BusSystemComponent => write!(f, "Bus/System Component"),
236 DeviceType::UnknownDevice => write!(f, "Unknown Device"),
237 DeviceType::IrrigationWaterMeter => write!(f, "Irrigation Water Meter"),
238 DeviceType::WaterDataLogger => write!(f, "Water Data Logger"),
239 DeviceType::GasDataLogger => write!(f, "Gas Data Logger"),
240 DeviceType::GasConverter => write!(f, "Gas Converter"),
241 DeviceType::CalorificValue => write!(f, "Calorific Value"),
242 DeviceType::HotWaterMeter => write!(f, "Hot Water Meter (≥90°C)"),
243 DeviceType::ColdWaterMeter => write!(f, "Cold Water Meter"),
244 DeviceType::DualRegisterWaterMeter => write!(f, "Dual Register Water Meter"),
245 DeviceType::PressureMeter => write!(f, "Pressure Meter"),
246 DeviceType::AdConverter => write!(f, "A/D Converter"),
247 DeviceType::SmokeDetector => write!(f, "Smoke Detector"),
248 DeviceType::RoomSensor => write!(f, "Room Sensor"),
249 DeviceType::GasDetector => write!(f, "Gas Detector"),
250 DeviceType::ReservedSensor(code) => write!(f, "Reserved Sensor (0x{:02X})", code),
251 DeviceType::ElectricityBreaker => write!(f, "Breaker (Electricity)"),
252 DeviceType::Valve => write!(f, "Valve (Gas/Water)"),
253 DeviceType::ReservedSwitch(code) => write!(f, "Reserved Switch (0x{:02X})", code),
254 DeviceType::CustomerUnit => write!(f, "Customer Unit (Display)"),
255 DeviceType::ReservedCustomer(code) => {
256 write!(f, "Reserved Customer Unit (0x{:02X})", code)
257 }
258 DeviceType::WasteWaterMeter => write!(f, "Waste Water Meter"),
259 DeviceType::Garbage => write!(f, "Garbage"),
260 DeviceType::ReservedCO2 => write!(f, "Reserved (CO₂)"),
261 DeviceType::ReservedEnvironmental(code) => {
262 write!(f, "Reserved Environmental (0x{:02X})", code)
263 }
264 DeviceType::ServiceTool => write!(f, "Service Tool"),
265 DeviceType::CommunicationController => write!(f, "Communication Controller (Gateway)"),
266 DeviceType::UnidirectionalRepeater => write!(f, "Unidirectional Repeater"),
267 DeviceType::BidirectionalRepeater => write!(f, "Bidirectional Repeater"),
268 DeviceType::ReservedSystem(code) => write!(f, "Reserved System (0x{:02X})", code),
269 DeviceType::RadioConverterSystemSide => write!(f, "Radio Converter (System Side)"),
270 DeviceType::RadioConverterMeterSide => write!(f, "Radio Converter (Meter Side)"),
271 DeviceType::BusConverterMeterSide => write!(f, "Bus Converter (Meter Side)"),
272 DeviceType::Reserved(code) => write!(f, "Reserved (0x{:02X})", code),
273 DeviceType::Wildcard => write!(f, "Wildcard"),
274 }
275 }
276}
277
278impl From<DeviceType> for u8 {
279 fn from(value: DeviceType) -> Self {
280 match value {
281 DeviceType::Other => 0x00,
282 DeviceType::OilMeter => 0x01,
283 DeviceType::ElectricityMeter => 0x02,
284 DeviceType::GasMeter => 0x03,
285 DeviceType::HeatMeterReturn => 0x04,
286 DeviceType::SteamMeter => 0x05,
287 DeviceType::WarmWaterMeter => 0x06,
288 DeviceType::WaterMeter => 0x07,
289 DeviceType::HeatCostAllocator => 0x08,
290 DeviceType::CompressedAir => 0x09,
291 DeviceType::CoolingMeterReturn => 0x0A,
292 DeviceType::CoolingMeterFlow => 0x0B,
293 DeviceType::HeatMeterFlow => 0x0C,
294 DeviceType::CombinedHeatCoolingMeter => 0x0D,
295 DeviceType::BusSystemComponent => 0x0E,
296 DeviceType::UnknownDevice => 0x0F,
297 DeviceType::IrrigationWaterMeter => 0x10,
298 DeviceType::WaterDataLogger => 0x11,
299 DeviceType::GasDataLogger => 0x12,
300 DeviceType::GasConverter => 0x13,
301 DeviceType::CalorificValue => 0x14,
302 DeviceType::HotWaterMeter => 0x15,
303 DeviceType::ColdWaterMeter => 0x16,
304 DeviceType::DualRegisterWaterMeter => 0x17,
305 DeviceType::PressureMeter => 0x18,
306 DeviceType::AdConverter => 0x19,
307 DeviceType::SmokeDetector => 0x1A,
308 DeviceType::RoomSensor => 0x1B,
309 DeviceType::GasDetector => 0x1C,
310 DeviceType::ReservedSensor(code) => code,
311 DeviceType::ElectricityBreaker => 0x20,
312 DeviceType::Valve => 0x21,
313 DeviceType::ReservedSwitch(code) => code,
314 DeviceType::CustomerUnit => 0x25,
315 DeviceType::ReservedCustomer(code) => code,
316 DeviceType::WasteWaterMeter => 0x28,
317 DeviceType::Garbage => 0x29,
318 DeviceType::ReservedCO2 => 0x2A,
319 DeviceType::ReservedEnvironmental(code) => code,
320 DeviceType::ServiceTool => 0x30,
321 DeviceType::CommunicationController => 0x31,
322 DeviceType::UnidirectionalRepeater => 0x32,
323 DeviceType::BidirectionalRepeater => 0x33,
324 DeviceType::ReservedSystem(code) => code,
325 DeviceType::RadioConverterSystemSide => 0x36,
326 DeviceType::RadioConverterMeterSide => 0x37,
327 DeviceType::BusConverterMeterSide => 0x38,
328 DeviceType::Reserved(code) => code,
329 DeviceType::Wildcard => 0xFF,
330 }
331 }
332}
333impl From<u8> for DeviceType {
334 fn from(value: u8) -> Self {
335 match value {
336 0x00 => DeviceType::Other,
337 0x01 => DeviceType::OilMeter,
338 0x02 => DeviceType::ElectricityMeter,
339 0x03 => DeviceType::GasMeter,
340 0x04 => DeviceType::HeatMeterReturn,
341 0x05 => DeviceType::SteamMeter,
342 0x06 => DeviceType::WarmWaterMeter,
343 0x07 => DeviceType::WaterMeter,
344 0x08 => DeviceType::HeatCostAllocator,
345 0x09 => DeviceType::CompressedAir,
346 0x0A => DeviceType::CoolingMeterReturn,
347 0x0B => DeviceType::CoolingMeterFlow,
348 0x0C => DeviceType::HeatMeterFlow,
349 0x0D => DeviceType::CombinedHeatCoolingMeter,
350 0x0E => DeviceType::BusSystemComponent,
351 0x0F => DeviceType::UnknownDevice,
352 0x10 => DeviceType::IrrigationWaterMeter,
353 0x11 => DeviceType::WaterDataLogger,
354 0x12 => DeviceType::GasDataLogger,
355 0x13 => DeviceType::GasConverter,
356 0x14 => DeviceType::CalorificValue,
357 0x15 => DeviceType::HotWaterMeter,
358 0x16 => DeviceType::ColdWaterMeter,
359 0x17 => DeviceType::DualRegisterWaterMeter,
360 0x18 => DeviceType::PressureMeter,
361 0x19 => DeviceType::AdConverter,
362 0x1A => DeviceType::SmokeDetector,
363 0x1B => DeviceType::RoomSensor,
364 0x1C => DeviceType::GasDetector,
365 0x1D..=0x1F => DeviceType::ReservedSensor(value),
366 0x20 => DeviceType::ElectricityBreaker,
367 0x21 => DeviceType::Valve,
368 0x22..=0x24 => DeviceType::ReservedSwitch(value),
369 0x25 => DeviceType::CustomerUnit,
370 0x26..=0x27 => DeviceType::ReservedCustomer(value),
371 0x28 => DeviceType::WasteWaterMeter,
372 0x29 => DeviceType::Garbage,
373 0x2A => DeviceType::ReservedCO2,
374 0x2B..=0x2F => DeviceType::ReservedEnvironmental(value),
375 0x30 => DeviceType::ServiceTool,
376 0x31 => DeviceType::CommunicationController,
377 0x32 => DeviceType::UnidirectionalRepeater,
378 0x33 => DeviceType::BidirectionalRepeater,
379 0x34..=0x35 => DeviceType::ReservedSystem(value),
380 0x36 => DeviceType::RadioConverterSystemSide,
381 0x37 => DeviceType::RadioConverterMeterSide,
382 0x38 => DeviceType::BusConverterMeterSide,
383 0x39..=0x3F => DeviceType::ReservedSystem(value),
384 0x40..=0xFE => DeviceType::Reserved(value),
385 0xFF => DeviceType::Wildcard,
386 }
387 }
388}
389impl From<IdentificationNumber> for u32 {
390 fn from(id: IdentificationNumber) -> Self {
391 id.number
392 }
393}
394
395impl IdentificationNumber {
396 pub fn from_bcd_hex_digits(digits: [u8; 4]) -> Result<Self, ApplicationLayerError> {
397 let number = bcd_hex_digits_to_u32(digits)?;
398 Ok(Self { number })
399 }
400}
401
402#[cfg(test)]
403mod test {
404 use super::*;
405
406 #[test]
407 fn test_manufacturer_code() -> Result<(), ApplicationLayerError> {
408 let code = ManufacturerCode::from_id(0x1ee6)?;
409 assert_eq!(
410 code,
411 ManufacturerCode {
412 code: ['G', 'W', 'F']
413 }
414 );
415 Ok(())
416 }
417
418 #[test]
419 fn test_manufacturer_code_ignores_top_bit() -> Result<(), ApplicationLayerError> {
420 assert_eq!(
422 ManufacturerCode::from_id(0xA697)?,
423 ManufacturerCode {
424 code: ['I', 'T', 'W']
425 }
426 );
427 assert_eq!(
429 ManufacturerCode::from_id(0x8000),
430 Err(ApplicationLayerError::InvalidManufacturerCode { code: 0x8000 })
431 );
432 Ok(())
433 }
434
435 #[test]
436 fn test_manufacturer_code_roundtrip() -> Result<(), ApplicationLayerError> {
437 let original_id = 0x1ee6;
439 let code = ManufacturerCode::from_id(original_id)?;
440 let converted_id = code.to_id();
441 assert_eq!(original_id, converted_id);
442
443 for id in [0x0000, 0x0421, 0x1234, 0x7FFF] {
445 if let Ok(code) = ManufacturerCode::from_id(id) {
446 assert_eq!(id, code.to_id());
447 }
448 }
449 Ok(())
450 }
451
452 #[test]
453 fn test_identification_number() -> Result<(), ApplicationLayerError> {
454 let data = [0x78, 0x56, 0x34, 0x12];
455 let result = IdentificationNumber::from_bcd_hex_digits(data)?;
456 assert_eq!(result, IdentificationNumber { number: 12345678 });
457 Ok(())
458 }
459
460 #[test]
461 fn test_configuration_field_debug() {
462 let cf = ConfigurationField::from(1360);
464 let debug_output = format!("{:?}", cf);
465 assert_eq!(
466 debug_output,
467 "ConfigurationField { mode: AesCbc128IvNonZero }"
468 );
469
470 let cf_no_enc = ConfigurationField::from(0);
472 let debug_output = format!("{:?}", cf_no_enc);
473 assert_eq!(debug_output, "ConfigurationField { mode: NoEncryption }");
474 }
475}
476
477#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
478#[derive(Debug, Clone, Copy, PartialEq)]
479#[cfg_attr(feature = "defmt", derive(defmt::Format))]
480#[non_exhaustive]
481pub enum Function {
482 SndNk { prm: bool },
483 SndUd { fcb: bool },
484 SndUd2,
485 SndUd3,
486 SndNr,
487 SendIr,
488 AccNr,
489 AccDmd,
490 ReqUd1 { fcb: bool },
491 ReqUd2 { fcb: bool },
492 RspUd { acd: bool, dfc: bool },
493 Ack,
494 Nack,
495 CnfIr,
496}
497
498#[cfg(feature = "std")]
499impl std::fmt::Display for Function {
500 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501 match self {
502 Function::SndNk { prm: _prm } => write!(f, "SndNk"),
503 Function::SndUd { fcb } => write!(f, "SndUd (FCB: {fcb})"),
504 Function::ReqUd2 { fcb } => write!(f, "ReqUd2 (FCB: {fcb})"),
505 Function::ReqUd1 { fcb } => write!(f, "ReqUd1 (FCB: {fcb})"),
506 Function::RspUd { acd, dfc } => write!(f, "RspUd (ACD: {acd}, DFC: {dfc})"),
507 _ => write!(f, "{:?}", self),
508 }
509 }
510}
511
512#[derive(Debug, Clone, Copy, PartialEq, Eq)]
513#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
514#[cfg_attr(feature = "defmt", derive(defmt::Format))]
515pub enum FrameError {
516 EmptyData,
517 InvalidStartByte,
518 InvalidStopByte,
519 WrongLengthIndication,
520 LengthShort,
521 LengthShorterThanSix { length: usize },
522 WrongLength { expected: usize, actual: usize },
523 WrongCrc { expected: u16, actual: u16 },
524 WrongChecksum { expected: u8, actual: u8 },
525 InvalidControlInformation { byte: u8 },
526 InvalidFunction { byte: u8 },
527}
528
529#[cfg(feature = "std")]
530impl std::error::Error for FrameError {}
531
532#[cfg(feature = "std")]
533impl std::fmt::Display for FrameError {
534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
535 match self {
536 FrameError::EmptyData => write!(f, "Data is empty"),
537 FrameError::InvalidStartByte => write!(f, "Invalid start byte"),
538 FrameError::InvalidStopByte => write!(f, "Invalid stop byte"),
539 FrameError::LengthShort => write!(f, "Length mismatch"),
540 FrameError::LengthShorterThanSix { length } => {
541 write!(f, "Length is shorter than six: {}", length)
542 }
543 FrameError::WrongChecksum { expected, actual } => write!(
544 f,
545 "Wrong checksum, expected: {}, actual: {}",
546 expected, actual
547 ),
548 FrameError::InvalidControlInformation { byte } => {
549 write!(f, "Invalid control information: {}", byte)
550 }
551 FrameError::InvalidFunction { byte } => write!(f, "Invalid function: {}", byte),
552 FrameError::WrongLengthIndication => write!(f, "Wrong length indication"),
553 FrameError::WrongLength { expected, actual } => write!(
554 f,
555 "Wrong length, expected: {}, actual: {}",
556 expected, actual
557 ),
558 FrameError::WrongCrc { expected, actual } => {
559 write!(f, "Wrong CRC, expected: {}, actual: {}", expected, actual)
560 }
561 }
562 }
563}
564impl TryFrom<u8> for Function {
565 type Error = FrameError;
566
567 fn try_from(byte: u8) -> Result<Self, Self::Error> {
568 match byte {
569 0x40 => Ok(Self::SndNk { prm: false }),
570 0x44 => Ok(Self::SndNr),
571 0x53 => Ok(Self::SndUd { fcb: false }),
572 0x73 => Ok(Self::SndUd { fcb: true }),
573 0x5B => Ok(Self::ReqUd2 { fcb: false }),
574 0x7B => Ok(Self::ReqUd2 { fcb: true }),
575 0x5A => Ok(Self::ReqUd1 { fcb: false }),
576 0x7A => Ok(Self::ReqUd1 { fcb: true }),
577 0x08 => Ok(Self::RspUd {
578 acd: false,
579 dfc: false,
580 }),
581 0x18 => Ok(Self::RspUd {
582 acd: false,
583 dfc: true,
584 }),
585 0x28 => Ok(Self::RspUd {
586 acd: true,
587 dfc: false,
588 }),
589 0x38 => Ok(Self::RspUd {
590 acd: true,
591 dfc: true,
592 }),
593 _ => Err(FrameError::InvalidFunction { byte }),
594 }
595 }
596}
597
598#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
603#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
604#[cfg_attr(feature = "defmt", derive(defmt::Format))]
605#[non_exhaustive]
606pub enum SecurityMode {
607 NoEncryption,
608 ManufacturerSpecific,
609 DesIvZero,
610 DesIvNonZero,
611 SpecificUsage4,
612 AesCbc128IvNonZero,
613 Reserved6,
614 AesCbc128IvZero,
615 AesCtr128Cmac,
616 AesGcm128,
617 AesCcm128,
618 Reserved11,
619 Reserved12,
620 SpecificUsage13,
621 Reserved14,
622 SpecificUsage15,
623 ReservedHigher(u8),
624}
625
626impl SecurityMode {
627 pub const fn from_bits(mode: u8) -> Self {
629 match mode & 0b0001_1111 {
630 0 => Self::NoEncryption,
631 1 => Self::ManufacturerSpecific,
632 2 => Self::DesIvZero,
633 3 => Self::DesIvNonZero,
634 4 => Self::SpecificUsage4,
635 5 => Self::AesCbc128IvNonZero,
636 6 => Self::Reserved6,
637 7 => Self::AesCbc128IvZero,
638 8 => Self::AesCtr128Cmac,
639 9 => Self::AesGcm128,
640 10 => Self::AesCcm128,
641 11 => Self::Reserved11,
642 12 => Self::Reserved12,
643 13 => Self::SpecificUsage13,
644 14 => Self::Reserved14,
645 15 => Self::SpecificUsage15,
646 other => Self::ReservedHigher(other),
647 }
648 }
649
650 pub const fn to_bits(&self) -> u8 {
652 match self {
653 Self::NoEncryption => 0,
654 Self::ManufacturerSpecific => 1,
655 Self::DesIvZero => 2,
656 Self::DesIvNonZero => 3,
657 Self::SpecificUsage4 => 4,
658 Self::AesCbc128IvNonZero => 5,
659 Self::Reserved6 => 6,
660 Self::AesCbc128IvZero => 7,
661 Self::AesCtr128Cmac => 8,
662 Self::AesGcm128 => 9,
663 Self::AesCcm128 => 10,
664 Self::Reserved11 => 11,
665 Self::Reserved12 => 12,
666 Self::SpecificUsage13 => 13,
667 Self::Reserved14 => 14,
668 Self::SpecificUsage15 => 15,
669 Self::ReservedHigher(mode) => *mode & 0b0001_1111,
670 }
671 }
672}
673
674#[cfg(feature = "std")]
675impl fmt::Display for SecurityMode {
676 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
677 match self {
678 Self::NoEncryption => write!(f, "No encryption used"),
679 Self::ManufacturerSpecific => write!(f, "Manufacturer specific usage"),
680 Self::DesIvZero => write!(f, "DES; IV = 0 (deprecated)"),
681 Self::DesIvNonZero => write!(f, "DES; IV ≠ 0 (deprecated)"),
682 Self::SpecificUsage4 => write!(f, "Specific usage (Bibliographical Entry [6])"),
683 Self::AesCbc128IvNonZero => write!(f, "AES-CBC-128; IV ≠ 0"),
684 Self::Reserved6 => write!(f, "Reserved for future use"),
685 Self::AesCbc128IvZero => write!(f, "AES-CBC-128; IV = 0"),
686 Self::AesCtr128Cmac => write!(f, "AES-CTR-128; CMAC"),
687 Self::AesGcm128 => write!(f, "AES-GCM-128"),
688 Self::AesCcm128 => write!(f, "AES-CCM-128"),
689 Self::Reserved11 => write!(f, "Reserved for future use"),
690 Self::Reserved12 => write!(f, "Reserved for future use"),
691 Self::SpecificUsage13 => write!(f, "Specific usage (Bibliographical Entry [8])"),
692 Self::Reserved14 => write!(f, "Reserved for future use"),
693 Self::SpecificUsage15 => write!(f, "Specific usage (Bibliographical Entry [7])"),
694 Self::ReservedHigher(mode) => write!(f, "Reserved for future use (mode {})", mode),
695 }
696 }
697}
698
699#[derive(Clone, Copy, PartialEq, Eq, Hash)]
715#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
716#[cfg_attr(feature = "defmt", derive(defmt::Format))]
717pub struct ConfigurationField {
718 raw: u16,
719}
720
721impl ConfigurationField {
722 pub const fn from_bytes(lsb: u8, msb: u8) -> Self {
728 Self {
729 raw: u16::from_le_bytes([lsb, msb]),
730 }
731 }
732
733 pub const fn raw(&self) -> u16 {
735 self.raw
736 }
737
738 pub const fn security_mode(&self) -> SecurityMode {
743 let mode_bits = ((self.raw >> 8) & 0b0001_1111) as u8;
744 SecurityMode::from_bits(mode_bits)
745 }
746
747 pub const fn mode_specific_lower(&self) -> u8 {
751 (self.raw & 0xFF) as u8
752 }
753
754 pub const fn mode_specific_upper(&self) -> u8 {
758 ((self.raw >> 13) & 0b0000_0111) as u8
759 }
760}
761
762impl From<u16> for ConfigurationField {
763 fn from(value: u16) -> Self {
764 Self { raw: value }
765 }
766}
767
768impl From<ConfigurationField> for u16 {
769 fn from(cf: ConfigurationField) -> Self {
770 cf.raw
771 }
772}
773
774#[cfg(feature = "std")]
775impl fmt::Display for ConfigurationField {
776 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777 write!(
778 f,
779 "Configuration Field: 0x{:04X} (Security mode: {})",
780 self.raw,
781 self.security_mode()
782 )
783 }
784}
785
786impl fmt::Debug for ConfigurationField {
787 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
788 f.debug_struct("ConfigurationField")
789 .field("mode", &self.security_mode())
790 .finish()
791 }
792}