bt_rfcomm/frame/mux_commands/
modem_status.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, Encodable};
7
8use crate::frame::FrameParseError;
9use crate::DLCI;
10
11/// Length (in bytes) of a Modem Status Command with no break value.
12const MODEM_STATUS_COMMAND_WITHOUT_BREAK_LENGTH: usize = 2;
13
14/// Length (in bytes) of a Modem Status Command with break value.
15const MODEM_STATUS_COMMAND_WITH_BREAK_LENGTH: usize = 3;
16
17bitfield! {
18    /// The Modem Status Address field defined in GSM 7.10 Section 5.4.6.3.7 Figure 9.
19    struct ModemStatusAddressField(u8);
20    impl Debug;
21    bool;
22    pub ea_bit, set_ea_bit: 0;
23    pub cr_bit, set_cr_bit: 1;
24    pub u8, dlci_raw, set_dlci: 7,2;
25}
26
27impl ModemStatusAddressField {
28    fn dlci(&self) -> Result<DLCI, FrameParseError> {
29        DLCI::try_from(self.dlci_raw())
30    }
31}
32
33bitfield! {
34    /// The Modem Status Signals defined in GSM 7.10 Section 5.4.6.3.7 Figure 10.
35    pub struct ModemStatusSignals(u8);
36    impl Debug;
37    bool;
38    pub ea_bit, set_ea_bit: 0;
39    pub flow_control, set_flow_control: 1;
40    pub ready_to_communicate, set_ready_to_communicate: 2;
41    pub ready_to_receive, set_ready_to_receive: 3;
42    pub incoming_call, set_incoming_call: 6;
43    pub data_valid, set_data_valid: 7;
44}
45
46impl Clone for ModemStatusSignals {
47    fn clone(&self) -> Self {
48        Self(self.0)
49    }
50}
51
52impl Default for ModemStatusSignals {
53    /// The default modem status signals.
54    ///
55    /// See GSM 7.10 Section 5.4.6.3.7 for the interpretation of each signal bit.
56    fn default() -> Self {
57        let mut signals = Self(0);
58        signals.set_ea_bit(true); // Always set. Only one octet for the signals.
59        signals.set_flow_control(false); // Indicates ready to accept frames.
60        signals.set_ready_to_communicate(true); // Ready to communicate.
61        signals.set_ready_to_receive(true); // Ready to receive.
62        signals.set_incoming_call(false); // No incoming call.
63        signals.set_data_valid(true); // Valid data being sent.
64        signals
65    }
66}
67
68bitfield! {
69    /// The Modem Status Break value defined in GSM 7.10 Section 5.4.6.3.7 Figure 11.
70    struct ModemStatusBreakField(u8);
71    impl Debug;
72    bool;
73    pub ea_bit, set_ea_bit: 0;
74    pub contains_break_value, set_contains_break_value: 1;
75    pub u8, break_value, set_break_value: 7,4;
76}
77
78impl PartialEq for ModemStatusSignals {
79    fn eq(&self, other: &Self) -> bool {
80        self.0 == other.0
81    }
82}
83
84/// Modem Status Command is used to convey V .24 control signals to the DLC.
85/// Defined in GSM 7.10 Section 6.4.6.3.7.
86#[derive(Clone, Debug, PartialEq)]
87pub struct ModemStatusParams {
88    pub dlci: DLCI,
89    pub signals: ModemStatusSignals,
90    // Break signal in data stream. In units of 200ms as defined in GSM 7.10 Section 5.4.6.3.7.
91    pub break_value: Option<u8>,
92}
93
94impl ModemStatusParams {
95    /// Returns the default parameters for a Modem Status command.
96    pub fn default(dlci: DLCI) -> Self {
97        Self { dlci, signals: ModemStatusSignals::default(), break_value: None }
98    }
99}
100
101impl Decodable for ModemStatusParams {
102    type Error = FrameParseError;
103
104    fn decode(buf: &[u8]) -> Result<Self, FrameParseError> {
105        if buf.len() != MODEM_STATUS_COMMAND_WITH_BREAK_LENGTH
106            && buf.len() != MODEM_STATUS_COMMAND_WITHOUT_BREAK_LENGTH
107        {
108            return Err(FrameParseError::InvalidBufferLength(
109                MODEM_STATUS_COMMAND_WITH_BREAK_LENGTH,
110                buf.len(),
111            ));
112        }
113
114        // Address field.
115        let address_field = ModemStatusAddressField(buf[0]);
116        let dlci = address_field.dlci()?;
117
118        // Signals field.
119        let signals = ModemStatusSignals(buf[1]);
120
121        // Optional Break Value field.
122        let mut break_value = None;
123        if buf.len() == MODEM_STATUS_COMMAND_WITH_BREAK_LENGTH {
124            let value = ModemStatusBreakField(buf[2]);
125            if value.contains_break_value() {
126                break_value = Some(value.break_value());
127            }
128        }
129
130        Ok(Self { dlci, signals, break_value })
131    }
132}
133
134impl Encodable for ModemStatusParams {
135    type Error = FrameParseError;
136
137    fn encoded_len(&self) -> usize {
138        if self.break_value.is_some() {
139            MODEM_STATUS_COMMAND_WITH_BREAK_LENGTH
140        } else {
141            MODEM_STATUS_COMMAND_WITHOUT_BREAK_LENGTH
142        }
143    }
144
145    fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
146        if buf.len() < self.encoded_len() {
147            return Err(FrameParseError::BufferTooSmall);
148        }
149
150        // Address field. E/A bit = 1, C/R bit = 1. See GSM 7.10 Section 5.4.6.3.7 Figure 9.
151        let mut address_field = ModemStatusAddressField(0);
152        address_field.set_ea_bit(true);
153        address_field.set_cr_bit(true);
154        address_field.set_dlci(u8::from(self.dlci));
155        buf[0] = address_field.0;
156
157        // Status Signals. E/A = 1 if no break value.
158        let mut status_signals_field = ModemStatusSignals(self.signals.0);
159        let ea_bit = self.break_value.is_none();
160        status_signals_field.set_ea_bit(ea_bit);
161        buf[1] = status_signals_field.0;
162
163        // (Optional) Break signal. E/A = 1 since last octet. Set break value.
164        if self.break_value.is_some() {
165            let mut break_value_field = ModemStatusBreakField(0);
166            break_value_field.set_ea_bit(true);
167            break_value_field.set_contains_break_value(true);
168            break_value_field.set_break_value(self.break_value.unwrap());
169            buf[2] = break_value_field.0;
170        }
171
172        Ok(())
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    use assert_matches::assert_matches;
181
182    #[test]
183    fn test_decode_modem_status_invalid_buf() {
184        let buf = [];
185        assert_matches!(
186            ModemStatusParams::decode(&buf[..]),
187            Err(FrameParseError::InvalidBufferLength(MODEM_STATUS_COMMAND_WITH_BREAK_LENGTH, 0))
188        );
189    }
190
191    #[test]
192    fn test_decode_modem_status_invalid_dlci() {
193        let buf = [
194            0b00000111, // DLCI = 1 is invalid, E/A = 1, Bit2 = 1 always.
195            0b00000001, // Signals = 0, E/A = 1 -> No break.
196        ];
197        assert_matches!(ModemStatusParams::decode(&buf[..]), Err(FrameParseError::InvalidDLCI(1)));
198    }
199
200    #[test]
201    fn test_decode_modem_status_without_break_value() {
202        let buf = [
203            0b00001111, // DLCI = 3, E/A = 1, Bit2 = 1 always.
204            0b00000001, // Signals = 0, E/A = 1 -> No break.
205        ];
206        let expected = ModemStatusParams {
207            dlci: DLCI::try_from(3).unwrap(),
208            signals: ModemStatusSignals(1),
209            break_value: None,
210        };
211        assert_eq!(ModemStatusParams::decode(&buf[..]).unwrap(), expected);
212    }
213
214    #[test]
215    fn test_decode_modem_status_with_empty_break_value() {
216        let buf = [
217            0b00001111, // DLCI = 3, E/A = 1, Bit2 = 1 always.
218            0b00000000, // Signals = 0, E/A = 0 -> Break.
219            0b00000001, // E/A = 1, B1 = 0 -> no break.
220        ];
221        let expected = ModemStatusParams {
222            dlci: DLCI::try_from(3).unwrap(),
223            signals: ModemStatusSignals(0),
224            break_value: None,
225        };
226        assert_eq!(ModemStatusParams::decode(&buf[..]).unwrap(), expected);
227    }
228
229    #[test]
230    fn test_decode_modem_status_with_break_value() {
231        let buf = [
232            0b00011111, // DLCI = 7, E/A = 1, Bit2 = 1 always.
233            0b11000011, // Signals set, E/A = 0 -> Break.
234            0b10100011, // E/A = 1, B1 = 1 -> Break = 10.
235        ];
236        let expected = ModemStatusParams {
237            dlci: DLCI::try_from(7).unwrap(),
238            signals: ModemStatusSignals(195),
239            break_value: Some(10),
240        };
241        assert_eq!(ModemStatusParams::decode(&buf[..]).unwrap(), expected);
242    }
243
244    #[test]
245    fn test_encode_modem_status_invalid_buf() {
246        let mut buf = [];
247        let command = ModemStatusParams {
248            dlci: DLCI::try_from(5).unwrap(),
249            signals: ModemStatusSignals(100),
250            break_value: Some(11),
251        };
252        assert_matches!(command.encode(&mut buf[..]), Err(FrameParseError::BufferTooSmall));
253    }
254
255    #[test]
256    fn test_encode_modem_status_no_break_signal() {
257        let mut buf = [0; 2];
258        let command = ModemStatusParams {
259            dlci: DLCI::try_from(5).unwrap(),
260            signals: ModemStatusSignals(1),
261            break_value: None,
262        };
263        let expected = [
264            0b00010111, // DLCI = 5, E/A = 1, Bit2 = 1 always.
265            0b00000001, // Signals = 0, E/A = 1 -> No break.
266        ];
267        assert!(command.encode(&mut buf[..]).is_ok());
268        assert_eq!(buf, expected);
269    }
270
271    #[test]
272    fn test_encode_modem_status_with_break_signal() {
273        let mut buf = [0; 3];
274        let command = ModemStatusParams {
275            dlci: DLCI::try_from(6).unwrap(),
276            signals: ModemStatusSignals(201),
277            break_value: Some(3),
278        };
279        let expected = [
280            0b00011011, // DLCI = 6, E/A = 1, Bit2 = 1 always.
281            0b11001000, // Signals = 0, E/A = 0 -> Break value.
282            0b00110011, // E/A = 1, B1 = 1 -> Break = 3.
283        ];
284        assert!(command.encode(&mut buf[..]).is_ok());
285        assert_eq!(buf, expected);
286    }
287}