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