Skip to main content

osdp/reply/
pdid.rs

1//! `osdp_PDID` (`0x45`) — peripheral device identification.
2//!
3//! # Spec: §7.4
4//!
5//! Body is exactly 12 bytes:
6//!
7//! ```text
8//! +-----+-----+-----+-------+--------+----------+----------+----------+----------+----------+----------+
9//! |VC1  |VC2  |VC3  |Model# |Version |Serial#0  |Serial#1  |Serial#2  |Serial#3  |FW major  |FW minor  |FW build|
10//! +-----+-----+-----+-------+--------+----------+----------+----------+----------+----------+----------+
11//!  byte0 byte1 byte2 byte3   byte4    byte5..8 (LE u32)                          byte9..11
12//! ```
13
14use crate::error::Error;
15use crate::payload_util::require_exact_len;
16use alloc::vec::Vec;
17
18/// `osdp_PDID` body.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct PdId {
21    /// IEEE-assigned OUI.
22    pub vendor_oui: [u8; 3],
23    /// Model number.
24    pub model: u8,
25    /// Hardware/firmware family version.
26    pub version: u8,
27    /// 32-bit serial number.
28    pub serial: u32,
29    /// Firmware revision: `[major, minor, build]`.
30    pub firmware: [u8; 3],
31}
32
33impl PdId {
34    /// Wire size.
35    pub const WIRE_LEN: usize = 12;
36
37    /// Encode.
38    pub fn encode(&self) -> Result<Vec<u8>, Error> {
39        let mut out = Vec::with_capacity(Self::WIRE_LEN);
40        out.extend_from_slice(&self.vendor_oui);
41        out.push(self.model);
42        out.push(self.version);
43        out.extend_from_slice(&self.serial.to_le_bytes());
44        out.extend_from_slice(&self.firmware);
45        Ok(out)
46    }
47
48    /// Decode.
49    pub fn decode(data: &[u8]) -> Result<Self, Error> {
50        require_exact_len(data, Self::WIRE_LEN, 0x45)?;
51        let mut vendor_oui = [0u8; 3];
52        vendor_oui.copy_from_slice(&data[..3]);
53        let mut firmware = [0u8; 3];
54        firmware.copy_from_slice(&data[9..12]);
55        Ok(Self {
56            vendor_oui,
57            model: data[3],
58            version: data[4],
59            serial: u32::from_le_bytes([data[5], data[6], data[7], data[8]]),
60            firmware,
61        })
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn roundtrip() {
71        let id = PdId {
72            vendor_oui: [0x00, 0x06, 0x8E], // HID's OUI
73            model: 0x12,
74            version: 0x34,
75            serial: 0xDEAD_BEEF,
76            firmware: [1, 2, 3],
77        };
78        let bytes = id.encode().unwrap();
79        assert_eq!(bytes.len(), 12);
80        let parsed = PdId::decode(&bytes).unwrap();
81        assert_eq!(parsed, id);
82    }
83
84    #[test]
85    fn rejects_truncated() {
86        assert!(PdId::decode(&[0; 11]).is_err());
87        assert!(PdId::decode(&[0; 13]).is_err());
88    }
89}