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