bt_rfcomm/
dlci.rs
1use anyhow::format_err;
6use core::fmt::{self, Display};
7
8use crate::frame::FrameParseError;
9use crate::{RfcommError, Role};
10
11#[derive(Clone, Copy, Hash, Eq, Debug, PartialEq)]
19pub struct DLCI(u8);
20
21impl DLCI {
22 pub const MUX_CONTROL_DLCI: DLCI = DLCI(0);
24 const MIN_USER_DLCI: DLCI = DLCI(2);
26 const MAX_USER_DLCI: DLCI = DLCI(61);
28
29 pub fn is_mux_control(&self) -> bool {
30 *self == Self::MUX_CONTROL_DLCI
31 }
32
33 pub fn is_user(&self) -> bool {
34 self.0 >= Self::MIN_USER_DLCI.0 && self.0 <= Self::MAX_USER_DLCI.0
35 }
36
37 pub fn validate(&self, role: Role) -> Result<(), RfcommError> {
45 if !self.is_user() {
46 return Err(RfcommError::InvalidDLCI(*self));
47 }
48
49 let valid_bit = match role {
50 Role::Responder => 0,
51 Role::Initiator => 1,
52 role => {
53 return Err(RfcommError::InvalidRole(role));
54 }
55 };
56
57 if self.0 % 2 == valid_bit {
58 Ok(())
59 } else {
60 Err(RfcommError::InvalidDLCI(*self))
61 }
62 }
63
64 pub fn initiator(&self, role: Role) -> Result<bool, RfcommError> {
68 if !self.is_user() {
69 return Err(RfcommError::InvalidDLCI(*self));
70 }
71
72 match role.opposite_role() {
76 Role::Responder => Ok(self.0 % 2 == 0),
77 Role::Initiator => Ok(self.0 % 2 == 1),
78 role => {
79 return Err(RfcommError::InvalidRole(role));
80 }
81 }
82 }
83}
84
85impl TryFrom<u8> for DLCI {
86 type Error = FrameParseError;
87
88 fn try_from(value: u8) -> Result<DLCI, Self::Error> {
89 if value != DLCI::MUX_CONTROL_DLCI.0
90 && (value < DLCI::MIN_USER_DLCI.0 || value > DLCI::MAX_USER_DLCI.0)
91 {
92 return Err(FrameParseError::InvalidDLCI(value));
93 }
94 Ok(DLCI(value))
95 }
96}
97
98impl From<DLCI> for u8 {
99 fn from(value: DLCI) -> u8 {
100 value.0
101 }
102}
103
104impl TryFrom<DLCI> for ServerChannel {
105 type Error = RfcommError;
106
107 fn try_from(dlci: DLCI) -> Result<ServerChannel, Self::Error> {
108 if !dlci.is_user() {
109 return Err(RfcommError::InvalidDLCI(dlci));
110 }
111
112 ServerChannel::try_from(dlci.0 >> 1)
114 }
115}
116
117impl Display for DLCI {
118 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(formatter, "{}", self.0)
120 }
121}
122
123#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
132pub struct ServerChannel(u8);
133
134impl ServerChannel {
135 const MAX: ServerChannel = ServerChannel(30);
136 const MIN: ServerChannel = ServerChannel(1);
137
138 pub fn all() -> impl Iterator<Item = ServerChannel> {
140 (Self::MIN.0..=Self::MAX.0).map(|x| ServerChannel(x))
141 }
142
143 pub fn to_dlci(&self, role: Role) -> Result<DLCI, RfcommError> {
146 let direction_bit = match role {
147 Role::Initiator => 1,
148 Role::Responder => 0,
149 r => {
150 return Err(RfcommError::InvalidRole(r));
151 }
152 };
153
154 let v = (self.0 << 1) | direction_bit;
155 DLCI::try_from(v).map_err(RfcommError::from)
156 }
157}
158
159impl TryFrom<u8> for ServerChannel {
160 type Error = RfcommError;
161 fn try_from(src: u8) -> Result<ServerChannel, Self::Error> {
162 if src < Self::MIN.0 || src > Self::MAX.0 {
163 return Err(RfcommError::Other(format_err!("Out of range: {:?}", src).into()));
164 }
165 Ok(ServerChannel(src))
166 }
167}
168
169impl From<ServerChannel> for u8 {
170 fn from(value: ServerChannel) -> u8 {
171 value.0
172 }
173}
174
175impl Display for ServerChannel {
176 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
177 write!(formatter, "{}", self.0)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 use assert_matches::assert_matches;
186
187 #[test]
188 fn test_create_dlci() {
189 let v1 = 10;
190 let dlci = DLCI::try_from(v1);
191 let expected_sc1 = ServerChannel::try_from(5).unwrap();
192 assert!(dlci.is_ok());
193 assert_eq!(ServerChannel::try_from(dlci.unwrap()).unwrap(), expected_sc1);
194
195 let v2 = 0;
196 let dlci = DLCI::try_from(v2).unwrap();
197 assert_matches!(ServerChannel::try_from(dlci), Err(RfcommError::InvalidDLCI(_)));
198
199 let v3 = 2;
200 let dlci = DLCI::try_from(v3);
201 let expected_sc3 = ServerChannel::try_from(1).unwrap();
202 assert!(dlci.is_ok());
203 assert_eq!(ServerChannel::try_from(dlci.unwrap()).unwrap(), expected_sc3);
204
205 let v4 = 61;
206 let dlci = DLCI::try_from(v4);
207 let expected_sc4 = ServerChannel::try_from(30).unwrap();
208 assert!(dlci.is_ok());
209 assert_eq!(ServerChannel::try_from(dlci.unwrap()).unwrap(), expected_sc4);
210
211 let v5 = 1;
212 let dlci = DLCI::try_from(v5);
213 assert!(dlci.is_err());
214
215 let v6 = 62;
216 let dlci = DLCI::try_from(v6);
217 assert!(dlci.is_err());
218
219 let v7 = 63;
220 let dlci = DLCI::try_from(v7);
221 assert!(dlci.is_err());
222 }
223
224 #[test]
225 fn validate_dlci_as_initiator_role() {
226 let role = Role::Initiator;
227
228 let dlci = DLCI::MUX_CONTROL_DLCI;
229 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
230
231 let dlci = DLCI::MIN_USER_DLCI;
232 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
233
234 let dlci = DLCI::try_from(9).unwrap();
235 assert!(dlci.validate(role).is_ok());
236 }
237
238 #[test]
239 fn validate_dlci_as_responder_role() {
240 let role = Role::Responder;
241
242 let dlci = DLCI::MUX_CONTROL_DLCI;
243 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
244
245 let dlci = DLCI::try_from(7).unwrap();
246 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidDLCI(_)));
247
248 let dlci = DLCI::try_from(10).unwrap();
249 assert!(dlci.validate(role).is_ok());
250 }
251
252 #[test]
253 fn validate_dlci_with_invalid_role_returns_error() {
254 let role = Role::Unassigned;
255 let dlci = DLCI::try_from(10).unwrap();
256 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidRole(_)));
257
258 let role = Role::Negotiating;
259 let dlci = DLCI::try_from(11).unwrap();
260 assert_matches!(dlci.validate(role), Err(RfcommError::InvalidRole(_)));
261 }
262
263 #[test]
264 fn dlci_check_is_initiator() {
265 let dlci = DLCI::MUX_CONTROL_DLCI;
266 assert_matches!(dlci.initiator(Role::Initiator), Err(RfcommError::InvalidDLCI(_)));
267 assert_matches!(dlci.initiator(Role::Responder), Err(RfcommError::InvalidDLCI(_)));
268
269 let dlci = DLCI::try_from(20).unwrap();
270 assert_matches!(dlci.initiator(Role::Initiator), Ok(true));
271 assert_matches!(dlci.initiator(Role::Responder), Ok(false));
272
273 let dlci = DLCI::try_from(25).unwrap();
274 assert_matches!(dlci.initiator(Role::Initiator), Ok(false));
275 assert_matches!(dlci.initiator(Role::Responder), Ok(true));
276 }
277
278 #[test]
279 fn dlci_check_as_initiator_with_invalid_role_returns_error() {
280 let role = Role::Unassigned;
281 let dlci = DLCI::try_from(10).unwrap();
282 assert_matches!(dlci.initiator(role), Err(RfcommError::InvalidRole(_)));
283
284 let role = Role::Negotiating;
285 let dlci = DLCI::try_from(11).unwrap();
286 assert_matches!(dlci.initiator(role), Err(RfcommError::InvalidRole(_)));
287 }
288
289 #[test]
290 fn convert_server_channel_to_dlci_invalid_role() {
291 let invalid_role = Role::Unassigned;
292 let server_channel = ServerChannel::try_from(10).unwrap();
293 assert_matches!(server_channel.to_dlci(invalid_role), Err(_));
294
295 let invalid_role = Role::Negotiating;
296 let server_channel = ServerChannel::try_from(13).unwrap();
297 assert_matches!(server_channel.to_dlci(invalid_role), Err(_));
298 }
299
300 #[test]
301 fn convert_server_channel_to_dlci_success() {
302 let server_channel = ServerChannel::try_from(5).unwrap();
303 let expected_dlci = DLCI::try_from(11).unwrap();
304 assert_eq!(server_channel.to_dlci(Role::Initiator).unwrap(), expected_dlci);
305
306 let expected_dlci = DLCI::try_from(10).unwrap();
307 assert_eq!(server_channel.to_dlci(Role::Responder).unwrap(), expected_dlci);
308
309 let server_channel = ServerChannel::MIN;
310 let expected_dlci = DLCI::try_from(2).unwrap();
311 assert_eq!(server_channel.to_dlci(Role::Responder).unwrap(), expected_dlci);
312
313 let server_channel = ServerChannel::MAX;
314 let expected_dlci = DLCI::try_from(61).unwrap();
315 assert_eq!(server_channel.to_dlci(Role::Initiator).unwrap(), expected_dlci);
316 }
317
318 #[test]
319 fn server_channel_from_primitive() {
320 let normal = 10;
321 let sc = ServerChannel::try_from(normal);
322 assert!(sc.is_ok());
323
324 let invalid = 0;
325 let sc = ServerChannel::try_from(invalid);
326 assert_matches!(sc, Err(_));
327
328 let too_large = 31;
329 let sc = ServerChannel::try_from(too_large);
330 assert_matches!(sc, Err(_));
331
332 let u8_max = std::u8::MAX;
333 let sc = ServerChannel::try_from(u8_max);
334 assert_matches!(sc, Err(_));
335 }
336}