bt_rfcomm/frame/mux_commands/
dlc_parameter_negotiation.rs
1use bitfield::bitfield;
6use packet_encoding::{decodable_enum, Decodable, Encodable};
7
8use crate::frame::FrameParseError;
9use crate::DLCI;
10
11const DLC_PARAMETER_NEGOTIATION_LENGTH: usize = 8;
14
15pub const DEFAULT_INITIAL_CREDITS: u8 = 7;
18
19decodable_enum! {
20 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#[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 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 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(¶ms_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]; 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]; 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, 0b11110000, 0b00000010, 0b00000000, 0b00000010, 0b00000000, 0b00000000, 0b00000001, ];
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, 0b10010000, 0b00000010, 0b00000000, 0b00000010, 0b00000000, 0b00000000, 0b00000001, ];
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, 0b11110000, 0b00001000, 0b00000000, 0b00000100, 0b00000000, 0b00000000, 0b00000001, ];
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, 0b11110000, 0b00001000, 0b00000000, 0b10000100, 0b00000001, 0b00011100, 0b00001100, ];
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, 0b11110000, 0b00001000, 0b00000000, 0b00000100, 0b00000001, 0b00000000, 0b00000110, ];
279 assert_eq!(buf, expected_buf);
280 }
281}