hftwo/
command.rs

1use crate::{Packet, PacketKind};
2
3/// Commands.
4///
5/// Specifies the commands in the spec as well as `Other` for user-defined
6/// commands.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
9#[repr(u32)]
10pub enum Command {
11    BinInfo = 0x0001,
12    Info = 0x0002,
13    ResetIntoApp = 0x0003,
14    ResetIntoBootloader = 0x0004,
15    StartFlash = 0x0005,
16    WriteFlashPage = 0x0006,
17    ChecksumPages = 0x0007,
18    ReadWords = 0x0008,
19    WriteWords = 0x0009,
20    Dmesg = 0x0010,
21    Other(u32),
22}
23
24impl From<u32> for Command {
25    fn from(value: u32) -> Self {
26        match value {
27            0x0001 => Self::BinInfo,
28            0x0002 => Self::Info,
29            0x0003 => Self::ResetIntoApp,
30            0x0004 => Self::ResetIntoBootloader,
31            0x0005 => Self::StartFlash,
32            0x0006 => Self::WriteFlashPage,
33            0x0007 => Self::ReadWords,
34            0x0008 => Self::WriteWords,
35            0x0010 => Self::Dmesg,
36            _ => Self::Other(value),
37        }
38    }
39}
40
41impl Into<u32> for Command {
42    fn into(self) -> u32 {
43        match self {
44            Self::BinInfo => 0x0001,
45            Self::Info => 0x0002,
46            Self::ResetIntoApp => 0x0003,
47            Self::ResetIntoBootloader => 0x0004,
48            Self::StartFlash => 0x0005,
49            Self::WriteFlashPage => 0x0006,
50            Self::ChecksumPages => 0x0007,
51            Self::ReadWords => 0x0008,
52            Self::WriteWords => 0x0009,
53            Self::Dmesg => 0x0010,
54            Self::Other(value) => value,
55        }
56    }
57}
58
59/// Command request.
60#[derive(Debug)]
61#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
62pub struct Request<'a>(&'a [u8]);
63
64impl<'a> Request<'a> {
65    pub const HEADER_LEN: usize = 8;
66
67    /// Creates a new [`Request`].
68    ///
69    /// `buf` must be 8 bytes larger than `data` to fit the header.
70    pub fn new(buf: &'a mut [u8], command: Command, tag: u16, data: &[u8]) -> Self {
71        // ensure header and data will fit in buffer
72        assert!(buf.len() == (data.len() + Self::HEADER_LEN));
73
74        // write command id
75        let cmd: u32 = command.into();
76        buf[0..4].copy_from_slice(&cmd.to_le_bytes());
77
78        // write tag
79        buf[4..6].copy_from_slice(&tag.to_le_bytes());
80
81        // write data
82        buf[8..].copy_from_slice(data);
83
84        Self(buf)
85    }
86
87    /// Creates a new [`Request`] from a byte array.
88    pub fn from_bytes(buf: &'a [u8]) -> Self {
89        assert!(buf.len() >= Self::HEADER_LEN);
90        Self(buf)
91    }
92
93    /// Data length.
94    pub fn len(&self) -> usize {
95        self.0.len() - Self::HEADER_LEN
96    }
97
98    /// Get command.
99    pub fn command(&self) -> Command {
100        let bytes = &self.0[0..4];
101        Command::from(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
102    }
103
104    /// Get tag.
105    pub fn tag(&self) -> u16 {
106        let bytes = &self.0[4..6];
107        u16::from_le_bytes([bytes[0], bytes[1]])
108    }
109
110    /// Command data.
111    pub fn data(&self) -> &[u8] {
112        &self.0[8..]
113    }
114
115    /// Get a [`Packet`] iterator.
116    pub fn into_packet_iter(&self) -> RequestPacketIter {
117        RequestPacketIter {
118            request: self,
119            chunk: 0,
120        }
121    }
122}
123
124/// Request packet iterator.
125///
126/// Doesn't implement the [`Iterator`] trait because of some no-allocation
127/// constraints.
128#[derive(Debug)]
129#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
130pub struct RequestPacketIter<'a> {
131    request: &'a Request<'a>,
132    chunk: usize,
133}
134
135impl<'a> RequestPacketIter<'a> {
136    /// Get the next packet using `buf` to store the packet data.
137    pub fn next<'b>(&mut self, buf: &'b mut [u8]) -> Option<Packet<'b>> {
138        let chunks = self.request.0.chunks(Packet::MAX_LEN);
139
140        let chunk = match self.request.0.chunks(Packet::MAX_LEN).nth(self.chunk) {
141            Some(c) => c,
142            None => return None,
143        };
144
145        let kind = if chunks.count() - 1 == self.chunk {
146            PacketKind::CommandFinal
147        } else {
148            PacketKind::CommandInner
149        };
150
151        // increment to next chunk
152        self.chunk += 1;
153
154        Some(Packet::new(buf, kind, chunk))
155    }
156}
157
158/// Response status.
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
161#[repr(u32)]
162pub enum Status {
163    /// The command was processed successfully.
164    Sucess = 0x00,
165    /// Command ID was not known to the device.
166    Unknown = 0x01,
167    /// An error occurred during execution of the command.
168    Error = 0x02,
169    /// Any other status response.
170    Other(u8),
171}
172
173impl From<u8> for Status {
174    fn from(value: u8) -> Self {
175        match value {
176            0x00 => Self::Sucess,
177            0x01 => Self::Unknown,
178            0x02 => Self::Error,
179            _ => Self::Other(value),
180        }
181    }
182}
183
184impl Into<u8> for Status {
185    fn into(self) -> u8 {
186        match self {
187            Self::Sucess => 0x00,
188            Self::Unknown => 0x01,
189            Self::Error => 0x02,
190            Self::Other(value) => value,
191        }
192    }
193}
194
195/// Command response.
196#[derive(Debug)]
197#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
198pub struct Response<'a>(&'a [u8]);
199
200impl<'a> Response<'a> {
201    pub const HEADER_LEN: usize = 4;
202
203    /// Creates a new [`Response`].
204    ///
205    /// `buf` must be 8 bytes larger than `data` to fit the header.
206    pub fn new(buf: &'a mut [u8], tag: u16, status: Status, status_info: u8, data: &[u8]) -> Self {
207        // ensure header and data will fit in buffer
208        assert!(buf.len() == data.len() + Self::HEADER_LEN);
209
210        buf[0..2].copy_from_slice(&tag.to_le_bytes());
211        buf[2] = status.into();
212        buf[3] = status_info;
213        buf[Self::HEADER_LEN..].copy_from_slice(data);
214
215        Self(buf)
216    }
217
218    /// Creates a new [`Response`] from a byte array.
219    pub fn from_bytes(buf: &'a [u8]) -> Self {
220        assert!(buf.len() >= Self::HEADER_LEN);
221        Self(buf)
222    }
223
224    /// Returns the tag.
225    pub fn tag(&self) -> u16 {
226        let bytes = &self.0[0..2];
227        u16::from_le_bytes([bytes[0], bytes[1]])
228    }
229
230    /// Returns the status.
231    pub fn status(&self) -> Status {
232        Status::from(self.0[2])
233    }
234
235    /// Returns the status info byte.
236    pub fn status_info(&self) -> u8 {
237        self.0[3]
238    }
239
240    /// Returns a slice containing the data.
241    pub fn data(&self) -> &[u8] {
242        &self.0[Self::HEADER_LEN..]
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[test]
251    fn test_command() {
252        let command = Command::from(u32::MAX);
253        assert_eq!(command, Command::Other(u32::MAX));
254
255        let value = 123456;
256        let input = Command::from(value);
257        let output: u32 = input.into();
258        assert_eq!(value, output);
259    }
260
261    #[test]
262    fn test_request() {
263        let mut buf = [0; 256];
264        let data = [0x55; 256 - Request::HEADER_LEN];
265        let request = Request::new(&mut buf, Command::Info, 0x123, &data);
266
267        let mut packet_iter = request.into_packet_iter();
268
269        let mut iter_count = 0;
270        let mut byte_count = 0;
271
272        loop {
273            let mut buf = [0; 64];
274            match packet_iter.next(&mut buf) {
275                Some(packet) => {
276                    byte_count += packet.data().len();
277                    iter_count += 1;
278
279                    println!("{:?}", packet.kind());
280                }
281                None => break,
282            }
283        }
284
285        assert_eq!(iter_count, 5);
286        assert_eq!(byte_count, 256);
287    }
288}