Skip to main content

bt_rfcomm/frame/mux_commands/
mod.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use bitfield::bitfield;
6use packet_encoding::{Decodable, Encodable, decodable_enum};
7
8/// The DLC PN frame definition.
9mod dlc_parameter_negotiation;
10pub use dlc_parameter_negotiation::{
11    CreditBasedFlowHandshake, DEFAULT_INITIAL_CREDITS, ParameterNegotiationParams,
12};
13
14/// The Flow Control On/Off frame definitions.
15mod flow_control;
16pub use flow_control::FlowControlParams;
17
18/// The Modem Status frame definition.
19mod modem_status;
20pub use modem_status::ModemStatusParams;
21
22/// The Not-supported frame definition.
23mod non_supported;
24pub use non_supported::NonSupportedCommandParams;
25
26/// The Remote Line Status frame definition.
27mod remote_line_status;
28pub use remote_line_status::{RemoteLineStatusParams, RlsError};
29
30/// The Remote Port Negotiation frame definition.
31mod remote_port_negotiation;
32pub use remote_port_negotiation::RemotePortNegotiationParams;
33
34/// The Test Command frame definition.
35mod test_command;
36pub use test_command::TestCommandParams;
37
38use crate::DLCI;
39use crate::frame::{CommandResponse, FrameParseError};
40
41decodable_enum! {
42    /// The supported Multiplexer Commands in RFCOMM. These commands are sent/received
43    /// over the Multiplexer Control Channel (DLCI 0) and are 6 bits wide.
44    /// See RFCOMM 4.3.
45    pub enum MuxCommandMarker<u8, FrameParseError, OutOfRange> {
46        ParameterNegotiation = 0b100000,
47        Test = 0b001000,
48        FlowControlOn = 0b101000,
49        FlowControlOff = 0b011000,
50        ModemStatus = 0b111000,
51        NonSupportedCommand = 0b000100,
52        RemotePortNegotiation = 0b100100,
53        RemoteLineStatus = 0b010100,
54    }
55}
56
57/// The minimum size (in bytes) of a MuxCommand. 1 Byte for the Type field,
58/// and at least 1 byte for the Length field. See GSM 7.10 Section 5.4.6.1.
59const MIN_MUX_COMMAND_SIZE: usize = 2;
60
61/// The MuxCommand Type Field is the first byte in the payload.
62/// See GSM 7.10 Section 5.4.6.1.
63const MUX_COMMAND_TYPE_IDX: usize = 0;
64bitfield! {
65    struct TypeField(u8);
66    impl Debug;
67    pub bool, ea_bit, set_ea_bit: 0;
68    pub bool, cr_bit, set_cr_bit: 1;
69    pub u8, command_type_raw, set_command_type: 7, 2;
70}
71
72impl TypeField {
73    fn command_type(&self) -> Result<MuxCommandMarker, FrameParseError> {
74        MuxCommandMarker::try_from(self.command_type_raw())
75            .or(Err(FrameParseError::UnsupportedMuxCommandType(self.command_type_raw())))
76    }
77
78    fn command_response(&self) -> CommandResponse {
79        // C/R bit is always set for commands - see GSM 7.10 Section 5.4.6.2.
80        if self.cr_bit() { CommandResponse::Command } else { CommandResponse::Response }
81    }
82}
83
84/// The MuxCommand Length Field starts at the second byte in the payload.
85/// See GSM 7.10 Section 5.4.6.1.
86const MUX_COMMAND_LENGTH_IDX: usize = 1;
87
88/// The length field is represented as multiple E/A padded octets, each 7-bits wide.
89/// See GSM 7.10 Section 5.2.1.4.
90const MUX_COMMAND_LENGTH_SHIFT: usize = 7;
91
92bitfield! {
93    pub struct LengthField(u8);
94    impl Debug;
95    pub bool, ea_bit, set_ea_bit: 0;
96    pub u8, length, set_length: 7, 1;
97}
98
99/// The parameters associated with a Mux Command.
100#[derive(Clone, Debug, PartialEq)]
101pub enum MuxCommandParams {
102    ParameterNegotiation(ParameterNegotiationParams),
103    FlowControlOn(FlowControlParams),
104    FlowControlOff(FlowControlParams),
105    ModemStatus(ModemStatusParams),
106    NonSupported(NonSupportedCommandParams),
107    RemoteLineStatus(RemoteLineStatusParams),
108    RemotePortNegotiation(RemotePortNegotiationParams),
109    Test(TestCommandParams),
110}
111
112impl MuxCommandParams {
113    pub fn marker(&self) -> MuxCommandMarker {
114        match self {
115            Self::ParameterNegotiation(_) => MuxCommandMarker::ParameterNegotiation,
116            Self::FlowControlOn(_) => MuxCommandMarker::FlowControlOn,
117            Self::FlowControlOff(_) => MuxCommandMarker::FlowControlOff,
118            Self::ModemStatus(_) => MuxCommandMarker::ModemStatus,
119            Self::NonSupported(_) => MuxCommandMarker::NonSupportedCommand,
120            Self::RemoteLineStatus(_) => MuxCommandMarker::RemoteLineStatus,
121            Self::RemotePortNegotiation(_) => MuxCommandMarker::RemotePortNegotiation,
122            Self::Test(_) => MuxCommandMarker::Test,
123        }
124    }
125
126    fn decode(command_type: &MuxCommandMarker, buf: &[u8]) -> Result<Self, FrameParseError> {
127        let params = match command_type {
128            MuxCommandMarker::ParameterNegotiation => {
129                Self::ParameterNegotiation(ParameterNegotiationParams::decode(buf)?)
130            }
131            MuxCommandMarker::FlowControlOn => Self::FlowControlOn(FlowControlParams::decode(buf)?),
132            MuxCommandMarker::FlowControlOff => {
133                Self::FlowControlOff(FlowControlParams::decode(buf)?)
134            }
135            MuxCommandMarker::ModemStatus => Self::ModemStatus(ModemStatusParams::decode(buf)?),
136            MuxCommandMarker::NonSupportedCommand => {
137                Self::NonSupported(NonSupportedCommandParams::decode(buf)?)
138            }
139            MuxCommandMarker::RemoteLineStatus => {
140                Self::RemoteLineStatus(RemoteLineStatusParams::decode(buf)?)
141            }
142            MuxCommandMarker::RemotePortNegotiation => {
143                Self::RemotePortNegotiation(RemotePortNegotiationParams::decode(buf)?)
144            }
145            MuxCommandMarker::Test => Self::Test(TestCommandParams::decode(buf)?),
146        };
147        Ok(params)
148    }
149}
150
151impl Encodable for MuxCommandParams {
152    type Error = FrameParseError;
153
154    fn encoded_len(&self) -> usize {
155        match self {
156            Self::ParameterNegotiation(cmd) => cmd.encoded_len(),
157            Self::FlowControlOn(cmd) => cmd.encoded_len(),
158            Self::FlowControlOff(cmd) => cmd.encoded_len(),
159            Self::ModemStatus(cmd) => cmd.encoded_len(),
160            Self::NonSupported(cmd) => cmd.encoded_len(),
161            Self::RemoteLineStatus(cmd) => cmd.encoded_len(),
162            Self::RemotePortNegotiation(cmd) => cmd.encoded_len(),
163            Self::Test(cmd) => cmd.encoded_len(),
164        }
165    }
166
167    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
168        match self {
169            Self::ParameterNegotiation(cmd) => cmd.encode(buf),
170            Self::FlowControlOn(cmd) => cmd.encode(buf),
171            Self::FlowControlOff(cmd) => cmd.encode(buf),
172            Self::ModemStatus(cmd) => cmd.encode(buf),
173            Self::NonSupported(cmd) => cmd.encode(buf),
174            Self::RemoteLineStatus(cmd) => cmd.encode(buf),
175            Self::RemotePortNegotiation(cmd) => cmd.encode(buf),
176            Self::Test(cmd) => cmd.encode(buf),
177        }
178    }
179}
180
181/// Converts the `length` into a vector of E/A padded octets in conformance with the message
182/// format defined in GSM 7.10 Section 5.4.6.1.
183fn length_to_ea_format(mut length: usize) -> Vec<u8> {
184    if length == 0 {
185        // Return single octet with 0 length.
186        let mut length_field = LengthField(0);
187        length_field.set_ea_bit(true);
188        return vec![length_field.0];
189    }
190
191    let mut octets = vec![];
192    let mask = 0b01111111;
193    // Process the length field in chunks of 7 - E/A bit = 0 for all non-last octets.
194    while length != 0 {
195        let mut length_field = LengthField(0);
196        let length_octet = (length & mask) as u8;
197        length_field.set_length(length_octet);
198        length_field.set_ea_bit(false);
199        octets.push(length_field);
200        length >>= MUX_COMMAND_LENGTH_SHIFT;
201    }
202    // Tag the last length octet with E/A = 1, signifying last octet.
203    let last_idx = octets.len() - 1;
204    octets[last_idx].set_ea_bit(true);
205
206    octets.iter().map(|l| l.0).collect()
207}
208
209/// The unique identifier associated with a MuxCommand.
210/// Some MuxCommands are associated with a DLCI - we uniquely identify such a command by it's
211/// optional DLCI and command type.
212#[derive(Debug, Eq, Hash, PartialEq)]
213pub struct MuxCommandIdentifier(pub Option<DLCI>, pub MuxCommandMarker);
214
215/// Represents an RFCOMM multiplexer command.
216#[derive(Clone, Debug, PartialEq)]
217pub struct MuxCommand {
218    /// The parameters associated with this MuxCommand - see RFCOMM 4.3 for the supported commands.
219    pub params: MuxCommandParams,
220
221    /// Whether this is a command or response Mux frame.
222    pub command_response: CommandResponse,
223}
224
225impl MuxCommand {
226    /// Returns the DLCI that the MuxCommand is referring to.
227    ///
228    /// Note: This is different from the DLCI that the MuxCommand is sent over. All RFCOMM Mux
229    /// Commands are sent over the Mux Control DLCI. The DLCI that the command itself is referring
230    /// to is different. Some commands are generic (i.e Test Command) and do not refer to a
231    /// particular DLCI; in these cases, None is returned.
232    pub fn dlci(&self) -> Option<DLCI> {
233        match &self.params {
234            MuxCommandParams::ParameterNegotiation(pn) => Some(pn.dlci),
235            MuxCommandParams::ModemStatus(status) => Some(status.dlci),
236            MuxCommandParams::RemoteLineStatus(rls) => Some(rls.dlci),
237            MuxCommandParams::RemotePortNegotiation(rpn) => Some(rpn.dlci),
238            _ => None,
239        }
240    }
241
242    /// Returns the "identifier" for this MuxCommand. Namely, it is the combination
243    /// of the (optional) DLCI for this command and its command type.
244    pub fn identifier(&self) -> MuxCommandIdentifier {
245        MuxCommandIdentifier(self.dlci(), self.params.marker())
246    }
247}
248
249impl Decodable for MuxCommand {
250    type Error = FrameParseError;
251
252    fn decode(buf: &[u8]) -> Result<Self, FrameParseError> {
253        if buf.len() < MIN_MUX_COMMAND_SIZE {
254            return Err(FrameParseError::BufferTooSmall);
255        }
256
257        // Parse the Type Field from the buffer.
258        let type_field = TypeField(buf[MUX_COMMAND_TYPE_IDX]);
259        let command_response = type_field.command_response();
260        let command_type = type_field.command_type()?;
261
262        // Parse the Length Field from the buffer. There can be multiple octets.
263        // This implementation supports lengths that can fit into a u64 (8 octets) - in practice,
264        // most lengths should fit in 1 octet. The Test Command is the only variable-length command.
265        const MAX_LENGTH_OCTETS: usize = 8;
266        let mut length: u64 = 0;
267        let mut num_length_octets: usize = 0;
268
269        for i in MUX_COMMAND_LENGTH_IDX..buf.len() {
270            let length_field = LengthField(buf[i]);
271            let length_octet: u64 = length_field.length().into();
272            length |= length_octet << (MUX_COMMAND_LENGTH_SHIFT * num_length_octets);
273            num_length_octets += 1;
274
275            // A set EA bit indicates last octet of the length.
276            if length_field.ea_bit() {
277                break;
278            }
279
280            if num_length_octets > MAX_LENGTH_OCTETS {
281                return Err(FrameParseError::InvalidFrame);
282            }
283        }
284
285        // Validate that the buffer is large enough given the variable length.
286        // 1 byte for Type field, `num_length_octets` bytes for the length field, and `length`
287        // bytes for the data.
288        let header_len = 1 + num_length_octets;
289        let calculated_buf_size: u64 = header_len as u64 + length;
290        if (buf.len() as u64) < calculated_buf_size {
291            return Err(FrameParseError::BufferTooSmall);
292        }
293
294        let params_payload = &buf[header_len..calculated_buf_size as usize];
295        let params = MuxCommandParams::decode(&command_type, params_payload)?;
296
297        Ok(Self { params, command_response })
298    }
299}
300
301impl Encodable for MuxCommand {
302    type Error = FrameParseError;
303
304    /// Returns the encoded length of the command.
305    /// - 1 Byte for Type field + n bytes to encode the length + `length` bytes for the payload.
306    fn encoded_len(&self) -> usize {
307        let length = self.params.encoded_len();
308        1 + length_to_ea_format(length).len() + length
309    }
310
311    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
312        if buf.len() < self.encoded_len() {
313            return Err(FrameParseError::BufferTooSmall);
314        }
315
316        // The CommandResponse bit for the type field is set according to GSM 7.10 Section 5.4.6.2.
317        let cr_bit = if self.command_response == CommandResponse::Command { true } else { false };
318
319        // Type Field: E/A = 1 always.
320        let mut type_field = TypeField(0);
321        type_field.set_ea_bit(true);
322        type_field.set_cr_bit(cr_bit);
323        type_field.set_command_type(u8::from(&self.params.marker()));
324        buf[0] = type_field.0;
325
326        // Length Field.
327        let length = self.params.encoded_len();
328        let length_octets = length_to_ea_format(length);
329        let length_end_idx = MUX_COMMAND_LENGTH_IDX + length_octets.len();
330        buf[MUX_COMMAND_LENGTH_IDX..length_end_idx].copy_from_slice(&length_octets);
331
332        // Payload of the MuxCommand.
333        self.params.encode(&mut buf[length_end_idx..])?;
334
335        Ok(())
336    }
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    use assert_matches::assert_matches;
344    use modem_status::ModemStatusSignals;
345
346    #[test]
347    fn test_decode_mux_command_empty_buf() {
348        let empty_buf = [];
349        assert_matches!(MuxCommand::decode(&empty_buf[..]), Err(FrameParseError::BufferTooSmall));
350    }
351
352    #[test]
353    fn test_decode_mux_command_invalid_command_type() {
354        let buf = [
355            0b11111101, // Type field, unrecognized command type.
356            0b00000001, // Length field, 0 length.
357        ];
358        assert_matches!(
359            MuxCommand::decode(&buf[..]),
360            Err(FrameParseError::UnsupportedMuxCommandType(0b111111))
361        );
362    }
363
364    #[test]
365    fn test_decode_mux_command_too_large_length() {
366        let buf = [
367            0b10000001, // Type field - DLCParameterNegotiation.
368            0b00000010, // Length field, 9 octets - this is too large.
369            0b00000010, // Length field, octet 2.
370            0b00000010, // Length field, octet 3.
371            0b00000010, // Length field, octet 4.
372            0b00000010, // Length field, octet 5.
373            0b00000010, // Length field, octet 6.
374            0b00000010, // Length field, octet 7.
375            0b00000010, // Length field, octet 8.
376            0b00000010, // Length field, octet 9.
377            0b00000000, // Length values - shouldn't matter since we error early.
378        ];
379        assert_matches!(MuxCommand::decode(&buf[..]), Err(FrameParseError::InvalidFrame));
380    }
381
382    #[test]
383    fn test_decode_mux_command_missing_length_octets() {
384        let buf = [
385            0b10000001, // Type field - DLCParameterNegotiation.
386            0b00000101, // Length field, Length = 2.
387            0b00000010, // Length data octet 1.
388                        // Missing octet 2 of length.
389        ];
390        assert_matches!(MuxCommand::decode(&buf[..]), Err(FrameParseError::BufferTooSmall));
391    }
392
393    #[test]
394    fn test_decode_dlc_parameter_negotiation_command() {
395        let buf = [
396            0b10000011, // Type field - DLCParameterNegotiation, C/R = Command.
397            0b00010001, // Length field - 1 octet. Length = 8.
398            0b00000000, // Data Octet1: DLCI of 0 is OK.
399            0b11110000, // Data Octet2: SupportedRequest.
400            0b00001100, // Data Octet3: Priority = 12.
401            0b00000000, // Data Octet4: Ignored.
402            0b00010100, // Data Octet5: Max Frame Size Octet 1 = 20.
403            0b00000000, // Data Octet6: Max Frame Size Octet 2 = 0.
404            0b00000000, // Data Octet7: Ignored
405            0b00000001, // Data Octet8: Initial Credits = 1.
406        ];
407        let expected = MuxCommand {
408            params: MuxCommandParams::ParameterNegotiation(ParameterNegotiationParams {
409                dlci: DLCI::try_from(0).unwrap(),
410                credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
411                priority: 12,
412                max_frame_size: 20,
413                initial_credits: 1,
414            }),
415            command_response: CommandResponse::Command,
416        };
417        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
418    }
419
420    #[test]
421    fn test_decode_test_command() {
422        let buf = [
423            0b00100011, // Type field - Test Command, C/R = Command, E/A = 1.
424            0b00001001, // Length field - 1 octet. Length = 4.
425            0b00000000, // Data Octet1: Test pattern.
426            0b00000000, // Data Octet2: Test pattern.
427            0b00000000, // Data Octet3: Test pattern.
428            0b00000000, // Data Octet4: Test pattern.
429        ];
430        let expected = MuxCommand {
431            params: MuxCommandParams::Test(TestCommandParams {
432                test_pattern: vec![0x00, 0x00, 0x00, 0x00],
433            }),
434            command_response: CommandResponse::Command,
435        };
436        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
437    }
438
439    #[test]
440    fn test_decode_flow_control_on_command() {
441        let buf = [
442            0b10100001, // Type field - Test Command, C/R = Response.
443            0b00000001, // Length field - 1 octet. Length = 0.
444        ];
445        let expected = MuxCommand {
446            params: MuxCommandParams::FlowControlOn(FlowControlParams {}),
447            command_response: CommandResponse::Response,
448        };
449        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
450    }
451
452    #[test]
453    fn test_decode_flow_control_off_command() {
454        let buf = [
455            0b01100011, // Type field - Test Command, C/R = Command.
456            0b00000001, // Length field - 1 octet. Length = 0.
457        ];
458        let expected = MuxCommand {
459            params: MuxCommandParams::FlowControlOff(FlowControlParams {}),
460            command_response: CommandResponse::Command,
461        };
462        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
463    }
464
465    #[test]
466    fn test_decode_modem_status_command() {
467        let buf = [
468            0b11100011, // Type field - Test Command, C/R = Command.
469            0b00000111, // Length field - 1 octet. Length = 3.
470            0b00001111, // Data Octet1: DLCI = 3, E/A = 1, Bit2 = 1 always.
471            0b00000000, // Data Octet2: Signals = 0, E/A = 0 -> Break.
472            0b00000001, // Data Octet3: E/A = 1, B1 = 0 -> no break.
473        ];
474        let expected = MuxCommand {
475            params: MuxCommandParams::ModemStatus(ModemStatusParams {
476                dlci: DLCI::try_from(3).unwrap(),
477                signals: ModemStatusSignals(0),
478                break_value: None,
479            }),
480            command_response: CommandResponse::Command,
481        };
482        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
483    }
484
485    #[test]
486    fn test_decode_not_supported_command_response() {
487        let buf = [
488            0b00010001, // Type field - Test Command, C/R = 0 (Response).
489            0b00000011, // Length field - 1 octet. Length = 1.
490            0b00000001, // Data octet 1: Command Type = 0 (unknown).
491        ];
492        let expected = MuxCommand {
493            params: MuxCommandParams::NonSupported(NonSupportedCommandParams {
494                cr_bit: false,
495                non_supported_command: 0,
496            }),
497            command_response: CommandResponse::Response,
498        };
499        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
500    }
501
502    #[test]
503    fn test_decode_rpn_command() {
504        let buf = [
505            0b10010011, // Type field - RPN Command, C/R = Command.
506            0b00000011, // Length field - 1 octet. Length = 1.
507            0b00011111, // Data octet1: DLCI = 7 is OK, E/A = 1, Bit2 = 1 always.
508        ];
509        let expected = MuxCommand {
510            params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
511                dlci: DLCI::try_from(7).unwrap(),
512                port_values: None,
513            }),
514            command_response: CommandResponse::Command,
515        };
516        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
517    }
518
519    #[test]
520    fn test_decode_rls_command() {
521        let buf = [
522            0b01010011, // Type field - RLS Command, C/R = Command.
523            0b00000101, // Length field - 1 octet. Length = 2.
524            0b00011111, // Data octet1: DLCI = 7, E/A = 1, Bit2 = 1 always.
525            0b00000000, // Data octet2: Bit1 = 0 -> No Status.
526        ];
527        let expected = MuxCommand {
528            params: MuxCommandParams::RemoteLineStatus(RemoteLineStatusParams::new(
529                DLCI::try_from(7).unwrap(),
530                None,
531            )),
532            command_response: CommandResponse::Command,
533        };
534        assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
535    }
536
537    #[test]
538    fn test_encode_invalid_buffer() {
539        let command = MuxCommand {
540            params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
541                dlci: DLCI::try_from(5).unwrap(),
542                port_values: None,
543            }),
544            command_response: CommandResponse::Command,
545        };
546        let mut buf = vec![];
547        assert_matches!(command.encode(&mut buf[..]), Err(FrameParseError::BufferTooSmall));
548    }
549
550    #[test]
551    fn test_encode_rpn_response() {
552        let command = MuxCommand {
553            params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
554                dlci: DLCI::try_from(5).unwrap(),
555                port_values: None,
556            }),
557            command_response: CommandResponse::Response,
558        };
559        let mut buf = vec![0; command.encoded_len()];
560        assert!(command.encode(&mut buf[..]).is_ok());
561        let expected = vec![
562            0b10010001, // CommandType = RPN, C/R = 0 (Response), E/A = 1.
563            0b00000011, // Length = 1, E/A = 1.
564            0b00010111, // Payload = RPN Command Params.
565        ];
566        assert_eq!(buf, expected);
567    }
568
569    /// Test Command is the only command with a variable payload length for the parameters.
570    /// Tests encoding a Test Command with a very long payload.
571    #[test]
572    fn test_encode_long_test_command() {
573        // 128 byte pattern - requires two octets for length.
574        let test_pattern = vec![
575            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
576            0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
577            0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
578            0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
579            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
580            0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03,
581            0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01,
582            0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
583            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
584            0x0e, 0x0f,
585        ];
586        let command = MuxCommand {
587            params: MuxCommandParams::Test(TestCommandParams {
588                test_pattern: test_pattern.clone(),
589            }),
590            command_response: CommandResponse::Command,
591        };
592        let mut buf = vec![0; command.encoded_len()];
593        assert!(command.encode(&mut buf[..]).is_ok());
594        let expected = vec![
595            0b00100011, // CommandType = Test, C/R = 1 (Command), E/A = 1.
596            0b00000000, // Length Octet1 = 0, E/A = 0.
597            0b00000011, // Length Octet2 = 128, E/A = 1.
598        ];
599        let expected = vec![expected, test_pattern].concat();
600        assert_eq!(buf, expected);
601    }
602
603    #[test]
604    fn test_zero_length_to_ea_format() {
605        let length = 0;
606        let expected = [0b00000001];
607        assert_eq!(length_to_ea_format(length), expected);
608    }
609
610    #[test]
611    fn test_small_length_to_ea_format() {
612        let small_length = 8;
613        let expected = [0b00010001];
614        assert_eq!(length_to_ea_format(small_length), expected);
615    }
616
617    #[test]
618    fn test_two_octet_length_to_ea_format() {
619        let two_octet_length = 245;
620        let expected = [0b11101010, 0b00000011];
621        assert_eq!(length_to_ea_format(two_octet_length), expected);
622    }
623
624    #[test]
625    fn test_multi_octet_length_to_ea_format() {
626        let multi_octet_length = 0b100000001100001001000;
627        let expected = [0b10010000, 0b01100000, 0b10000001];
628        assert_eq!(length_to_ea_format(multi_octet_length), expected);
629    }
630}