bt_avctp/avctp/
types.rs

1// Copyright 2019 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
7use crate::{Error, Result};
8
9/// An AVCTP Transaction Label
10/// Not used outside the library. Public as part of some internal Error variants.
11/// See Section 6.1.1
12#[derive(Debug, Clone, PartialEq)]
13pub struct TxLabel(u8);
14
15// Transaction labels are only 4 bits.
16const MAX_TX_LABEL: u8 = 0xF;
17
18impl TryFrom<u8> for TxLabel {
19    type Error = Error;
20    fn try_from(value: u8) -> Result<Self> {
21        if value > MAX_TX_LABEL {
22            Err(Error::OutOfRange)
23        } else {
24            Ok(TxLabel(value))
25        }
26    }
27}
28
29impl From<&TxLabel> for u8 {
30    fn from(v: &TxLabel) -> u8 {
31        v.0
32    }
33}
34
35impl From<&TxLabel> for usize {
36    fn from(v: &TxLabel) -> usize {
37        v.0 as usize
38    }
39}
40
41/// An AVCTP Profile Identifier
42/// The type indicates the how the command/request frame is encoded. It should be identical to the
43/// 16bit UUID of the service class for this profile.
44/// See Section 6.1.1
45#[derive(Debug, Clone, PartialEq)]
46pub(crate) struct ProfileId([u8; 2]);
47
48/// 16bit UUID for "A/V Remote Control" assigned by the Bluetooth assigned numbers document
49pub(crate) const AV_REMOTE_PROFILE: &ProfileId = &ProfileId([0x11, 0x0e]);
50
51impl From<[u8; 2]> for ProfileId {
52    fn from(value: [u8; 2]) -> Self {
53        Self(value)
54    }
55}
56
57decodable_enum! {
58    /// Indicates whether this packet is part of a fragmented packet set.
59    /// See Section 6.1
60    pub enum PacketType<u8, Error, OutOfRange> {
61        Single = 0x00,
62        Start = 0x01,
63        Continue = 0x02,
64        End = 0x03,
65    }
66}
67
68decodable_enum! {
69    /// Specifies the type of the packet as being either Command or Response
70    /// See Section 6.1.1
71    pub enum MessageType<u8, Error, OutOfRange> {
72        Command = 0x00,
73        Response = 0x01,
74    }
75}
76
77#[derive(Debug)]
78pub struct Header {
79    label: TxLabel,            // byte 0, bit 7..4
80    packet_type: PacketType,   // byte 0, bit 3..2
81    message_type: MessageType, // byte 0, bit 1
82    invalid_profile_id: bool,  // byte 0, bit 0
83    num_packets: u8,           // byte 1 if packet type == start
84    profile_id: ProfileId,     // byte 1..2 (byte 2..3 if packet type is start)
85}
86
87impl Header {
88    pub(crate) fn new(
89        label: TxLabel,
90        profile_id: ProfileId,
91        message_type: MessageType,
92        invalid_profile_id: bool,
93    ) -> Header {
94        Header {
95            label,
96            profile_id,
97            message_type,
98            packet_type: PacketType::Single,
99            invalid_profile_id,
100            num_packets: 1,
101        }
102    }
103
104    /// Creates a new header from this header with it's message type set to response.
105    pub(crate) fn create_response(&self, packet_type: PacketType) -> Header {
106        Header {
107            label: self.label.clone(),
108            profile_id: self.profile_id.clone(),
109            message_type: MessageType::Response,
110            packet_type,
111            invalid_profile_id: false,
112            num_packets: 1,
113        }
114    }
115
116    /// Creates a new header from this header with it's message type set to response
117    /// and with the ipid (invalid profile id) bit set to true.
118    pub(crate) fn create_invalid_profile_id_response(&self) -> Header {
119        Header {
120            label: self.label.clone(),
121            profile_id: self.profile_id.clone(),
122            message_type: MessageType::Response,
123            packet_type: PacketType::Single,
124            invalid_profile_id: true,
125            num_packets: 1,
126        }
127    }
128
129    pub(crate) fn label(&self) -> &TxLabel {
130        &self.label
131    }
132
133    pub(crate) fn profile_id(&self) -> &ProfileId {
134        &self.profile_id
135    }
136
137    pub fn message_type(&self) -> &MessageType {
138        &self.message_type
139    }
140
141    pub fn packet_type(&self) -> &PacketType {
142        &self.packet_type
143    }
144
145    pub fn is_invalid_profile_id(&self) -> bool {
146        self.invalid_profile_id
147    }
148
149    // convenience helpers
150    pub fn is_type(&self, other: &MessageType) -> bool {
151        &self.message_type == other
152    }
153
154    pub fn is_single(&self) -> bool {
155        self.packet_type == PacketType::Single
156    }
157}
158
159impl Decodable for Header {
160    type Error = Error;
161
162    fn decode(bytes: &[u8]) -> Result<Header> {
163        if bytes.len() < 3 {
164            return Err(Error::OutOfRange);
165        }
166        let label = TxLabel::try_from(bytes[0] >> 4)?;
167        let packet_type = PacketType::try_from((bytes[0] >> 2) & 0x3)?;
168        let (id_offset, num_packets) = match packet_type {
169            PacketType::Start => {
170                if bytes.len() < 4 {
171                    return Err(Error::OutOfRange);
172                }
173                (2, bytes[1])
174            }
175            _ => (1, 1),
176        };
177
178        let profile_id = ProfileId::from([bytes[id_offset], bytes[id_offset + 1]]);
179        let invalid_profile_id = bytes[0] & 0x1 == 1;
180        let header = Header {
181            label,
182            profile_id,
183            message_type: MessageType::try_from(bytes[0] >> 1 & 0x1)?,
184            packet_type,
185            invalid_profile_id,
186            num_packets,
187        };
188        Ok(header)
189    }
190}
191
192impl Encodable for Header {
193    type Error = Error;
194
195    fn encoded_len(&self) -> usize {
196        match self.packet_type {
197            PacketType::Start => 4,
198            _ => 3,
199        }
200    }
201
202    fn encode(&self, buf: &mut [u8]) -> Result<()> {
203        if buf.len() < self.encoded_len() {
204            return Err(Error::Encoding);
205        }
206        let invalid_profile_id: u8 = if self.invalid_profile_id { 1 } else { 0 };
207        buf[0] = u8::from(&self.label) << 4
208            | u8::from(&self.packet_type) << 2
209            | u8::from(&self.message_type) << 1
210            | invalid_profile_id;
211        let mut buf_idx = 1;
212        if self.packet_type == PacketType::Start {
213            buf[buf_idx] = self.num_packets;
214            buf_idx = 2;
215        }
216        let profile_id = self.profile_id.0;
217        buf[buf_idx] = profile_id[0];
218        buf[buf_idx + 1] = profile_id[1];
219        Ok(())
220    }
221}
222
223#[cfg(test)]
224mod test {
225    use super::*;
226
227    #[test]
228    /// Test Header encoding
229    fn test_header_encode() {
230        let header =
231            Header::new(TxLabel(0), AV_REMOTE_PROFILE.clone(), MessageType::Command, false);
232        assert!(!header.is_invalid_profile_id());
233        assert!(header.is_single());
234        assert!(header.is_type(&MessageType::Command));
235        assert_eq!(TxLabel(0), *header.label());
236        let len = header.encoded_len();
237        assert_eq!(3, len);
238        let mut buf = vec![0; len];
239        assert!(header.encode(&mut buf[..]).is_ok());
240
241        assert_eq!(
242            &[
243                0x00, // TxLabel 0, Single 0, Command 0, Ipid 0,
244                0x11, // AV PROFILE
245                0x0e, // AV PROFILE
246            ],
247            &buf[..]
248        );
249    }
250
251    #[test]
252    /// Test Header encoding
253    fn test_header_encode_response() {
254        let header =
255            Header::new(TxLabel(15), AV_REMOTE_PROFILE.clone(), MessageType::Command, false);
256        let header = header.create_response(PacketType::Single);
257        assert!(!header.is_invalid_profile_id());
258        assert!(header.is_single());
259        assert!(header.is_type(&MessageType::Response));
260        assert_eq!(TxLabel(15), *header.label());
261        let len = header.encoded_len();
262        assert_eq!(3, len);
263        let mut buf = vec![0; len];
264        assert!(header.encode(&mut buf[..]).is_ok());
265
266        assert_eq!(
267            &[
268                0xf2, // TxLabel 15, Single 0, Response 1, Ipid 0
269                0x11, // AV PROFILE
270                0x0e, // AV PROFILE
271            ],
272            &buf[..]
273        );
274    }
275
276    #[test]
277    /// Test Header encoding
278    fn test_header_encode_invalid_profile_response() {
279        let header =
280            Header::new(TxLabel(0), AV_REMOTE_PROFILE.clone(), MessageType::Command, false);
281        let header = header.create_invalid_profile_id_response();
282        assert!(header.is_invalid_profile_id());
283        assert!(header.is_single());
284        assert!(header.is_type(&MessageType::Response));
285        assert_eq!(TxLabel(0), *header.label());
286        let len = header.encoded_len();
287        assert_eq!(3, len);
288        let mut buf = vec![0; len];
289        assert!(header.encode(&mut buf[..]).is_ok());
290
291        assert_eq!(
292            &[
293                0x03, // TxLabel 0, Single 0, Response 1, Ipid 1
294                0x11, // AV PROFILE
295                0x0e, // AV PROFILE
296            ],
297            &buf[..]
298        );
299    }
300
301    #[test]
302    /// Test Header decoding
303    fn test_header_decode_invalid_packet_response() {
304        let header = Header::decode(&[
305            0xf3, // TxLabel 15, Single 0, Response 1, Ipid 1
306            0x11, // AV PROFILE
307            0x0e, // AV PROFILE
308            0x12, // extra ignored
309            0x34, // extra ignored
310            0x45, // extra ignored
311        ])
312        .expect("unable to decode header");
313        assert!(header.is_invalid_profile_id());
314        assert!(header.is_single());
315        assert!(header.is_type(&MessageType::Response));
316        assert_eq!(TxLabel(15), *header.label());
317    }
318
319    #[test]
320    /// Test Header decoding
321    fn test_header_decode_command() {
322        let header = Header::decode(&[
323            0x80, // TxLabel 8, Single 0, Command 0, Ipid 0
324            0x11, // AV PROFILE
325            0x0e, // AV PROFILE
326            0x34, // extra ignored
327            0x45, // extra ignored
328        ])
329        .expect("unable to decode header");
330        assert!(!header.is_invalid_profile_id());
331        assert!(header.is_single());
332        assert!(header.is_type(&MessageType::Command));
333        assert_eq!(TxLabel(8), *header.label());
334    }
335
336    #[test]
337    /// Test Header decoding
338    fn test_header_decode_invalid() {
339        assert_eq!(
340            Error::OutOfRange,
341            Header::decode(&[
342                0x80, // TxLabel 8, Single 0, Command 0, Ipid 0
343                0x11, // AV PROFILE
344                      // missing fields
345            ])
346            .unwrap_err()
347        );
348    }
349
350    #[test]
351    fn txlabel_tofrom_u8() {
352        let mut label: Result<TxLabel> = TxLabel::try_from(15);
353        assert!(label.is_ok());
354        assert_eq!(15, u8::from(&label.unwrap()));
355        label = TxLabel::try_from(16);
356        assert_eq!(Err(Error::OutOfRange), label);
357    }
358
359    #[test]
360    fn txlabel_to_usize() {
361        let label = TxLabel::try_from(1).unwrap();
362        assert_eq!(1, usize::from(&label));
363    }
364}