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