bt_rfcomm/frame/mux_commands/
dlc_parameter_negotiation.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
8use crate::frame::FrameParseError;
9use crate::DLCI;
10
11/// The length (in bytes) of a DLC Parameter Negotiation command.
12/// Defined in GSM 7.10 Section 5.4.6.3.1.
13const DLC_PARAMETER_NEGOTIATION_LENGTH: usize = 8;
14
15/// The default initial credit amount for the peer during parameter negotiation.
16/// This is chosen as the maximum initial credits allowed as per RFCOMM 5.5.3.
17pub const DEFAULT_INITIAL_CREDITS: u8 = 7;
18
19decodable_enum! {
20    /// The Credit Based Flow Handshake variants defined in RFCOMM Table 5.3.
21    pub enum CreditBasedFlowHandshake<u8, FrameParseError, OutOfRange> {
22        Unsupported = 0x0,
23        SupportedRequest = 0xF,
24        SupportedResponse = 0xE,
25    }
26}
27
28bitfield! {
29    struct DLCParameterNegotiationFields([u8]);
30    impl Debug;
31    pub u8, dlci_raw, set_dlci: 5,0;
32    pub u8, credit_handshake, set_credit_handshake: 15, 12;
33    pub u8, priority, set_priority: 21, 16;
34    pub u16, max_frame_size, set_max_frame_size: 47, 32;
35    pub u8, initial_credits, set_initial_credits: 58, 56;
36}
37
38impl DLCParameterNegotiationFields<[u8; DLC_PARAMETER_NEGOTIATION_LENGTH]> {
39    fn dlci(&self) -> Result<DLCI, FrameParseError> {
40        DLCI::try_from(self.dlci_raw())
41    }
42
43    fn credit_based_flow_handshake(&self) -> Result<CreditBasedFlowHandshake, FrameParseError> {
44        CreditBasedFlowHandshake::try_from(self.credit_handshake())
45    }
46}
47
48/// The DLC Parameter Negotiation command - used to negotiate parameters for a given DLC.
49/// See GSM 7.10 Section 5.4.6.3.1 for the fields and RFCOMM 5.5.3 for modifications.
50#[derive(Clone, Debug, PartialEq)]
51pub struct ParameterNegotiationParams {
52    pub dlci: DLCI,
53    pub credit_based_flow_handshake: CreditBasedFlowHandshake,
54    pub priority: u8,
55    pub max_frame_size: u16,
56    pub initial_credits: u8,
57}
58
59impl ParameterNegotiationParams {
60    /// Returns the default parameters for a Parameter Negotiation command.
61    pub fn default_command(dlci: DLCI, max_frame_size: u16) -> Self {
62        Self {
63            dlci,
64            credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
65            priority: 1,
66            max_frame_size,
67            initial_credits: DEFAULT_INITIAL_CREDITS,
68        }
69    }
70
71    /// Returns true if credit-based flow control is supported by the parameters.
72    pub fn credit_based_flow(&self) -> bool {
73        self.credit_based_flow_handshake == CreditBasedFlowHandshake::SupportedRequest
74            || self.credit_based_flow_handshake == CreditBasedFlowHandshake::SupportedResponse
75    }
76}
77
78impl Decodable for ParameterNegotiationParams {
79    type Error = FrameParseError;
80
81    fn decode(buf: &[u8]) -> Result<Self, FrameParseError> {
82        if buf.len() != DLC_PARAMETER_NEGOTIATION_LENGTH {
83            return Err(FrameParseError::InvalidBufferLength(
84                DLC_PARAMETER_NEGOTIATION_LENGTH,
85                buf.len(),
86            ));
87        }
88
89        let mut fixed_buf = [0; DLC_PARAMETER_NEGOTIATION_LENGTH];
90        fixed_buf.copy_from_slice(&buf[..]);
91        let parameters = DLCParameterNegotiationFields(fixed_buf);
92
93        let dlci = parameters.dlci()?;
94        let credit_based_flow_handshake = parameters.credit_based_flow_handshake()?;
95        let priority = parameters.priority();
96        let max_frame_size = parameters.max_frame_size();
97        let initial_credits = parameters.initial_credits();
98
99        Ok(ParameterNegotiationParams {
100            dlci,
101            credit_based_flow_handshake,
102            priority,
103            max_frame_size,
104            initial_credits,
105        })
106    }
107}
108
109impl Encodable for ParameterNegotiationParams {
110    type Error = FrameParseError;
111
112    fn encoded_len(&self) -> usize {
113        DLC_PARAMETER_NEGOTIATION_LENGTH
114    }
115
116    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
117        if buf.len() < self.encoded_len() {
118            return Err(FrameParseError::BufferTooSmall);
119        }
120
121        let mut params = DLCParameterNegotiationFields([0; DLC_PARAMETER_NEGOTIATION_LENGTH]);
122        params.set_dlci(u8::from(self.dlci));
123        params.set_credit_handshake(u8::from(&self.credit_based_flow_handshake));
124        params.set_priority(self.priority);
125        params.set_max_frame_size(self.max_frame_size);
126        params.set_initial_credits(self.initial_credits);
127
128        let params_bytes = params.0;
129        buf.copy_from_slice(&params_bytes[..]);
130
131        Ok(())
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    use assert_matches::assert_matches;
140
141    #[test]
142    fn test_parse_too_small_buf() {
143        let buf = [0x00, 0x00, 0x00]; // Too small.
144        assert_matches!(
145            ParameterNegotiationParams::decode(&buf[..]),
146            Err(FrameParseError::InvalidBufferLength(DLC_PARAMETER_NEGOTIATION_LENGTH, 3))
147        );
148    }
149
150    #[test]
151    fn test_parse_too_large_buf() {
152        let buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; // Too large.
153        assert_matches!(
154            ParameterNegotiationParams::decode(&buf[..]),
155            Err(FrameParseError::InvalidBufferLength(DLC_PARAMETER_NEGOTIATION_LENGTH, 9))
156        );
157    }
158
159    #[test]
160    fn test_parse_invalid_dlci() {
161        let buf = [
162            0b00000001, // DLCI of 1 is invalid.
163            0b11110000, // SupportedRequest.
164            0b00000010, // Priority = 2.
165            0b00000000, // Ignored.
166            0b00000010, // Max Frame Size Octet 1 = 4.
167            0b00000000, // Max Frame Size Octet 2 = 0.
168            0b00000000, // Ignored
169            0b00000001, // Initial Credits = 1.
170        ];
171        assert_matches!(
172            ParameterNegotiationParams::decode(&buf[..]),
173            Err(FrameParseError::InvalidDLCI(1))
174        );
175    }
176
177    #[test]
178    fn test_parse_invalid_credit_handshake() {
179        let buf = [
180            0b00000000, // DLCI of 0 is OK.
181            0b10010000, // Invalid handshake value.
182            0b00000010, // Priority = 2.
183            0b00000000, // Ignored.
184            0b00000010, // Max Frame Size Octet 1 = 4.
185            0b00000000, // Max Frame Size Octet 2 = 0.
186            0b00000000, // Ignored
187            0b00000001, // Initial Credits = 1.
188        ];
189        assert_matches!(
190            ParameterNegotiationParams::decode(&buf[..]),
191            Err(FrameParseError::OutOfRange)
192        );
193    }
194
195    #[test]
196    fn test_parse_valid_command() {
197        let buf = [
198            0b00000000, // DLCI of 0 is OK.
199            0b11110000, // SupportedRequest.
200            0b00001000, // Priority = 8.
201            0b00000000, // Ignored.
202            0b00000100, // Max Frame Size Octet 1 = 4.
203            0b00000000, // Max Frame Size Octet 2 = 0.
204            0b00000000, // Ignored
205            0b00000001, // Initial Credits = 1.
206        ];
207
208        let expected = ParameterNegotiationParams {
209            dlci: DLCI::try_from(0).unwrap(),
210            credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
211            priority: 8,
212            max_frame_size: 4,
213            initial_credits: 1,
214        };
215        assert_eq!(ParameterNegotiationParams::decode(&buf[..]).unwrap(), expected);
216    }
217
218    #[test]
219    fn test_parse_two_octet_frame_size_command() {
220        let buf = [
221            0b00000000, // DLCI of 0 is OK.
222            0b11110000, // SupportedRequest.
223            0b00001000, // Priority = 8.
224            0b00000000, // Ignored.
225            0b10000100, // Max Frame Size Octet 1 = 132.
226            0b00000001, // Max Frame Size Octet 2 = 256.
227            0b00011100, // Ignored.
228            0b00001100, // Initial Credits = 4, stray bit should be ignored.
229        ];
230
231        let expected = ParameterNegotiationParams {
232            dlci: DLCI::try_from(0).unwrap(),
233            credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
234            priority: 8,
235            max_frame_size: 388,
236            initial_credits: 4,
237        };
238        assert_eq!(ParameterNegotiationParams::decode(&buf[..]).unwrap(), expected);
239    }
240
241    #[test]
242    fn test_encode_invalid_buf_error() {
243        let mut small_buf = [];
244        let dlc_pn_command = ParameterNegotiationParams {
245            dlci: DLCI::try_from(0).unwrap(),
246            credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
247            priority: 8,
248            max_frame_size: 260,
249            initial_credits: 4,
250        };
251        assert_matches!(
252            dlc_pn_command.encode(&mut small_buf[..]),
253            Err(FrameParseError::BufferTooSmall)
254        );
255    }
256
257    #[test]
258    fn test_encode_command_success() {
259        let dlc_pn_command = ParameterNegotiationParams {
260            dlci: DLCI::try_from(5).unwrap(),
261            credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
262            priority: 8,
263            max_frame_size: 260,
264            initial_credits: 6,
265        };
266        let mut buf = vec![0; dlc_pn_command.encoded_len()];
267
268        assert!(dlc_pn_command.encode(&mut buf[..]).is_ok());
269        let expected_buf = [
270            0b00000101, // DLCI of 5 is OK.
271            0b11110000, // SupportedRequest.
272            0b00001000, // Priority = 8.
273            0b00000000, // Ignored.
274            0b00000100, // Max Frame Size Octet 1 = 4.
275            0b00000001, // Max Frame Size Octet 2 = 256.
276            0b00000000, // Ignored
277            0b00000110, // Initial Credits = 6.
278        ];
279        assert_eq!(buf, expected_buf);
280    }
281}