bt_rfcomm/frame/
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 packet_encoding::{decodable_enum, Decodable, Encodable};
6
7/// The command or response classification used when parsing an RFCOMM frame.
8mod command_response;
9pub use command_response::CommandResponse;
10/// Errors associated with parsing an RFCOMM frame.
11mod error;
12pub use error::FrameParseError;
13/// Frame Check Sequence calculations.
14mod fcs;
15/// Field definitions for an RFCOMM frame.
16mod field;
17/// Definitions for multiplexer command frames.
18pub mod mux_commands;
19
20use self::fcs::{calculate_fcs, verify_fcs};
21use self::field::*;
22use self::mux_commands::MuxCommand;
23use crate::{Role, DLCI};
24
25decodable_enum! {
26    /// The type of frame provided in the Control field.
27    /// The P/F bit is set to 0 for all frame types.
28    /// See table 2, GSM 07.10 Section 5.2.1.3 and RFCOMM 4.2.
29    pub enum FrameTypeMarker<u8, FrameParseError, UnsupportedFrameType> {
30        SetAsynchronousBalancedMode = 0b00101111,
31        UnnumberedAcknowledgement = 0b01100011,
32        DisconnectedMode = 0b00001111,
33        Disconnect = 0b01000011,
34        UnnumberedInfoHeaderCheck = 0b11101111,
35    }
36}
37
38impl FrameTypeMarker {
39    /// Returns true if the frame type is a valid multiplexer start-up frame.
40    //
41    /// These are the only frames which are allowed to be sent before the multiplexer starts, and
42    /// must be sent over the Mux Control Channel.
43    fn is_mux_startup(&self, dlci: &DLCI) -> bool {
44        dlci.is_mux_control()
45            && (*self == FrameTypeMarker::SetAsynchronousBalancedMode
46                || *self == FrameTypeMarker::UnnumberedAcknowledgement
47                || *self == FrameTypeMarker::DisconnectedMode)
48    }
49
50    /// Returns the number of octets needed when calculating the FCS.
51    fn fcs_octets(&self) -> usize {
52        // For UIH frames, the first 2 bytes of the buffer are used to calculate the FCS.
53        // Otherwise, the first 3. Defined in RFCOMM 5.1.1.
54        if *self == FrameTypeMarker::UnnumberedInfoHeaderCheck {
55            2
56        } else {
57            3
58        }
59    }
60
61    /// Returns true if the `frame_type` is expected to contain a credit octet.
62    ///
63    /// `credit_based_flow` indicates whether credit flow control is enabled for the session.
64    /// `poll_final` is the P/F bit associated with the Control Field of the frame.
65    /// `dlci` is the DLCI associated with the frame.
66    ///
67    /// RFCOMM 6.5.2 describes the RFCOMM specifics for credit-based flow control. Namely,
68    /// "...It does not apply to DLCI 0 or to non-UIH frames."
69    fn has_credit_octet(&self, credit_based_flow: bool, poll_final: bool, dlci: DLCI) -> bool {
70        *self == FrameTypeMarker::UnnumberedInfoHeaderCheck
71            && !dlci.is_mux_control()
72            && credit_based_flow
73            && poll_final
74    }
75}
76
77/// A UIH Frame that contains user data.
78#[derive(Clone, Debug, PartialEq)]
79pub struct UserData {
80    pub information: Vec<u8>,
81}
82
83impl UserData {
84    pub fn is_empty(&self) -> bool {
85        self.information.is_empty()
86    }
87
88    pub fn empty() -> Self {
89        Self { information: vec![] }
90    }
91}
92
93impl Decodable for UserData {
94    type Error = FrameParseError;
95
96    fn decode(buf: &[u8]) -> Result<Self, FrameParseError> {
97        Ok(Self { information: buf.to_vec() })
98    }
99}
100
101impl Encodable for UserData {
102    type Error = FrameParseError;
103
104    fn encoded_len(&self) -> usize {
105        self.information.len()
106    }
107
108    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
109        if buf.len() < self.encoded_len() {
110            return Err(FrameParseError::BufferTooSmall);
111        }
112        buf.copy_from_slice(&self.information);
113        Ok(())
114    }
115}
116
117/// The data associated with a UIH Frame.
118#[derive(Clone, Debug, PartialEq)]
119pub enum UIHData {
120    /// A UIH Frame with user data.
121    User(UserData),
122    /// A UIH Frame with a Mux Command.
123    Mux(MuxCommand),
124}
125
126impl Encodable for UIHData {
127    type Error = FrameParseError;
128
129    fn encoded_len(&self) -> usize {
130        match self {
131            UIHData::User(data) => data.encoded_len(),
132            UIHData::Mux(command) => command.encoded_len(),
133        }
134    }
135
136    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
137        if buf.len() < self.encoded_len() {
138            return Err(FrameParseError::BufferTooSmall);
139        }
140
141        match self {
142            UIHData::User(data) => data.encode(buf),
143            UIHData::Mux(command) => command.encode(buf),
144        }
145    }
146}
147
148/// The types of frames supported in RFCOMM.
149/// See RFCOMM 4.2 for the supported frame types.
150#[derive(Clone, Debug, PartialEq)]
151pub enum FrameData {
152    SetAsynchronousBalancedMode,
153    UnnumberedAcknowledgement,
154    DisconnectedMode,
155    Disconnect,
156    UnnumberedInfoHeaderCheck(UIHData),
157}
158
159impl FrameData {
160    pub fn marker(&self) -> FrameTypeMarker {
161        match self {
162            FrameData::SetAsynchronousBalancedMode => FrameTypeMarker::SetAsynchronousBalancedMode,
163            FrameData::UnnumberedAcknowledgement => FrameTypeMarker::UnnumberedAcknowledgement,
164            FrameData::DisconnectedMode => FrameTypeMarker::DisconnectedMode,
165            FrameData::Disconnect => FrameTypeMarker::Disconnect,
166            FrameData::UnnumberedInfoHeaderCheck(_) => FrameTypeMarker::UnnumberedInfoHeaderCheck,
167        }
168    }
169
170    fn decode(
171        frame_type: &FrameTypeMarker,
172        dlci: &DLCI,
173        buf: &[u8],
174    ) -> Result<Self, FrameParseError> {
175        let data = match frame_type {
176            FrameTypeMarker::SetAsynchronousBalancedMode => FrameData::SetAsynchronousBalancedMode,
177            FrameTypeMarker::UnnumberedAcknowledgement => FrameData::UnnumberedAcknowledgement,
178            FrameTypeMarker::DisconnectedMode => FrameData::DisconnectedMode,
179            FrameTypeMarker::Disconnect => FrameData::Disconnect,
180            FrameTypeMarker::UnnumberedInfoHeaderCheck => {
181                let uih_data = if dlci.is_mux_control() {
182                    UIHData::Mux(MuxCommand::decode(buf)?)
183                } else {
184                    UIHData::User(UserData::decode(buf)?)
185                };
186                FrameData::UnnumberedInfoHeaderCheck(uih_data)
187            }
188        };
189        Ok(data)
190    }
191}
192
193impl Encodable for FrameData {
194    type Error = FrameParseError;
195
196    fn encoded_len(&self) -> usize {
197        match self {
198            FrameData::SetAsynchronousBalancedMode
199            | FrameData::UnnumberedAcknowledgement
200            | FrameData::DisconnectedMode
201            | FrameData::Disconnect => 0,
202            FrameData::UnnumberedInfoHeaderCheck(data) => data.encoded_len(),
203        }
204    }
205
206    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
207        if buf.len() < self.encoded_len() {
208            return Err(FrameParseError::BufferTooSmall);
209        }
210
211        match self {
212            FrameData::SetAsynchronousBalancedMode
213            | FrameData::UnnumberedAcknowledgement
214            | FrameData::DisconnectedMode
215            | FrameData::Disconnect => Ok(()),
216            FrameData::UnnumberedInfoHeaderCheck(data) => data.encode(buf),
217        }
218    }
219}
220
221/// The minimum frame size (bytes) for an RFCOMM Frame - Address, Control, Length, FCS.
222/// See RFCOMM 5.1.
223const MIN_FRAME_SIZE: usize = 4;
224
225/// The maximum size (bytes) of an RFCOMM header in a packet.
226/// Address (1 byte), Control (1 byte), Length (2 bytes), Credits (1 byte), FCS (1 byte)
227/// See RFCOMM 5.1.
228pub const MAX_RFCOMM_HEADER_SIZE: usize = 6;
229
230/// The maximum length that can be represented in a single E/A padded octet.
231const MAX_SINGLE_OCTET_LENGTH: usize = 127;
232
233/// Returns true if the provided `length` needs to be represented as 2 octets.
234fn is_two_octet_length(length: usize) -> bool {
235    length > MAX_SINGLE_OCTET_LENGTH
236}
237
238/// Returns the C/R bit for a non-UIH frame.
239fn cr_bit_for_non_uih_frame(role: Role, command_response: CommandResponse) -> bool {
240    // Defined in GSM Section 5.2.1.2 Table 1.
241    match (role, command_response) {
242        (Role::Initiator, CommandResponse::Command)
243        | (Role::Responder, CommandResponse::Response) => true,
244        _ => false,
245    }
246}
247
248/// Returns the C/R bit for a UIH frame.
249/// This must only be used on frames sent after multiplexer startup.
250fn cr_bit_for_uih_frame(role: Role) -> bool {
251    // The C/R bit is based on subclause 5.4.3.1 and matches the role of the device.
252    match role {
253        Role::Initiator => true,
254        _ => false,
255    }
256}
257
258/// The highest-level unit of data that is passed around in RFCOMM.
259#[derive(Clone, Debug, PartialEq)]
260pub struct Frame {
261    /// The role of the device associated with this frame.
262    pub role: Role,
263    /// The DLCI associated with this frame.
264    pub dlci: DLCI,
265    /// The data associated with this frame.
266    pub data: FrameData,
267    /// The P/F bit for this frame. See RFCOMM 5.2.1 which describes the usages
268    /// of the P/F bit in RFCOMM.
269    pub poll_final: bool,
270    /// Whether this frame is a Command or Response frame.
271    pub command_response: CommandResponse,
272    /// The credits associated with this frame. Credits are only applicable to UIH frames
273    /// when credit-based flow control is enabled. See RFCOMM 6.5.
274    pub credits: Option<u8>,
275}
276
277impl Frame {
278    /// Attempts to parse the provided `buf` into a Frame.
279    ///
280    /// `role` is the current Role of the RFCOMM Session.
281    /// `credit_based_flow` indicates whether credit-based flow control is turned on for this
282    /// Session.
283    pub fn parse(role: Role, credit_based_flow: bool, buf: &[u8]) -> Result<Self, FrameParseError> {
284        if buf.len() < MIN_FRAME_SIZE {
285            return Err(FrameParseError::BufferTooSmall);
286        }
287
288        // Parse the Address Field of the frame.
289        let address_field = AddressField(buf[FRAME_ADDRESS_IDX]);
290        let dlci: DLCI = address_field.dlci()?;
291        let cr_bit: bool = address_field.cr_bit();
292
293        // Parse the Control Field of the frame.
294        let control_field = ControlField(buf[FRAME_CONTROL_IDX]);
295        let frame_type: FrameTypeMarker = control_field.frame_type()?;
296        let poll_final = control_field.poll_final();
297
298        // If the Session multiplexer hasn't started, then the `frame_type` must be a
299        // multiplexer startup frame.
300        if !role.is_multiplexer_started() && !frame_type.is_mux_startup(&dlci) {
301            return Err(FrameParseError::InvalidFrame);
302        }
303
304        // Classify the frame as either a Command or Response depending on the role, type of frame,
305        // and the C/R bit of the Address Field.
306        let command_response = CommandResponse::classify(role, frame_type, cr_bit)?;
307
308        // Parse the Information field of the Frame. If the EA bit is 0, then we need to construct
309        // the InformationLength using two bytes.
310        let information_field = InformationField(buf[FRAME_INFORMATION_IDX]);
311        let is_two_octet_length = !information_field.ea_bit();
312        let mut length = information_field.length() as u16;
313        if is_two_octet_length {
314            length |= (buf[FRAME_INFORMATION_IDX + 1] as u16) << INFORMATION_SECOND_OCTET_SHIFT;
315        }
316
317        // The header size depends on the Information Length size and the (optional) credits octet.
318        // Address (1) + Control (1) + Length (1 or 2)
319        let mut header_size = 2 + if is_two_octet_length { 2 } else { 1 };
320        let mut credits = None;
321        if frame_type.has_credit_octet(credit_based_flow, poll_final, dlci) {
322            if buf.len() < header_size {
323                return Err(FrameParseError::BufferTooSmall);
324            }
325            credits = Some(buf[header_size]);
326            header_size += 1;
327        }
328
329        // Check the FCS before parsing the body of the packet.
330        let fcs_index = header_size + usize::from(length);
331        if buf.len() <= fcs_index {
332            return Err(FrameParseError::BufferTooSmall);
333        }
334        let fcs = buf[fcs_index];
335        if !verify_fcs(fcs, &buf[..frame_type.fcs_octets()]) {
336            return Err(FrameParseError::FCSCheckFailed);
337        }
338
339        let data = &buf[header_size..fcs_index];
340        let data = FrameData::decode(&frame_type, &dlci, data)?;
341
342        Ok(Self { role, dlci, data, poll_final, command_response, credits })
343    }
344
345    pub fn make_sabm_command(role: Role, dlci: DLCI) -> Self {
346        Self {
347            role,
348            dlci,
349            data: FrameData::SetAsynchronousBalancedMode,
350            poll_final: true, // Always set for SABM.
351            command_response: CommandResponse::Command,
352            credits: None,
353        }
354    }
355
356    pub fn make_dm_response(role: Role, dlci: DLCI) -> Self {
357        Self {
358            role,
359            dlci,
360            data: FrameData::DisconnectedMode,
361            poll_final: true, // Always set for DM response.
362            command_response: CommandResponse::Response,
363            credits: None,
364        }
365    }
366
367    pub fn make_ua_response(role: Role, dlci: DLCI) -> Self {
368        Self {
369            role,
370            dlci,
371            data: FrameData::UnnumberedAcknowledgement,
372            poll_final: true, // Always set for UA response.
373            command_response: CommandResponse::Response,
374            credits: None,
375        }
376    }
377
378    pub fn make_mux_command(role: Role, data: MuxCommand) -> Self {
379        let command_response = data.command_response;
380        Self {
381            role,
382            dlci: DLCI::MUX_CONTROL_DLCI,
383            data: FrameData::UnnumberedInfoHeaderCheck(UIHData::Mux(data)),
384            poll_final: false, // Always unset for UIH frames, GSM 5.4.3.1.
385            command_response,
386            credits: None,
387        }
388    }
389
390    pub fn make_user_data_frame(
391        role: Role,
392        dlci: DLCI,
393        user_data: UserData,
394        credits: Option<u8>,
395    ) -> Self {
396        // When credit based flow control is supported, the `poll_final` bit is redefined
397        // for UIH frames. See RFCOMM 6.5.2. If credits are provided, then the `poll_final` bit
398        // should be set.
399        Self {
400            role,
401            dlci,
402            data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(user_data)),
403            poll_final: credits.is_some(),
404            command_response: CommandResponse::Command,
405            credits,
406        }
407    }
408
409    pub fn make_disc_command(role: Role, dlci: DLCI) -> Self {
410        Self {
411            role,
412            dlci,
413            data: FrameData::Disconnect,
414            poll_final: true, // Always set for Disconnect.
415            command_response: CommandResponse::Command,
416            credits: None,
417        }
418    }
419}
420
421impl Encodable for Frame {
422    type Error = FrameParseError;
423
424    fn encoded_len(&self) -> usize {
425        // Address + Control + FCS + (optional) Credits + 1 or 2 octets for Length + Frame data.
426        3 + self.credits.map_or(0, |_| 1)
427            + if is_two_octet_length(self.data.encoded_len()) { 2 } else { 1 }
428            + self.data.encoded_len()
429    }
430
431    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
432        if buf.len() != self.encoded_len() {
433            return Err(FrameParseError::BufferTooSmall);
434        }
435
436        let assumed_role = if !self.role.is_multiplexer_started() {
437            if !self.data.marker().is_mux_startup(&self.dlci) {
438                return Err(FrameParseError::InvalidFrame);
439            }
440            // The role is only determined after the multiplexer starts. Per GSM 5.2.1.2, the
441            // initiating side always sends the first SABM.
442            if self.data.marker() == FrameTypeMarker::SetAsynchronousBalancedMode {
443                Role::Initiator
444            } else {
445                Role::Responder
446            }
447        } else {
448            self.role
449        };
450        // The C/R bit of the Address Field depends on the frame type:
451        //   - For UIH frames, the C/R bit is based on GSM Section 5.4.3.1.
452        //   - For other frames, the C/R bit is determined by Table 1 in GSM Section 5.2.1.2.
453        let cr_bit = if self.data.marker() == FrameTypeMarker::UnnumberedInfoHeaderCheck {
454            cr_bit_for_uih_frame(assumed_role)
455        } else {
456            cr_bit_for_non_uih_frame(assumed_role, self.command_response)
457        };
458
459        // Set the Address Field, E/A = 1 since there is only one octet.
460        let mut address_field = AddressField(0);
461        address_field.set_ea_bit(true);
462        address_field.set_cr_bit(cr_bit);
463        address_field.set_dlci(u8::from(self.dlci));
464        buf[FRAME_ADDRESS_IDX] = address_field.0;
465
466        // Control Field.
467        let mut control_field = ControlField(0);
468        control_field.set_frame_type(u8::from(&self.data.marker()));
469        control_field.set_poll_final(self.poll_final);
470        buf[FRAME_CONTROL_IDX] = control_field.0;
471
472        // Information Field.
473        let data_length = self.data.encoded_len();
474        let is_two_octet_length = is_two_octet_length(data_length);
475        let mut first_octet_length = InformationField(0);
476        first_octet_length.set_length(data_length as u8);
477        first_octet_length.set_ea_bit(!is_two_octet_length);
478        buf[FRAME_INFORMATION_IDX] = first_octet_length.0;
479        // If the length is two octets, get the upper 8 bits and set the second octet.
480        if is_two_octet_length {
481            let second_octet_length = (data_length >> INFORMATION_SECOND_OCTET_SHIFT) as u8;
482            buf[FRAME_INFORMATION_IDX + 1] = second_octet_length;
483        }
484
485        // Address + Control + Information.
486        let mut header_size = 2 + if is_two_octet_length { 2 } else { 1 };
487
488        // Encode the credits for this frame, if applicable.
489        let credit_based_flow = self.credits.is_some();
490        if self.data.marker().has_credit_octet(credit_based_flow, self.poll_final, self.dlci) {
491            buf[header_size] = self.credits.unwrap();
492            header_size += 1;
493        }
494
495        let fcs_idx = header_size + data_length as usize;
496
497        // Frame data.
498        self.data.encode(&mut buf[header_size..fcs_idx])?;
499
500        // FCS that is computed based on `frame_type`.
501        buf[fcs_idx] = calculate_fcs(&buf[..self.data.marker().fcs_octets()]);
502
503        Ok(())
504    }
505}
506
507#[cfg(test)]
508mod tests {
509    use crate::frame::mux_commands::ModemStatusParams;
510
511    use super::*;
512
513    use assert_matches::assert_matches;
514    use mux_commands::{MuxCommandParams, RemotePortNegotiationParams};
515
516    #[test]
517    fn test_is_mux_startup_frame() {
518        let control_dlci = DLCI::try_from(0).unwrap();
519        let user_dlci = DLCI::try_from(5).unwrap();
520
521        let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
522        assert!(frame_type.is_mux_startup(&control_dlci));
523        assert!(!frame_type.is_mux_startup(&user_dlci));
524
525        let frame_type = FrameTypeMarker::UnnumberedAcknowledgement;
526        assert!(frame_type.is_mux_startup(&control_dlci));
527        assert!(!frame_type.is_mux_startup(&user_dlci));
528
529        let frame_type = FrameTypeMarker::DisconnectedMode;
530        assert!(frame_type.is_mux_startup(&control_dlci));
531        assert!(!frame_type.is_mux_startup(&user_dlci));
532
533        let frame_type = FrameTypeMarker::Disconnect;
534        assert!(!frame_type.is_mux_startup(&control_dlci));
535        assert!(!frame_type.is_mux_startup(&user_dlci));
536    }
537
538    #[test]
539    fn test_has_credit_octet() {
540        let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
541        let pf = true;
542        let credit_based_flow = true;
543        let dlci = DLCI::try_from(3).unwrap();
544        assert!(frame_type.has_credit_octet(credit_based_flow, pf, dlci));
545
546        let pf = false;
547        let credit_based_flow = true;
548        assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
549
550        let pf = true;
551        let credit_based_flow = false;
552        assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
553
554        let pf = true;
555        let credit_based_flow = true;
556        let dlci = DLCI::try_from(0).unwrap(); // Mux DLCI.
557        assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
558
559        let pf = false;
560        let credit_based_flow = false;
561        assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
562
563        let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
564        let pf = true;
565        let credit_based_flow = true;
566        let dlci = DLCI::try_from(5).unwrap();
567        assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
568    }
569
570    #[test]
571    fn test_parse_too_small_frame() {
572        let role = Role::Unassigned;
573        let buf: &[u8] = &[0x00];
574        assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::BufferTooSmall));
575    }
576
577    #[test]
578    fn test_parse_invalid_dlci() {
579        let role = Role::Unassigned;
580        let buf: &[u8] = &[
581            0b00000101, // Address Field - EA = 1, C/R = 0, DLCI = 1.
582            0b00101111, // Control Field - SABM command with P/F = 0.
583            0b00000001, // Length Field - Bit0 = 1: Indicates one octet length.
584            0x00,       // Random FCS.
585        ];
586        assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::InvalidDLCI(1)));
587    }
588
589    /// It's possible that a remote device sends a packet with an invalid frame.
590    /// In this case, we should error gracefully.
591    #[test]
592    fn test_parse_invalid_frame_type() {
593        let role = Role::Unassigned;
594        let buf: &[u8] = &[
595            0b00000001, // Address Field - EA = 1, C/R = 0, DLCI = 0.
596            0b10101010, // Control Field - Invalid command with P/F = 0.
597            0b00000001, // Length Field - Bit1 = 0 indicates 1 octet length.
598            0x00,       // Random FCS.
599        ];
600        assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::UnsupportedFrameType));
601    }
602
603    /// It's possible that the remote peer sends a packet for a valid frame, but the session
604    /// multiplexer has not started. In this case, we should error gracefully.
605    #[test]
606    fn test_parse_invalid_frame_type_sent_before_mux_startup() {
607        let role = Role::Unassigned;
608        let buf: &[u8] = &[
609            0b00000001, // Address Field - EA = 1, C/R = 0, DLCI = 0.
610            0b11101111, // Control Field - UnnumberedInfoHeaderCheck with P/F = 0.
611            0b00000001, // Length Field - Bit1 = 0 indicates 1 octet length.
612            0x00,       // Random FCS.
613        ];
614        assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::InvalidFrame));
615    }
616
617    #[test]
618    fn test_parse_invalid_frame_missing_fcs() {
619        let role = Role::Unassigned;
620        let buf: &[u8] = &[
621            0b00000011, // Address Field - EA = 1, C/R = 1, DLCI = 0.
622            0b00101111, // Control Field - SABM command with P/F = 0.
623            0b00000000, // Length Field - Bit1 = 0 Indicates two octet length.
624            0b00000001, // Second octet of length.
625                        // Missing FCS.
626        ];
627        assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::BufferTooSmall));
628    }
629
630    #[test]
631    fn test_parse_valid_frame_over_mux_dlci() {
632        let role = Role::Unassigned;
633        let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
634        let mut buf = vec![
635            0b00000011, // Address Field - EA = 1, C/R = 1, DLCI = 0.
636            0b00101111, // Control Field - SABM command with P/F = 0.
637            0b00000001, // Length Field - Bit1 = 1 Indicates one octet length - no info.
638        ];
639        // Calculate the FCS and tack it on to the end.
640        let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
641        buf.push(fcs);
642
643        let res = Frame::parse(role, false, &buf[..]).unwrap();
644        let expected_frame = Frame {
645            role,
646            dlci: DLCI::try_from(0).unwrap(),
647            data: FrameData::SetAsynchronousBalancedMode,
648            poll_final: false,
649            command_response: CommandResponse::Command,
650            credits: None,
651        };
652        assert_eq!(res, expected_frame);
653    }
654
655    #[test]
656    fn test_parse_valid_frame_over_user_dlci() {
657        let role = Role::Responder;
658        let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
659        let mut buf = vec![
660            0b00001111, // Address Field - EA = 1, C/R = 1, User DLCI = 3.
661            0b00101111, // Control Field - SABM command with P/F = 0.
662            0b00000001, // Length Field - Bit1 = 1 Indicates one octet length - no info.
663        ];
664        // Calculate the FCS for the first three bytes, since non-UIH frame.
665        let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
666        buf.push(fcs);
667
668        let res = Frame::parse(role, false, &buf[..]).unwrap();
669        let expected_frame = Frame {
670            role,
671            dlci: DLCI::try_from(3).unwrap(),
672            data: FrameData::SetAsynchronousBalancedMode,
673            poll_final: false,
674            command_response: CommandResponse::Response,
675            credits: None,
676        };
677        assert_eq!(res, expected_frame);
678    }
679
680    #[test]
681    fn test_parse_frame_with_information_length_invalid_buf_size() {
682        let role = Role::Responder;
683        let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
684        let mut buf = vec![
685            0b00001111, // Address Field - EA = 1, C/R = 1, User DLCI = 3.
686            0b11101111, // Control Field - UIH command with P/F = 0.
687            0b00000111, // Length Field - Bit1 = 1 Indicates one octet length = 3.
688            0b00000000, // Data octet #1 - missing octets 2,3.
689        ];
690        // Calculate the FCS for the first two bytes, since UIH frame.
691        let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
692        buf.push(fcs);
693
694        assert_matches!(Frame::parse(role, false, &buf[..]), Err(FrameParseError::BufferTooSmall));
695    }
696
697    #[test]
698    fn test_parse_valid_frame_with_information_length() {
699        let role = Role::Responder;
700        let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
701        let mut buf = vec![
702            0b00001101, // Address Field - EA = 1, C/R = 0, User DLCI = 3.
703            0b11101111, // Control Field - UIH command with P/F = 0.
704            0b00000101, // Length Field - Bit1 = 1 Indicates one octet length = 2.
705            0b00000000, // Data octet #1,
706            0b00000000, // Data octet #2,
707        ];
708        // Calculate the FCS for the first two bytes, since UIH frame.
709        let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
710        buf.push(fcs);
711
712        let res = Frame::parse(role, false, &buf[..]).unwrap();
713        let expected_frame = Frame {
714            role,
715            dlci: DLCI::try_from(3).unwrap(),
716            data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(UserData {
717                information: vec![
718                    0b00000000, // Data octet #1.
719                    0b00000000, // Data octet #2.
720                ],
721            })),
722            poll_final: false,
723            command_response: CommandResponse::Response,
724            credits: None,
725        };
726        assert_eq!(res, expected_frame);
727    }
728
729    #[test]
730    fn test_parse_valid_frame_with_two_octet_information_length() {
731        let role = Role::Responder;
732        let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
733        let length = 129;
734        let length_data = vec![0; length];
735
736        // Concatenate the header, `length_data` payload, and FCS.
737        let buf = vec![
738            0b00001101, // Address Field - EA = 1, C/R = 0, User DLCI = 3.
739            0b11101111, // Control Field - UIH command with P/F = 0.
740            0b00000010, // Length Field0 - E/A = 0. Length = 1.
741            0b00000001, // Length Field1 - No E/A. Length = 128.
742        ];
743        // Calculate the FCS for the first two bytes, since UIH frame.
744        let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
745        let buf = [buf, length_data.clone(), vec![fcs]].concat();
746
747        let res = Frame::parse(role, false, &buf[..]).unwrap();
748        let expected_frame = Frame {
749            role,
750            dlci: DLCI::try_from(3).unwrap(),
751            data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(UserData {
752                information: length_data,
753            })),
754            poll_final: false,
755            command_response: CommandResponse::Response,
756            credits: None,
757        };
758        assert_eq!(res, expected_frame);
759    }
760
761    #[test]
762    fn test_parse_uih_frame_with_mux_command() {
763        let role = Role::Responder;
764        let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
765        let mut buf = vec![
766            0b00000001, // Address Field - EA = 1, C/R = 0, Mux DLCI = 0.
767            0b11111111, // Control Field - UIH command with P/F = 1.
768            0b00000111, // Length Field - Bit1 = 1 Indicates one octet length = 3.
769            0b10010001, // Data octet #1 - RPN command.
770            0b00000011, // Data octet #2 - RPN Command length = 1.
771            0b00011111, // Data octet #3 - RPN Data, DLCI = 7.
772        ];
773        // Calculate the FCS for the first two bytes, since UIH frame.
774        let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
775        buf.push(fcs);
776
777        let res = Frame::parse(role, false, &buf[..]).unwrap();
778        let expected_mux_command = MuxCommand {
779            params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
780                dlci: DLCI::try_from(7).unwrap(),
781                port_values: None,
782            }),
783            command_response: CommandResponse::Response,
784        };
785        let expected_frame = Frame {
786            role,
787            dlci: DLCI::try_from(0).unwrap(),
788            data: FrameData::UnnumberedInfoHeaderCheck(UIHData::Mux(expected_mux_command)),
789            poll_final: true,
790            command_response: CommandResponse::Response,
791            credits: None,
792        };
793        assert_eq!(res, expected_frame);
794    }
795
796    #[test]
797    fn test_parse_uih_frame_with_credits() {
798        let role = Role::Initiator;
799        let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
800        let credit_based_flow = true;
801        let mut buf = vec![
802            0b00011111, // Address Field - EA = 1, C/R = 1, User DLCI = 7.
803            0b11111111, // Control Field - UIH command with P/F = 1.
804            0b00000111, // Length Field - Bit1 = 1 Indicates one octet length = 3.
805            0b00000101, // Credits Field = 5.
806            0b00000000, // UserData octet #1.
807            0b00000001, // UserData octet #2.
808            0b00000010, // UserData octet #3.
809        ];
810        // Calculate the FCS for the first two bytes, since UIH frame.
811        let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
812        buf.push(fcs);
813
814        let res = Frame::parse(role, credit_based_flow, &buf[..]).unwrap();
815        let expected_user_data = UserData { information: vec![0x00, 0x01, 0x02] };
816        let expected_frame = Frame {
817            role,
818            dlci: DLCI::try_from(7).unwrap(),
819            data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(expected_user_data)),
820            poll_final: true,
821            command_response: CommandResponse::Command,
822            credits: Some(5),
823        };
824        assert_eq!(res, expected_frame);
825    }
826
827    #[test]
828    fn test_encode_frame_invalid_buf() {
829        let frame = Frame {
830            role: Role::Unassigned,
831            dlci: DLCI::try_from(0).unwrap(),
832            data: FrameData::SetAsynchronousBalancedMode,
833            poll_final: false,
834            command_response: CommandResponse::Command,
835            credits: None,
836        };
837        let mut buf = [];
838        assert_matches!(frame.encode(&mut buf[..]), Err(FrameParseError::BufferTooSmall));
839    }
840
841    /// Tests that attempting to encode a Mux Startup frame over a user DLCI is rejected.
842    #[test]
843    fn test_encode_mux_startup_frame_over_user_dlci_fails() {
844        let frame = Frame {
845            role: Role::Unassigned,
846            dlci: DLCI::try_from(3).unwrap(),
847            data: FrameData::SetAsynchronousBalancedMode,
848            poll_final: false,
849            command_response: CommandResponse::Command,
850            credits: None,
851        };
852        let mut buf = vec![0; frame.encoded_len()];
853        assert_matches!(frame.encode(&mut buf[..]), Err(FrameParseError::InvalidFrame));
854    }
855
856    #[test]
857    fn encode_mux_startup_command_succeeds() {
858        let frame = Frame {
859            role: Role::Unassigned,
860            dlci: DLCI::try_from(0).unwrap(),
861            data: FrameData::SetAsynchronousBalancedMode,
862            poll_final: true,
863            command_response: CommandResponse::Command,
864            credits: None,
865        };
866        let mut buf = vec![0; frame.encoded_len()];
867        assert!(frame.encode(&mut buf[..]).is_ok());
868        let expected = vec![
869            0b00000011, // Address Field: DLCI = 0, C/R = 1, E/A = 1.
870            0b00111111, // Control Field: SABM, P/F = 1.
871            0b00000001, // Length Field: Length = 0, E/A = 1.
872            0b00011100, // FCS - precomputed.
873        ];
874        assert_eq!(buf, expected);
875    }
876
877    #[test]
878    fn encode_mux_startup_response_succeeds() {
879        let frame = Frame::make_ua_response(Role::Unassigned, DLCI::try_from(0).unwrap());
880        let mut buf = vec![0; frame.encoded_len()];
881        assert!(frame.encode(&mut buf[..]).is_ok());
882        let expected = vec![
883            0b00000011, // Address Field: DLCI = 0, C/R = 1, E/A = 1.
884            0b01110011, // Control Field: UA, P/F = 1.
885            0b00000001, // Length Field: Length = 0, E/A = 1.
886            0b11010111, // FCS - precomputed.
887        ];
888        assert_eq!(buf, expected);
889    }
890
891    #[test]
892    fn encode_user_data_as_initiator_succeeds() {
893        let frame = Frame::make_user_data_frame(
894            Role::Initiator,
895            DLCI::try_from(3).unwrap(),
896            UserData {
897                information: vec![
898                    0b00000001, // Data octet #1.
899                    0b00000010, // Data octet #2.
900                ],
901            },
902            Some(8),
903        );
904        let mut buf = vec![0; frame.encoded_len()];
905        assert!(frame.encode(&mut buf[..]).is_ok());
906        let expected = vec![
907            0b00001111, // Address Field: DLCI = 3, C/R = 1, E/A = 1.
908            0b11111111, // Control Field - UIH command with P/F = 1.
909            0b00000101, // Length Field - Bit1 = 1 Indicates one octet, length = 2.
910            0b00001000, // Credit Field - Credits = 8.
911            0b00000001, // Data octet #1.
912            0b00000010, // Data octet #2.
913            0b11110011, // FCS - precomputed.
914        ];
915        assert_eq!(buf, expected);
916    }
917
918    #[test]
919    fn test_encode_user_data_as_responder_succeeds() {
920        let frame = Frame::make_user_data_frame(
921            Role::Responder,
922            DLCI::try_from(9).unwrap(),
923            UserData {
924                information: vec![
925                    0b00000001, // Data octet #1.
926                ],
927            },
928            Some(10),
929        );
930        let mut buf = vec![0; frame.encoded_len()];
931        assert!(frame.encode(&mut buf[..]).is_ok());
932        let expected = vec![
933            0b00100101, // Address Field: DLCI = 3, C/R = 0, E/A = 1.
934            0b11111111, // Control Field - UIH command with P/F = 1.
935            0b00000011, // Length Field - Bit1 = 1 Indicates one octet, length = 1.
936            0b00001010, // Credit Field - Credits = 10.
937            0b00000001, // Data octet #1.
938            0b11101001, // FCS - precomputed.
939        ];
940        assert_eq!(buf, expected);
941    }
942
943    #[test]
944    fn encode_mux_command_as_initiator() {
945        let mux_command = MuxCommand {
946            params: MuxCommandParams::ModemStatus(ModemStatusParams::default(
947                DLCI::try_from(5).unwrap(),
948            )),
949            command_response: CommandResponse::Command,
950        };
951        let frame = Frame::make_mux_command(Role::Initiator, mux_command);
952
953        let mut buf = vec![0; frame.encoded_len()];
954        assert!(frame.encode(&mut buf[..]).is_ok());
955        let expected = vec![
956            0b00000011, // Address Field: DLCI = 0, C/R = 1, E/A = 1.
957            0b11101111, // Control Field - UIH command with P/F = 1.
958            0b00001001, // Length Field - Bit1 = 1 Indicates one octet, length = 4.
959            0b11100011, // Data octet #1 - MSC response, C/R = 1, E/A = 1.
960            0b00000101, // Data octet #2 - Length = 2, E/A = 1.
961            0b00010111, // Data octet #3 DLCI = 5, E/A = 1, Bit2 = 1 always.
962            0b10001101, // Data octet #4 Signals = default, E/A = 1.
963            0b01110000, // FCS - precomputed.
964        ];
965        assert_eq!(buf, expected);
966    }
967
968    #[test]
969    fn encode_mux_command_as_responder() {
970        let mux_command = MuxCommand {
971            params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
972                dlci: DLCI::try_from(7).unwrap(),
973                port_values: None,
974            }),
975            command_response: CommandResponse::Command,
976        };
977        let frame = Frame::make_mux_command(Role::Responder, mux_command);
978
979        let mut buf = vec![0; frame.encoded_len()];
980        assert!(frame.encode(&mut buf[..]).is_ok());
981        let expected = vec![
982            0b00000001, // Address Field: DLCI = 0, C/R = 0, E/A = 1.
983            0b11101111, // Control Field - UIH command with P/F = 1.
984            0b00000111, // Length Field - Bit1 = 1 Indicates one octet, length = 3.
985            0b10010011, // Data octet #1 - RPN command, C/R = 1, E/A = 1.
986            0b00000011, // Data octet #2 - RPN Command length = 1.
987            0b00011111, // Data octet #3 - RPN Data, DLCI = 7.
988            0b10101010, // FCS - precomputed.
989        ];
990        assert_eq!(buf, expected);
991    }
992
993    #[test]
994    fn encode_mux_response_as_initiator() {
995        let mux_command = MuxCommand {
996            params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
997                dlci: DLCI::try_from(13).unwrap(),
998                port_values: None,
999            }),
1000            command_response: CommandResponse::Response,
1001        };
1002        let frame = Frame::make_mux_command(Role::Initiator, mux_command);
1003
1004        let mut buf = vec![0; frame.encoded_len()];
1005        assert!(frame.encode(&mut buf[..]).is_ok());
1006        let expected = vec![
1007            0b00000011, // Address Field: DLCI = 0, C/R = 1, E/A = 1.
1008            0b11101111, // Control Field - UIH command with P/F = 1.
1009            0b00000111, // Length Field - Bit1 = 1 Indicates one octet, length = 3.
1010            0b10010001, // Data octet #1 - RPN command, C/R = 0, E/A = 1.
1011            0b00000011, // Data octet #2 - RPN Command length = 1.
1012            0b00110111, // Data octet #3 - RPN Data, DLCI = 7.
1013            0b01110000, // FCS - precomputed.
1014        ];
1015        assert_eq!(buf, expected);
1016    }
1017
1018    #[test]
1019    fn encode_mux_response_as_responder() {
1020        let mux_command = MuxCommand {
1021            params: MuxCommandParams::ModemStatus(ModemStatusParams::default(
1022                DLCI::try_from(11).unwrap(),
1023            )),
1024            command_response: CommandResponse::Response,
1025        };
1026        let frame = Frame::make_mux_command(Role::Responder, mux_command);
1027
1028        let mut buf = vec![0; frame.encoded_len()];
1029        assert!(frame.encode(&mut buf[..]).is_ok());
1030        let expected = vec![
1031            0b00000001, // Address Field: DLCI = 0, C/R = 0, E/A = 1.
1032            0b11101111, // Control Field - UIH command with P/F = 1.
1033            0b00001001, // Length Field - Bit1 = 1 Indicates one octet, length = 4.
1034            0b11100001, // Data octet #1 - MSC response, C/R = 0, E/A = 1.
1035            0b00000101, // Data octet #2 - Length = 2, E/A = 1.
1036            0b00101111, // Data octet #3 DLCI = 11, E/A = 1, Bit2 = 1 always.
1037            0b10001101, // Data octet #4 Signals = default, E/A = 1.
1038            0b10101010, // FCS - precomputed.
1039        ];
1040        assert_eq!(buf, expected);
1041    }
1042
1043    #[test]
1044    fn test_encode_user_data_with_two_octet_length_succeeds() {
1045        let length = 130;
1046        let mut information = vec![0; length];
1047        let frame = Frame {
1048            role: Role::Initiator,
1049            dlci: DLCI::try_from(5).unwrap(),
1050            data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(UserData {
1051                information: information.clone(),
1052            })),
1053            poll_final: true,
1054            command_response: CommandResponse::Command,
1055            credits: Some(8),
1056        };
1057        let mut buf = vec![0; frame.encoded_len()];
1058        assert!(frame.encode(&mut buf[..]).is_ok());
1059        let mut expected = vec![
1060            0b00010111, // Address Field: DLCI = 5, C/R = 1, E/A = 1.
1061            0b11111111, // Control Field - UIH command with P/F = 1.
1062            0b00000100, // Length Field - E/A = 0. Length = 2.
1063            0b00000001, // Length Field2 - 128.
1064            0b00001000, // Credit Field - Credits = 8.
1065        ];
1066        // Add the information.
1067        expected.append(&mut information);
1068        // Add the precomputed FCS.
1069        expected.push(0b0000_1100);
1070        assert_eq!(buf, expected);
1071    }
1072}