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