ieee802154/mac/
command.rs

1//! MAC commands
2//!
3//! Work in progress
4
5use crate::mac::frame::{
6    header::{PanId, ShortAddress},
7    DecodeError,
8};
9use crate::utils::OptionalFrom;
10use byte::{check_len, BytesExt, TryRead, TryWrite};
11
12extended_enum!(
13    /// MAC command identifiers
14    CommandId, u8,
15    /// Association request, request association to PAN
16    AssociationRequest => 1,
17    /// Association response
18    AssociationResponse => 2,
19    /// Dissassociation notification
20    DisassociationNotification => 3,
21    /// Data request
22    DataRequest => 4,
23    /// PAN identifier conflict notification, sent from coordinator to offending device
24    PanIdConflictNotification => 5,
25    /// Orphan notification,
26    OrphanNotification => 6,
27    /// Beacon request, sent from a device which want to join a PAN
28    BeaconRequest => 7,
29    /// Coordinator re-alignment, the coordinator will change network parameters
30    CoordinatorRealignment => 8,
31    /// Guaranteed time slot request, request a guaranteed time slot
32    GuaranteedTimeSlotRequest => 9,
33);
34
35const CAP_FFD: u8 = 0x02;
36const CAP_MAINS_POWER: u8 = 0x04;
37const CAP_IDLE_RECEIVE: u8 = 0x08;
38const CAP_FRAME_PROTECTION: u8 = 0x40;
39const CAP_ALLOCATE_ADDRESS: u8 = 0x80;
40
41/// Association request capability information
42///
43/// Sent with association request to report the capabilities of the device.
44#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46pub struct CapabilityInformation {
47    /// Full-function device (FFD) or a reduced-function device (RFD)
48    /// RFD and FFD have different function sets.
49    pub full_function_device: bool,
50    /// Device is connected to a mains power source or not
51    pub mains_power: bool,
52    /// Receive is enabled while idle
53    pub idle_receive: bool,
54    /// Frames are cryptographically protected
55    pub frame_protection: bool,
56    /// Device wish to have an short address allocated by the coordinator
57    pub allocate_address: bool,
58}
59
60impl From<u8> for CapabilityInformation {
61    fn from(byte: u8) -> Self {
62        let full_function_device = byte & CAP_FFD == CAP_FFD;
63        let mains_power = byte & CAP_MAINS_POWER == CAP_MAINS_POWER;
64        let idle_receive = byte & CAP_IDLE_RECEIVE == CAP_IDLE_RECEIVE;
65        let frame_protection =
66            byte & CAP_FRAME_PROTECTION == CAP_FRAME_PROTECTION;
67        let allocate_address =
68            byte & CAP_ALLOCATE_ADDRESS == CAP_ALLOCATE_ADDRESS;
69        Self {
70            full_function_device,
71            mains_power,
72            idle_receive,
73            frame_protection,
74            allocate_address,
75        }
76    }
77}
78
79impl From<CapabilityInformation> for u8 {
80    fn from(ar: CapabilityInformation) -> Self {
81        let mut byte = 0u8;
82        if ar.full_function_device {
83            byte = byte | CAP_FFD;
84        }
85        if ar.mains_power {
86            byte = byte | CAP_MAINS_POWER;
87        }
88        if ar.idle_receive {
89            byte = byte | CAP_IDLE_RECEIVE;
90        }
91        if ar.frame_protection {
92            byte = byte | CAP_FRAME_PROTECTION;
93        }
94        if ar.allocate_address {
95            byte = byte | CAP_ALLOCATE_ADDRESS;
96        }
97        byte
98    }
99}
100
101extended_enum!(
102    /// Association Status
103    AssociationStatus, u8,
104    /// Successful
105    Successful => 0x00,
106    /// Network (PAN) at capacity
107    NetworkAtCapacity => 0x01,
108    /// Access to PAN denied
109    AccessDenied => 0x02,
110    /// Duplicate hopping sequence offset
111    HoppingSequenceOffsetDuplication => 0x03,
112    /// Fast association was successful
113    FastAssociationSuccesful => 0x80,
114);
115
116extended_enum!(
117    /// Disassociation Reason
118    DisassociationReason, u8,
119    /// Coordinator requested device to leave
120    CoordinatorLeave => 1,
121    /// Device requested to leave
122    DeviceLeave => 2,
123);
124
125/// Coordinator re-alignment data
126///
127/// Changes to the PAN sent by the coordinator.
128#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
129#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130pub struct CoordinatorRealignmentData {
131    /// PAN id that the coordinator will use
132    pub pan_id: PanId,
133    /// Short address that the coordinator will use
134    pub coordinator_address: ShortAddress,
135    /// Channel that the coordinator will use
136    pub channel: u8,
137    /// Device address or broadcast
138    pub device_address: ShortAddress,
139    /// Channel page or channel number the coordinator will use
140    pub channel_page: Option<u8>,
141}
142
143impl TryWrite for CoordinatorRealignmentData {
144    fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
145        let offset = &mut 0;
146        bytes.write(offset, self.pan_id)?;
147        bytes.write(offset, self.coordinator_address)?;
148        bytes.write(offset, self.channel)?;
149        bytes.write(offset, self.device_address)?;
150        if let Some(channel_page) = self.channel_page {
151            bytes.write(offset, channel_page)?;
152        }
153        Ok(*offset)
154    }
155}
156
157impl TryRead<'_> for CoordinatorRealignmentData {
158    fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
159        let offset = &mut 0;
160        check_len(&bytes, 7)?;
161        let pan_id = bytes.read(offset)?;
162        let coordinator_address = bytes.read(offset)?;
163        let channel = bytes.read(offset)?;
164        let device_address = bytes.read(offset)?;
165        let channel_page = if bytes.len() > *offset {
166            Some(bytes.read(offset)?)
167        } else {
168            None
169        };
170        Ok((
171            Self {
172                pan_id,
173                coordinator_address,
174                channel,
175                device_address,
176                channel_page,
177            },
178            *offset,
179        ))
180    }
181}
182
183const GTSC_RECEIVE_ONLY: u8 = 0x10;
184const GTSC_ALLOCATION: u8 = 0x20;
185
186/// Guaranteed time slot characteristics
187///
188/// GTS configuration requested with the guaranteed time slot request command.
189#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
190#[cfg_attr(feature = "defmt", derive(defmt::Format))]
191pub struct GuaranteedTimeSlotCharacteristics {
192    /// Number of slots requested
193    pub count: u8,
194    /// Receive only slots, otherwise transmit
195    pub receive_only: bool,
196    /// Request type, Allocate or de-allocate
197    pub allocation: bool,
198}
199
200impl From<u8> for GuaranteedTimeSlotCharacteristics {
201    fn from(byte: u8) -> Self {
202        let receive_only = byte & GTSC_RECEIVE_ONLY == GTSC_RECEIVE_ONLY;
203        let allocation = byte & GTSC_ALLOCATION == GTSC_ALLOCATION;
204        Self {
205            count: (byte & 0x0f),
206            receive_only,
207            allocation,
208        }
209    }
210}
211
212impl From<GuaranteedTimeSlotCharacteristics> for u8 {
213    fn from(gtsc: GuaranteedTimeSlotCharacteristics) -> Self {
214        let mut byte = gtsc.count & 0x0f;
215        if gtsc.receive_only {
216            byte = byte | GTSC_RECEIVE_ONLY;
217        }
218        if gtsc.allocation {
219            byte = byte | GTSC_ALLOCATION;
220        }
221        byte
222    }
223}
224
225/// MAC commands
226#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
227#[cfg_attr(feature = "defmt", derive(defmt::Format))]
228pub enum Command {
229    /// Association request, request association to a PAN
230    AssociationRequest(CapabilityInformation),
231    /// Association response, response to a association request
232    AssociationResponse(ShortAddress, AssociationStatus),
233    /// Notification of disassociation from the PAN
234    DisassociationNotification(DisassociationReason),
235    /// Request for data
236    DataRequest,
237    /// Notification of PAN idetifier conflict
238    PanIdConflictNotification,
239    /// Notification of orphan
240    OrphanNotification,
241    /// Request a beacon
242    BeaconRequest,
243    /// Coordinator re-alignment, the coordinator will change PAN parameters
244    CoordinatorRealignment(CoordinatorRealignmentData),
245    /// Request a guaranteed time slot (GTS)
246    GuaranteedTimeSlotRequest(GuaranteedTimeSlotCharacteristics),
247}
248
249impl TryWrite for Command {
250    fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
251        let offset = &mut 0;
252        match self {
253            Command::AssociationRequest(capability) => {
254                bytes.write(offset, u8::from(CommandId::AssociationRequest))?;
255                bytes.write(offset, u8::from(capability))?;
256            }
257            Command::AssociationResponse(address, status) => {
258                bytes
259                    .write(offset, u8::from(CommandId::AssociationResponse))?;
260                bytes.write(offset, address)?;
261                bytes.write(offset, u8::from(status))?;
262            }
263            Command::DisassociationNotification(reason) => {
264                bytes.write(
265                    offset,
266                    u8::from(CommandId::DisassociationNotification),
267                )?;
268                bytes.write(offset, u8::from(reason))?;
269            }
270            Command::DataRequest => {
271                bytes.write(offset, u8::from(CommandId::DataRequest))?;
272            }
273            Command::PanIdConflictNotification => {
274                bytes.write(
275                    offset,
276                    u8::from(CommandId::PanIdConflictNotification),
277                )?;
278            }
279            Command::OrphanNotification => {
280                bytes.write(offset, u8::from(CommandId::OrphanNotification))?;
281            }
282            Command::BeaconRequest => {
283                bytes.write(offset, u8::from(CommandId::BeaconRequest))?;
284            }
285            Command::CoordinatorRealignment(data) => {
286                bytes.write(
287                    offset,
288                    u8::from(CommandId::CoordinatorRealignment),
289                )?;
290                bytes.write(offset, data)?;
291            }
292            Command::GuaranteedTimeSlotRequest(characteristics) => {
293                bytes.write(
294                    offset,
295                    u8::from(CommandId::GuaranteedTimeSlotRequest),
296                )?;
297                bytes.write(offset, u8::from(characteristics))?;
298            }
299        }
300        Ok(*offset)
301    }
302}
303
304impl TryRead<'_> for Command {
305    fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
306        let offset = &mut 0;
307        let cmd = CommandId::optional_from(bytes.read::<u8>(offset)?)
308            .ok_or(DecodeError::InvalidValue)?;
309        Ok((
310            match cmd {
311                CommandId::AssociationRequest => {
312                    let capability =
313                        CapabilityInformation::from(bytes.read::<u8>(offset)?);
314                    Command::AssociationRequest(capability)
315                }
316                CommandId::AssociationResponse => {
317                    let address: ShortAddress = bytes.read(offset)?;
318                    let status =
319                        AssociationStatus::optional_from(bytes.read(offset)?)
320                            .ok_or(DecodeError::InvalidValue)?;
321                    Command::AssociationResponse(address, status)
322                }
323                CommandId::DisassociationNotification => {
324                    let reason = DisassociationReason::optional_from(
325                        bytes.read(offset)?,
326                    )
327                    .ok_or(DecodeError::InvalidValue)?;
328                    Command::DisassociationNotification(reason)
329                }
330                CommandId::DataRequest => Command::DataRequest,
331                CommandId::PanIdConflictNotification => {
332                    Command::PanIdConflictNotification
333                }
334                CommandId::OrphanNotification => Command::OrphanNotification,
335                CommandId::BeaconRequest => Command::BeaconRequest,
336                CommandId::CoordinatorRealignment => {
337                    Command::CoordinatorRealignment(bytes.read(offset)?)
338                }
339                CommandId::GuaranteedTimeSlotRequest => {
340                    let characteristics =
341                        GuaranteedTimeSlotCharacteristics::from(
342                            bytes.read::<u8>(offset)?,
343                        );
344                    Command::GuaranteedTimeSlotRequest(characteristics)
345                }
346            },
347            *offset,
348        ))
349    }
350}
351
352#[cfg(test)]
353mod tests {
354    use super::*;
355
356    #[test]
357    fn decode_association_request() {
358        let data = [0x01, 0x8e];
359        let mut len = 0usize;
360        let command: Command = data.read(&mut len).unwrap();
361        assert_eq!(len, data.len());
362        assert_eq!(
363            command,
364            Command::AssociationRequest(CapabilityInformation {
365                full_function_device: true,
366                mains_power: true,
367                idle_receive: true,
368                frame_protection: false,
369                allocate_address: true,
370            })
371        );
372    }
373
374    #[test]
375    fn encode_association_request() {
376        let command = Command::AssociationRequest(CapabilityInformation {
377            full_function_device: false,
378            mains_power: false,
379            idle_receive: false,
380            frame_protection: false,
381            allocate_address: false,
382        });
383        let mut data = [0u8; 32];
384        let mut len = 0usize;
385        data.write(&mut len, command).unwrap();
386
387        assert_eq!(len, 2);
388        assert_eq!(data[..len], [0x01, 0x00]);
389
390        let command = Command::AssociationRequest(CapabilityInformation {
391            full_function_device: true,
392            mains_power: false,
393            idle_receive: false,
394            frame_protection: false,
395            allocate_address: false,
396        });
397        let mut len = 0usize;
398        data.write(&mut len, command).unwrap();
399
400        assert_eq!(len, 2);
401        assert_eq!(data[..len], [0x01, 0x02]);
402
403        let command = Command::AssociationRequest(CapabilityInformation {
404            full_function_device: false,
405            mains_power: true,
406            idle_receive: false,
407            frame_protection: false,
408            allocate_address: false,
409        });
410        let mut len = 0usize;
411        data.write(&mut len, command).unwrap();
412
413        assert_eq!(len, 2);
414        assert_eq!(data[..len], [0x01, 0x04]);
415
416        let command = Command::AssociationRequest(CapabilityInformation {
417            full_function_device: false,
418            mains_power: false,
419            idle_receive: true,
420            frame_protection: false,
421            allocate_address: false,
422        });
423        let mut len = 0usize;
424        data.write(&mut len, command).unwrap();
425
426        assert_eq!(len, 2);
427        assert_eq!(data[..len], [0x01, 0x08]);
428
429        let command = Command::AssociationRequest(CapabilityInformation {
430            full_function_device: false,
431            mains_power: false,
432            idle_receive: false,
433            frame_protection: true,
434            allocate_address: false,
435        });
436        let mut len = 0usize;
437        data.write(&mut len, command).unwrap();
438
439        assert_eq!(len, 2);
440        assert_eq!(data[..len], [0x01, 0x40]);
441
442        let command = Command::AssociationRequest(CapabilityInformation {
443            full_function_device: false,
444            mains_power: false,
445            idle_receive: false,
446            frame_protection: false,
447            allocate_address: true,
448        });
449        let mut len = 0usize;
450        data.write(&mut len, command).unwrap();
451
452        assert_eq!(len, 2);
453        assert_eq!(data[..len], [0x01, 0x80]);
454    }
455
456    #[test]
457    fn decode_association_response() {
458        let data = [0x02, 0x40, 0x77, 0x00];
459        let mut len = 0usize;
460        let command: Command = data.read(&mut len).unwrap();
461        assert_eq!(len, data.len());
462        assert_eq!(
463            command,
464            Command::AssociationResponse(
465                ShortAddress(0x7740),
466                AssociationStatus::Successful
467            )
468        );
469
470        let data = [0x02, 0xaa, 0x55, 0x01];
471        let mut len = 0usize;
472        let command: Command = data.read(&mut len).unwrap();
473        assert_eq!(len, data.len());
474        assert_eq!(
475            command,
476            Command::AssociationResponse(
477                ShortAddress(0x55aa),
478                AssociationStatus::NetworkAtCapacity
479            )
480        );
481
482        let data = [0x02, 0x00, 0x00, 0x02];
483        let mut len = 0usize;
484        let command: Command = data.read(&mut len).unwrap();
485        assert_eq!(len, data.len());
486        assert_eq!(
487            command,
488            Command::AssociationResponse(
489                ShortAddress(0x0000),
490                AssociationStatus::AccessDenied
491            )
492        );
493
494        let data = [0x02, 0x00, 0x00, 0x03];
495        let mut len = 0usize;
496        let command: Command = data.read(&mut len).unwrap();
497        assert_eq!(len, data.len());
498        assert_eq!(
499            command,
500            Command::AssociationResponse(
501                ShortAddress(0x0000),
502                AssociationStatus::HoppingSequenceOffsetDuplication
503            )
504        );
505
506        let data = [0x02, 0x00, 0x00, 0x80];
507        let mut len = 0usize;
508        let command: Command = data.read(&mut len).unwrap();
509        assert_eq!(len, data.len());
510        assert_eq!(
511            command,
512            Command::AssociationResponse(
513                ShortAddress(0x0000),
514                AssociationStatus::FastAssociationSuccesful
515            )
516        );
517
518        let data = [0x02, 0x00, 0x00, 0x04];
519        let result = data.read::<Command>(&mut len);
520        assert!(result.is_err());
521
522        let data = [0x02, 0x00, 0x00, 0x7f];
523        let result = data.read::<Command>(&mut len);
524        assert!(result.is_err());
525
526        let data = [0x02, 0x00, 0x00, 0x81];
527        let result = data.read::<Command>(&mut len);
528        assert!(result.is_err());
529    }
530
531    #[test]
532    fn encode_association_response() {
533        let mut data = [0u8; 4];
534        let command = Command::AssociationResponse(
535            ShortAddress(0x55aa),
536            AssociationStatus::Successful,
537        );
538        let mut len = 0usize;
539        data.write(&mut len, command).unwrap();
540
541        assert_eq!(len, data.len());
542        assert_eq!(data[..len], [0x02, 0xaa, 0x55, 0x00]);
543
544        let command = Command::AssociationResponse(
545            ShortAddress(0x1234),
546            AssociationStatus::NetworkAtCapacity,
547        );
548        let mut len = 0usize;
549        data.write(&mut len, command).unwrap();
550
551        assert_eq!(len, data.len());
552        assert_eq!(data[..len], [0x02, 0x34, 0x12, 0x01]);
553
554        let command = Command::AssociationResponse(
555            ShortAddress(0xcffe),
556            AssociationStatus::AccessDenied,
557        );
558        let mut len = 0usize;
559        data.write(&mut len, command).unwrap();
560
561        assert_eq!(len, data.len());
562        assert_eq!(data[..len], [0x02, 0xfe, 0xcf, 0x02]);
563
564        let command = Command::AssociationResponse(
565            ShortAddress(0xfedc),
566            AssociationStatus::HoppingSequenceOffsetDuplication,
567        );
568        let mut len = 0usize;
569        data.write(&mut len, command).unwrap();
570
571        assert_eq!(len, data.len());
572        assert_eq!(data[..len], [0x02, 0xdc, 0xfe, 0x03]);
573
574        let command = Command::AssociationResponse(
575            ShortAddress(0x0ff0),
576            AssociationStatus::FastAssociationSuccesful,
577        );
578        let mut len = 0usize;
579        data.write(&mut len, command).unwrap();
580
581        assert_eq!(len, data.len());
582        assert_eq!(data[..len], [0x02, 0xf0, 0x0f, 0x80]);
583    }
584
585    #[test]
586    fn decode_disassociation_notification() {
587        let data = [0x03, 0x01];
588        let mut len = 0usize;
589        let command: Command = data.read(&mut len).unwrap();
590        assert_eq!(len, data.len());
591        assert_eq!(
592            command,
593            Command::DisassociationNotification(
594                DisassociationReason::CoordinatorLeave
595            )
596        );
597
598        let data = [0x03, 0x02];
599        let mut len = 0usize;
600        let command: Command = data.read(&mut len).unwrap();
601        assert_eq!(len, data.len());
602        assert_eq!(
603            command,
604            Command::DisassociationNotification(
605                DisassociationReason::DeviceLeave
606            )
607        );
608
609        let data = [0x03, 0x00];
610        let result = data.read::<Command>(&mut len);
611        assert!(result.is_err());
612
613        let data = [0x03, 0x03];
614        let result = data.read::<Command>(&mut len);
615        assert!(result.is_err());
616    }
617
618    #[test]
619    fn encode_disassociation_notification() {
620        let mut data = [0u8; 32];
621
622        let command = Command::DisassociationNotification(
623            DisassociationReason::CoordinatorLeave,
624        );
625        let mut len = 0usize;
626        data.write(&mut len, command).unwrap();
627
628        assert_eq!(len, 2);
629        assert_eq!(data[..len], [0x03, 0x01]);
630
631        let command = Command::DisassociationNotification(
632            DisassociationReason::DeviceLeave,
633        );
634        let mut len = 0usize;
635        data.write(&mut len, command).unwrap();
636
637        assert_eq!(len, 2);
638        assert_eq!(data[..len], [0x03, 0x02]);
639    }
640
641    #[test]
642    fn decode_coordinator_realignment() {
643        let data = [0x08, 0x23, 0x11, 0x01, 0x00, 0x0f, 0x34, 0x12];
644        let mut len = 0usize;
645        let command: Command = data.read(&mut len).unwrap();
646        assert_eq!(len, data.len());
647        assert_eq!(
648            command,
649            Command::CoordinatorRealignment(CoordinatorRealignmentData {
650                pan_id: PanId(0x1123),
651                coordinator_address: ShortAddress(0x0001),
652                channel: 15,
653                device_address: ShortAddress(0x1234),
654                channel_page: None,
655            })
656        );
657
658        let data = [0x08, 0x34, 0x12, 0x21, 0x43, 0x0b, 0xcd, 0xab, 0x01];
659        let mut len = 0usize;
660        let command: Command = data.read(&mut len).unwrap();
661        assert_eq!(len, data.len());
662        assert_eq!(
663            command,
664            Command::CoordinatorRealignment(CoordinatorRealignmentData {
665                pan_id: PanId(0x1234),
666                coordinator_address: ShortAddress(0x4321),
667                channel: 11,
668                device_address: ShortAddress(0xabcd),
669                channel_page: Some(1),
670            })
671        );
672    }
673
674    #[test]
675    fn encode_coordinator_realignment() {
676        let mut data = [0u8; 32];
677
678        let command =
679            Command::CoordinatorRealignment(CoordinatorRealignmentData {
680                pan_id: PanId(0x1123),
681                coordinator_address: ShortAddress(0x0001),
682                channel: 15,
683                device_address: ShortAddress(0x1234),
684                channel_page: None,
685            });
686        let mut len = 0usize;
687        data.write(&mut len, command).unwrap();
688
689        assert_eq!(len, 8);
690        assert_eq!(
691            &data[..len],
692            [0x08, 0x23, 0x11, 0x01, 0x00, 0x0f, 0x34, 0x12]
693        );
694
695        let command =
696            Command::CoordinatorRealignment(CoordinatorRealignmentData {
697                pan_id: PanId(0xbeef),
698                coordinator_address: ShortAddress(0xfeed),
699                channel: 26,
700                device_address: ShortAddress(0x1234),
701                channel_page: Some(15),
702            });
703        let mut len = 0usize;
704        data.write(&mut len, command).unwrap();
705
706        assert_eq!(len, 9);
707        assert_eq!(
708            &data[..len],
709            [0x08, 0xef, 0xbe, 0xed, 0xfe, 0x1a, 0x34, 0x12, 0x0f]
710        );
711    }
712
713    #[test]
714    fn decode_guaranteed_time_slot_request() {
715        let data = [0x09, 0x01];
716        let mut len = 0usize;
717        let command: Command = data.read(&mut len).unwrap();
718        assert_eq!(len, data.len());
719        assert_eq!(
720            command,
721            Command::GuaranteedTimeSlotRequest(
722                GuaranteedTimeSlotCharacteristics {
723                    count: 1,
724                    receive_only: false,
725                    allocation: false,
726                }
727            )
728        );
729
730        let data = [0x09, 0x12];
731        let mut len = 0usize;
732        let command: Command = data.read(&mut len).unwrap();
733        assert_eq!(len, data.len());
734        assert_eq!(
735            command,
736            Command::GuaranteedTimeSlotRequest(
737                GuaranteedTimeSlotCharacteristics {
738                    count: 2,
739                    receive_only: true,
740                    allocation: false,
741                }
742            )
743        );
744
745        let data = [0x09, 0x23];
746        let mut len = 0usize;
747        let command: Command = data.read(&mut len).unwrap();
748        assert_eq!(len, data.len());
749        assert_eq!(
750            command,
751            Command::GuaranteedTimeSlotRequest(
752                GuaranteedTimeSlotCharacteristics {
753                    count: 3,
754                    receive_only: false,
755                    allocation: true,
756                }
757            )
758        );
759    }
760
761    #[test]
762    fn encode_guaranteed_time_slot_request() {
763        let mut data = [0u8; 32];
764
765        let command = Command::GuaranteedTimeSlotRequest(
766            GuaranteedTimeSlotCharacteristics {
767                count: 1,
768                receive_only: false,
769                allocation: false,
770            },
771        );
772        let mut len = 0usize;
773        data.write(&mut len, command).unwrap();
774
775        assert_eq!(len, 2);
776
777        assert_eq!(data[..len], [0x09, 0x01]);
778
779        let command = Command::GuaranteedTimeSlotRequest(
780            GuaranteedTimeSlotCharacteristics {
781                count: 15,
782                receive_only: true,
783                allocation: false,
784            },
785        );
786        let mut len = 0usize;
787        data.write(&mut len, command).unwrap();
788
789        assert_eq!(len, 2);
790        assert_eq!(data[..len], [0x09, 0x1f]);
791
792        let command = Command::GuaranteedTimeSlotRequest(
793            GuaranteedTimeSlotCharacteristics {
794                count: 15,
795                receive_only: false,
796                allocation: true,
797            },
798        );
799        let mut len = 0usize;
800        data.write(&mut len, command).unwrap();
801
802        assert_eq!(len, 2);
803        assert_eq!(data[..len], [0x09, 0x2f]);
804    }
805
806    #[test]
807    fn decode_other_commands() {
808        let data = [0x04];
809        let mut len = 0usize;
810        let command: Command = data.read(&mut len).unwrap();
811        assert_eq!(len, data.len());
812        assert_eq!(command, Command::DataRequest);
813
814        let data = [0x05];
815        let mut len = 0usize;
816        let command: Command = data.read(&mut len).unwrap();
817        assert_eq!(len, data.len());
818        assert_eq!(command, Command::PanIdConflictNotification);
819
820        let data = [0x06];
821        let mut len = 0usize;
822        let command: Command = data.read(&mut len).unwrap();
823        assert_eq!(len, data.len());
824        assert_eq!(command, Command::OrphanNotification);
825
826        let data = [0x07];
827        let mut len = 0usize;
828        let command: Command = data.read(&mut len).unwrap();
829        assert_eq!(len, data.len());
830        assert_eq!(command, Command::BeaconRequest);
831    }
832
833    #[test]
834    fn encode_other_commands() {
835        let mut data = [0u8; 32];
836
837        let command = Command::DataRequest;
838        let mut len = 0usize;
839        data.write(&mut len, command).unwrap();
840
841        assert_eq!(len, 1);
842        assert_eq!(data[..len], [0x04]);
843
844        let command = Command::PanIdConflictNotification;
845        let mut len = 0usize;
846        data.write(&mut len, command).unwrap();
847
848        assert_eq!(len, 1);
849        assert_eq!(data[..len], [0x05]);
850
851        let command = Command::OrphanNotification;
852        let mut len = 0usize;
853        data.write(&mut len, command).unwrap();
854
855        assert_eq!(len, 1);
856        assert_eq!(data[..len], [0x06]);
857
858        let command = Command::BeaconRequest;
859        let mut len = 0usize;
860        data.write(&mut len, command).unwrap();
861
862        assert_eq!(len, 1);
863        assert_eq!(data[..len], [0x07]);
864    }
865}