1use crate::*;
2
3#[derive(Copy, Clone, Eq, PartialEq, Debug)]
4#[cfg_attr(feature = "defmt", derive(defmt::Format))]
5pub enum Command<'a> {
6 GetStatus,
7 GetUdpPorts,
8 GetIpAddress,
9 GetMacAddress,
10 GetClientMacAddress,
11 GetClientIpAddress,
12 GetNeighborDiscoverySettings,
13 GetOperationMode,
14 GetUartSettings,
15
16 SetOperationMode {
17 mode: OperationMode,
18 han_sleep: bool,
19 channel: Channel,
20 tx_power: TxPower,
21 },
22 SetNeighborDiscoverySettings {
23 enable: bool,
24 },
25 SetUartSettings {
26 flow_control: bool,
27 },
28
29 OpenUdpPort(u16),
30 CloseUdpPort(u16),
31 TransmitData {
32 destination_address: u128,
33 source_port: u16,
34 destination_port: u16,
35 data: &'a [u8],
36 },
37 DoActiveScan {
38 duration: ScanDuration,
39 mask_channels: u32,
40 pairing_id: Option<u64>,
41 },
42
43 GetVersionInformation,
44
45 SetRouteBPanaAuthenticationInformation {
46 id: &'a [u8],
47 password: &'a [u8],
48 },
49
50 StartRouteBOperation,
51 StartRouteBPana,
52 TerminateRouteBPana,
53 TerminateRouteBOperation,
54 RedoRouteBPanaAuthentication,
55}
56
57#[allow(clippy::result_unit_err)] pub fn serialize_to_bytes<'a>(cmd: &'a Command<'a>, buf: &mut [u8]) -> Result<usize, ()> {
59 let mut total_len = COMMAND_RANGE_DATA.start;
60
61 if buf.len() < total_len {
62 return Err(());
63 }
64
65 buf[COMMAND_RANGE_UNIQUE_CODE].copy_from_slice(&0xd0ea83fc_u32.to_be_bytes());
66
67 match cmd {
68 Command::GetStatus => {
69 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0001_u16.to_be_bytes())
70 }
71
72 Command::GetUdpPorts => {
73 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0007_u16.to_be_bytes())
74 }
75
76 Command::GetIpAddress => {
77 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0009_u16.to_be_bytes())
78 }
79
80 Command::GetMacAddress => {
81 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x000e_u16.to_be_bytes())
82 }
83
84 Command::GetClientMacAddress => {
85 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0011_u16.to_be_bytes())
86 }
87
88 Command::GetClientIpAddress => {
89 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0100_u16.to_be_bytes())
90 }
91
92 Command::GetNeighborDiscoverySettings => {
93 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0102_u16.to_be_bytes())
94 }
95
96 Command::GetOperationMode => {
97 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0107_u16.to_be_bytes())
98 }
99
100 Command::GetUartSettings => {
101 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x010b_u16.to_be_bytes())
102 }
103
104 Command::SetOperationMode {
105 mode,
106 han_sleep,
107 channel,
108 tx_power,
109 } => {
110 let data = [
111 u8::from(*mode),
112 if *han_sleep { 0x01 } else { 0x00 },
113 u8::from(*channel),
114 u8::from(*tx_power),
115 ];
116 total_len += data.len();
117 if buf.len() < total_len {
118 return Err(());
119 }
120 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x005f_u16.to_be_bytes());
121 buf[COMMAND_RANGE_DATA.start..COMMAND_RANGE_DATA.start + data.len()]
122 .copy_from_slice(&data);
123 }
124
125 Command::SetNeighborDiscoverySettings { enable } => {
126 let data = [*enable as _];
127 total_len += data.len();
128 if buf.len() < total_len {
129 return Err(());
130 }
131 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0101_u16.to_be_bytes());
132 buf[COMMAND_RANGE_DATA.start..COMMAND_RANGE_DATA.start + data.len()]
133 .copy_from_slice(&data);
134 }
135
136 Command::SetUartSettings { flow_control } => {
137 let data = [*flow_control as _];
138 total_len += data.len();
139 if buf.len() < total_len {
140 return Err(());
141 }
142 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x010a_u16.to_be_bytes());
143 buf[COMMAND_RANGE_DATA.start..COMMAND_RANGE_DATA.start + data.len()]
144 .copy_from_slice(&data);
145 }
146
147 Command::OpenUdpPort(port) => {
148 let data = port.to_be_bytes();
149 total_len += data.len();
150 if buf.len() < total_len {
151 return Err(());
152 }
153 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0005_u16.to_be_bytes());
154 buf[COMMAND_RANGE_DATA.start..COMMAND_RANGE_DATA.start + data.len()]
155 .copy_from_slice(&data);
156 }
157
158 Command::CloseUdpPort(port) => {
159 let data = port.to_be_bytes();
160 total_len += data.len();
161 if buf.len() < total_len {
162 return Err(());
163 }
164 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0006_u16.to_be_bytes());
165 buf[COMMAND_RANGE_DATA.start..COMMAND_RANGE_DATA.start + data.len()]
166 .copy_from_slice(&data);
167 }
168
169 Command::TransmitData {
170 destination_address,
171 source_port,
172 destination_port,
173 data,
174 } => {
175 if data.len() > 0x4d0 {
176 return Err(());
177 }
178 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0008_u16.to_be_bytes());
179
180 let mut data_len = 0;
181 for s in [
182 destination_address.to_be_bytes().as_slice(),
183 source_port.to_be_bytes().as_slice(),
184 destination_port.to_be_bytes().as_slice(),
185 (data.len() as u16).to_be_bytes().as_slice(),
186 data,
187 ] {
188 let new_data_len = data_len + s.len();
189 if buf.len() < total_len + new_data_len {
190 return Err(());
191 }
192 buf[total_len + data_len..total_len + new_data_len].copy_from_slice(s);
193 data_len = new_data_len;
194 }
195 total_len += data_len;
196 }
197
198 Command::DoActiveScan {
199 duration,
200 mask_channels,
201 pairing_id: None,
202 } => {
203 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0051_u16.to_be_bytes());
204
205 let mut data_len = 0;
206 for s in [
207 u8::from(*duration).to_be_bytes().as_slice(),
208 mask_channels.to_be_bytes().as_slice(),
209 0x00_u8.to_be_bytes().as_slice(),
210 ] {
211 let new_data_len = data_len + s.len();
212 if buf.len() < total_len + new_data_len {
213 return Err(());
214 }
215 buf[total_len + data_len..total_len + new_data_len].copy_from_slice(s);
216 data_len = new_data_len;
217 }
218 total_len += data_len;
219 }
220
221 Command::DoActiveScan {
222 duration,
223 mask_channels,
224 pairing_id: Some(pairing_id),
225 } => {
226 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0051_u16.to_be_bytes());
227
228 let mut data_len = 0;
229 for s in [
230 u8::from(*duration).to_be_bytes().as_slice(),
231 mask_channels.to_be_bytes().as_slice(),
232 0x01_u8.to_be_bytes().as_slice(),
233 pairing_id.to_be_bytes().as_slice(),
234 ] {
235 let new_data_len = data_len + s.len();
236 if buf.len() < total_len + new_data_len {
237 return Err(());
238 }
239 buf[total_len + data_len..total_len + new_data_len].copy_from_slice(s);
240 data_len = new_data_len;
241 }
242 total_len += data_len;
243 }
244
245 Command::GetVersionInformation => {
246 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x006b_u16.to_be_bytes())
247 }
248
249 Command::SetRouteBPanaAuthenticationInformation { id, password } => {
250 if id.len() != 32 || password.len() != 12 {
251 return Err(());
252 }
253
254 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0054_u16.to_be_bytes());
255
256 let mut data_len = 0;
257 for s in [id, password] {
258 let new_data_len = data_len + s.len();
259 if buf.len() < total_len + new_data_len {
260 return Err(());
261 }
262 buf[total_len + data_len..total_len + new_data_len].copy_from_slice(s);
263 data_len = new_data_len;
264 }
265 total_len += data_len;
266 }
267
268 Command::StartRouteBOperation => {
269 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0053_u16.to_be_bytes())
270 }
271
272 Command::StartRouteBPana => {
273 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0056_u16.to_be_bytes())
274 }
275
276 Command::TerminateRouteBPana => {
277 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0057_u16.to_be_bytes())
278 }
279
280 Command::TerminateRouteBOperation => {
281 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x0058_u16.to_be_bytes())
282 }
283
284 Command::RedoRouteBPanaAuthentication => {
285 buf[COMMAND_RANGE_COMMAND_CODE].copy_from_slice(&0x00d2_u16.to_be_bytes())
286 }
287 }
288
289 let message_len = (total_len - 4 - 2 - 2) as u16;
290 buf[COMMAND_RANGE_MESSAGE_LEN].copy_from_slice(&message_len.to_be_bytes());
291
292 let header_sum = buf[COMMAND_RANGE_UNIQUE_CODE.start..COMMAND_RANGE_HEADER_SUM.start]
293 .iter()
294 .fold(0_u16, |acc, &x| acc + x as u16);
295 buf[COMMAND_RANGE_HEADER_SUM].copy_from_slice(&header_sum.to_be_bytes());
296
297 let data_sum = buf[COMMAND_RANGE_DATA.start..total_len]
298 .iter()
299 .fold(0_u16, |acc, &x| acc + x as u16);
300 buf[COMMAND_RANGE_DATA_SUM].copy_from_slice(&data_sum.to_be_bytes());
301
302 Ok(total_len)
303}
304
305#[cfg(test)]
306mod tests {
307 extern crate std;
308 use crate::command::*;
309
310 #[test]
311 fn test_serialize_to_bytes() {
312 let mut buf = [0; 256];
313
314 assert_eq!(
315 serialize_to_bytes(&Command::GetStatus, &mut buf).map(|len| &buf[..len]),
316 Ok([
317 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x01_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x3e_u8, 0x00_u8, 0x00_u8, ]
323 .as_slice())
324 );
325
326 assert_eq!(
327 serialize_to_bytes(&Command::GetUdpPorts, &mut buf).map(|len| &buf[..len]),
328 Ok([
329 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x07_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x44_u8, 0x00_u8, 0x00_u8, ]
335 .as_slice())
336 );
337
338 assert_eq!(
339 serialize_to_bytes(&Command::GetIpAddress, &mut buf).map(|len| &buf[..len]),
340 Ok([
341 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x09_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x46_u8, 0x00_u8, 0x00_u8, ]
347 .as_slice())
348 );
349
350 assert_eq!(
351 serialize_to_bytes(&Command::GetMacAddress, &mut buf).map(|len| &buf[..len]),
352 Ok([
353 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x0e_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x4b_u8, 0x00_u8, 0x00_u8, ]
359 .as_slice())
360 );
361
362 assert_eq!(
363 serialize_to_bytes(&Command::GetClientMacAddress, &mut buf).map(|len| &buf[..len]),
364 Ok([
365 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x11_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x4e_u8, 0x00_u8, 0x00_u8, ]
371 .as_slice())
372 );
373
374 assert_eq!(
375 serialize_to_bytes(&Command::GetClientIpAddress, &mut buf).map(|len| &buf[..len]),
376 Ok([
377 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x01_u8, 0x00_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x3e_u8, 0x00_u8, 0x00_u8, ]
383 .as_slice())
384 );
385
386 assert_eq!(
387 serialize_to_bytes(&Command::GetNeighborDiscoverySettings, &mut buf)
388 .map(|len| &buf[..len]),
389 Ok([
390 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x01_u8, 0x02_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x40_u8, 0x00_u8, 0x00_u8, ]
396 .as_slice())
397 );
398
399 assert_eq!(
400 serialize_to_bytes(&Command::GetOperationMode, &mut buf).map(|len| &buf[..len]),
401 Ok([
402 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x01_u8, 0x07_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x45_u8, 0x00_u8, 0x00_u8, ]
408 .as_slice())
409 );
410
411 assert_eq!(
412 serialize_to_bytes(&Command::GetUartSettings, &mut buf).map(|len| &buf[..len]),
413 Ok([
414 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x01_u8, 0x0b_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x49_u8, 0x00_u8, 0x00_u8, ]
420 .as_slice())
421 );
422
423 assert_eq!(
424 serialize_to_bytes(
425 &Command::SetOperationMode {
426 mode: OperationMode::Dual,
427 han_sleep: false,
428 channel: Channel::Ch5F922p9MHz,
429 tx_power: TxPower::P20mW,
430 },
431 &mut buf,
432 )
433 .map(|len| &buf[..len]),
434 Ok([
435 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x5f_u8, 0x00_u8, 0x08_u8, 0x03_u8, 0xa0_u8, 0x00_u8, 0x0a_u8, 0x05_u8, 0x00_u8, 0x05_u8, 0x00_u8, ]
445 .as_slice())
446 );
447
448 assert_eq!(
449 serialize_to_bytes(
450 &Command::SetNeighborDiscoverySettings { enable: true },
451 &mut buf
452 )
453 .map(|len| &buf[..len]),
454 Ok([
455 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x01_u8, 0x01_u8, 0x00_u8, 0x05_u8, 0x03_u8, 0x40_u8, 0x00_u8, 0x01_u8, 0x01_u8, ]
462 .as_slice())
463 );
464
465 assert_eq!(
466 serialize_to_bytes(&Command::SetUartSettings { flow_control: true }, &mut buf)
467 .map(|len| &buf[..len]),
468 Ok([
469 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x01_u8, 0x0A_u8, 0x00_u8, 0x05_u8, 0x03_u8, 0x49_u8, 0x00_u8, 0x01_u8, 0x01_u8, ]
476 .as_slice())
477 );
478
479 assert_eq!(
480 serialize_to_bytes(&Command::OpenUdpPort(0x1234), &mut buf).map(|len| &buf[..len]),
481 Ok([
482 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x05_u8, 0x00_u8, 0x06_u8, 0x03_u8, 0x44_u8, 0x00_u8, 0x46_u8, 0x12_u8, 0x34_u8, ]
489 .as_slice())
490 );
491
492 assert_eq!(
493 serialize_to_bytes(&Command::CloseUdpPort(0x1234), &mut buf).map(|len| &buf[..len]),
494 Ok([
495 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x06_u8, 0x00_u8, 0x06_u8, 0x03_u8, 0x45_u8, 0x00_u8, 0x46_u8, 0x12_u8, 0x34_u8, ]
502 .as_slice())
503 );
504
505 assert_eq!(
506 serialize_to_bytes(
507 &Command::TransmitData {
508 destination_address: 0x123456789abcdef0123456789abcdef0,
509 source_port: 0xabcd,
510 destination_port: 0x1234,
511 data: &[0xde, 0xad, 0xbe, 0xef],
512 },
513 &mut buf
514 )
515 .map(|len| &buf[..len]),
516 Ok([
517 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x08_u8, 0x00_u8, 0x1e_u8, 0x03_u8, 0x5f_u8, 0x0d_u8, 0x6a_u8, 0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, 0x9a_u8, 0xbc_u8, 0xde_u8, 0xf0_u8, 0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, 0x9a_u8, 0xbc_u8, 0xde_u8, 0xf0_u8, 0xab_u8, 0xcd_u8, 0x12_u8, 0x34_u8, 0x00_u8, 0x04_u8, 0xde_u8, 0xad_u8, 0xbe_u8, 0xef_u8, ]
531 .as_slice())
532 );
533
534 assert_eq!(
535 serialize_to_bytes(
536 &Command::DoActiveScan {
537 duration: ScanDuration::T38p56ms,
538 mask_channels: 0x12345678,
539 pairing_id: None,
540 },
541 &mut buf
542 )
543 .map(|len| &buf[..len]),
544 Ok([
545 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x51_u8, 0x00_u8, 0x0a_u8, 0x03_u8, 0x94_u8, 0x01_u8, 0x16_u8, 0x02_u8, 0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, 0x00_u8, ]
554 .as_slice())
555 );
556
557 assert_eq!(
558 serialize_to_bytes(
559 &Command::DoActiveScan {
560 duration: ScanDuration::T38p56ms,
561 mask_channels: 0x12345678,
562 pairing_id: Some(0x12345678_9abcdef0),
563 },
564 &mut buf
565 )
566 .map(|len| &buf[..len]),
567 Ok([
568 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x51_u8, 0x00_u8, 0x12_u8, 0x03_u8, 0x9c_u8, 0x05_u8, 0x4f_u8, 0x02_u8, 0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, 0x01_u8, 0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, 0x9a_u8, 0xbc_u8, 0xde_u8, 0xf0_u8, ]
579 .as_slice())
580 );
581
582 assert_eq!(
583 serialize_to_bytes(&Command::GetVersionInformation, &mut buf).map(|len| &buf[..len]),
584 Ok([
585 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x6b_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0xa8_u8, 0x00_u8, 0x00_u8, ]
591 .as_slice())
592 );
593
594 assert_eq!(
595 serialize_to_bytes(
596 &Command::SetRouteBPanaAuthenticationInformation {
597 id: b"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
598 password: b"0123456789ab",
599 },
600 &mut buf,
601 )
602 .map(|len| &buf[..len]),
603 Ok([
604 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x54_u8, 0x00_u8, 0x30_u8, 0x03_u8, 0xbd_u8, 0x0b_u8, 0xde_u8, b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b',
616 ]
617 .as_slice())
618 );
619
620 assert_eq!(
621 serialize_to_bytes(&Command::StartRouteBOperation, &mut buf).map(|len| &buf[..len]),
622 Ok([
623 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x53_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x90_u8, 0x00_u8, 0x00_u8, ]
629 .as_slice())
630 );
631
632 assert_eq!(
633 serialize_to_bytes(&Command::StartRouteBPana, &mut buf).map(|len| &buf[..len]),
634 Ok([
635 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x56_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x93_u8, 0x00_u8, 0x00_u8, ]
641 .as_slice())
642 );
643
644 assert_eq!(
645 serialize_to_bytes(&Command::TerminateRouteBPana, &mut buf).map(|len| &buf[..len]),
646 Ok([
647 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x57_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x94_u8, 0x00_u8, 0x00_u8, ]
653 .as_slice())
654 );
655
656 assert_eq!(
657 serialize_to_bytes(&Command::TerminateRouteBOperation, &mut buf).map(|len| &buf[..len]),
658 Ok([
659 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0x58_u8, 0x00_u8, 0x04_u8, 0x03_u8, 0x95_u8, 0x00_u8, 0x00_u8, ]
665 .as_slice())
666 );
667
668 assert_eq!(
669 serialize_to_bytes(&Command::RedoRouteBPanaAuthentication, &mut buf)
670 .map(|len| &buf[..len]),
671 Ok([
672 0xd0_u8, 0xea_u8, 0x83_u8, 0xfc_u8, 0x00_u8, 0xd2_u8, 0x00_u8, 0x04_u8, 0x04_u8, 0x0f_u8, 0x00_u8, 0x00_u8, ]
678 .as_slice())
679 );
680 }
681}