bt_hci/
param.rs

1//! Parameter types for HCI command and event packets [📖](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-8af7a4d8-7a08-0895-b041-fdf9e27d6508)
2
3use crate::{AsHciBytes, ByteAlignedValue, FixedSizeValue, FromHciBytes, FromHciBytesError, WriteHci};
4
5mod classic;
6mod cmd_mask;
7mod event_masks;
8mod feature_masks;
9mod le;
10mod macros;
11mod primitives;
12mod status;
13
14pub use classic::*;
15pub use cmd_mask::*;
16pub use event_masks::*;
17pub use feature_masks::*;
18pub use le::*;
19pub(crate) use macros::{param, param_slice};
20pub use status::*;
21
22/// A special parameter which takes all remaining bytes in the buffer
23#[repr(transparent)]
24#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct RemainingBytes<'a>(&'a [u8]);
27
28impl core::ops::Deref for RemainingBytes<'_> {
29    type Target = [u8];
30
31    fn deref(&self) -> &Self::Target {
32        self.0
33    }
34}
35
36impl WriteHci for RemainingBytes<'_> {
37    #[inline(always)]
38    fn size(&self) -> usize {
39        self.0.len()
40    }
41
42    #[inline(always)]
43    fn write_hci<W: embedded_io::Write>(&self, mut writer: W) -> Result<(), W::Error> {
44        writer.write_all(self.0)
45    }
46
47    #[inline(always)]
48    async fn write_hci_async<W: embedded_io_async::Write>(&self, mut writer: W) -> Result<(), W::Error> {
49        writer.write_all(self.0).await
50    }
51}
52
53impl AsHciBytes for RemainingBytes<'_> {
54    fn as_hci_bytes(&self) -> &[u8] {
55        self.0
56    }
57}
58
59impl<'a> FromHciBytes<'a> for RemainingBytes<'a> {
60    fn from_hci_bytes(data: &'a [u8]) -> Result<(Self, &'a [u8]), FromHciBytesError> {
61        Ok((RemainingBytes(data), &[]))
62    }
63}
64
65param!(struct BdAddr([u8; 6]));
66
67impl BdAddr {
68    /// Create a new instance.
69    pub fn new(val: [u8; 6]) -> Self {
70        Self(val)
71    }
72
73    /// Get the byte representation.
74    pub fn raw(&self) -> &[u8] {
75        &self.0[..]
76    }
77}
78
79unsafe impl ByteAlignedValue for BdAddr {}
80
81impl<'de> crate::FromHciBytes<'de> for &'de BdAddr {
82    #[inline(always)]
83    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
84        <BdAddr as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
85    }
86}
87
88param!(struct ConnHandle(u16));
89
90impl ConnHandle {
91    /// Create a new instance.
92    pub fn new(val: u16) -> Self {
93        assert!(val <= 0xeff);
94        Self(val)
95    }
96
97    /// Get the underlying representation.
98    pub fn raw(&self) -> u16 {
99        self.0
100    }
101}
102
103/// A 16-bit duration. The `US` generic parameter indicates the timebase in µs.
104#[repr(transparent)]
105#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
106#[cfg_attr(feature = "defmt", derive(defmt::Format))]
107pub struct Duration<const US: u32 = 625>(u16);
108
109unsafe impl<const US: u32> FixedSizeValue for Duration<US> {
110    #[inline(always)]
111    fn is_valid(_data: &[u8]) -> bool {
112        true
113    }
114}
115
116impl<const US: u32> Duration<US> {
117    #[inline(always)]
118    /// Create a new instance from raw value.
119    pub const fn from_u16(val: u16) -> Self {
120        Self(val)
121    }
122
123    /// Create an instance from microseconds.
124    #[inline(always)]
125    pub fn from_micros(val: u64) -> Self {
126        Self::from_u16(unwrap!((val / u64::from(US)).try_into()))
127    }
128
129    /// Create an instance from milliseconds.
130    #[inline(always)]
131    pub fn from_millis(val: u32) -> Self {
132        Self::from_micros(u64::from(val) * 1000)
133    }
134
135    /// Create an instance from seconds.
136    #[inline(always)]
137    pub fn from_secs(val: u32) -> Self {
138        Self::from_micros(u64::from(val) * 1_000_000)
139    }
140
141    /// Get the underlying representation.
142    #[inline(always)]
143    pub fn as_u16(&self) -> u16 {
144        self.0
145    }
146
147    /// Get value as microseconds.
148    #[inline(always)]
149    pub fn as_micros(&self) -> u64 {
150        u64::from(self.as_u16()) * u64::from(US)
151    }
152
153    /// Get value as milliseconds.
154    #[inline(always)]
155    pub fn as_millis(&self) -> u32 {
156        unwrap!((self.as_micros() / 1000).try_into())
157    }
158
159    /// Get value as seconds.
160    #[inline(always)]
161    pub fn as_secs(&self) -> u32 {
162        // (u16::MAX * u32::MAX / 1_000_000) < u32::MAX so this is safe
163        (self.as_micros() / 1_000_000) as u32
164    }
165}
166
167#[cfg(feature = "embassy-time")]
168impl<const US: u32> From<embassy_time::Duration> for Duration<US> {
169    fn from(duration: embassy_time::Duration) -> Self {
170        Self::from_micros(duration.as_micros())
171    }
172}
173
174/// A 24-bit isochronous duration (in microseconds)
175#[repr(transparent)]
176#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
178pub struct ExtDuration<const US: u16 = 1>([u8; 3]);
179
180unsafe impl<const US: u16> FixedSizeValue for ExtDuration<US> {
181    #[inline(always)]
182    fn is_valid(_data: &[u8]) -> bool {
183        true
184    }
185}
186
187unsafe impl<const US: u16> ByteAlignedValue for ExtDuration<US> {}
188
189impl<'de, const US: u16> FromHciBytes<'de> for &'de ExtDuration<US> {
190    #[inline(always)]
191    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
192        <ExtDuration<US> as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
193    }
194}
195
196impl<const US: u16> ExtDuration<US> {
197    /// Create a new instance from raw value.
198    #[inline(always)]
199    pub fn from_u32(val: u32) -> Self {
200        assert!(val < (1 << 24));
201        Self(*unwrap!(val.to_le_bytes().first_chunk()))
202    }
203
204    /// Create an instance from microseconds.
205    #[inline(always)]
206    pub fn from_micros(val: u64) -> Self {
207        Self::from_u32(unwrap!((val / u64::from(US)).try_into()))
208    }
209
210    /// Create an instance from milliseconds.
211    #[inline(always)]
212    pub fn from_millis(val: u32) -> Self {
213        Self::from_micros(u64::from(val) * 1000)
214    }
215
216    /// Create an instance from seconds.
217    #[inline(always)]
218    pub fn from_secs(val: u32) -> Self {
219        Self::from_micros(u64::from(val) * 1_000_000)
220    }
221
222    /// Get value as microseconds.
223    #[inline(always)]
224    pub fn as_micros(&self) -> u64 {
225        u64::from_le_bytes([self.0[0], self.0[1], self.0[2], 0, 0, 0, 0, 0]) * u64::from(US)
226    }
227
228    /// Get value as milliseconds.
229    #[inline(always)]
230    pub fn as_millis(&self) -> u32 {
231        // ((1 << 24 - 1) * u16::MAX / 1_000) < u32::MAX so this is safe
232        (self.as_micros() / 1000) as u32
233    }
234
235    /// Get value as seconds.
236    #[inline(always)]
237    pub fn as_secs(&self) -> u32 {
238        // ((1 << 24 - 1) * u16::MAX / 1_000_000) < u32::MAX so this is safe
239        (self.as_micros() / 1_000_000) as u32
240    }
241}
242
243#[cfg(feature = "embassy-time")]
244impl<const US: u16> From<embassy_time::Duration> for ExtDuration<US> {
245    fn from(duration: embassy_time::Duration) -> Self {
246        Self::from_micros(duration.as_micros())
247    }
248}
249
250param!(
251    enum DisconnectReason {
252        AuthenticationFailure = 0x05,
253        RemoteUserTerminatedConn = 0x13,
254        RemoteDeviceTerminatedConnLowResources = 0x14,
255        RemoteDeviceTerminatedConnPowerOff = 0x15,
256        UnsupportedRemoteFeature = 0x1A,
257        PairingWithUnitKeyNotSupported = 0x29,
258        UnacceptableConnParameters = 0x3b,
259    }
260);
261
262param!(
263    enum RemoteConnectionParamsRejectReason {
264        UnacceptableConnParameters = 0x3b,
265    }
266);
267
268param! {
269    #[derive(Default)]
270    enum PowerLevelKind {
271        #[default]
272        Current = 0,
273        Maximum = 1,
274    }
275}
276
277param! {
278    #[derive(Default)]
279    enum ControllerToHostFlowControl {
280        #[default]
281        Off = 0,
282        AclOnSyncOff = 1,
283        AclOffSyncOn = 2,
284        BothOn = 3,
285    }
286}
287
288param!(struct CoreSpecificationVersion(u8));
289
290#[allow(missing_docs)]
291impl CoreSpecificationVersion {
292    pub const VERSION_1_0B: CoreSpecificationVersion = CoreSpecificationVersion(0x00);
293    pub const VERSION_1_1: CoreSpecificationVersion = CoreSpecificationVersion(0x01);
294    pub const VERSION_1_2: CoreSpecificationVersion = CoreSpecificationVersion(0x02);
295    pub const VERSION_2_0_EDR: CoreSpecificationVersion = CoreSpecificationVersion(0x03);
296    pub const VERSION_2_1_EDR: CoreSpecificationVersion = CoreSpecificationVersion(0x04);
297    pub const VERSION_3_0_HS: CoreSpecificationVersion = CoreSpecificationVersion(0x05);
298    pub const VERSION_4_0: CoreSpecificationVersion = CoreSpecificationVersion(0x06);
299    pub const VERSION_4_1: CoreSpecificationVersion = CoreSpecificationVersion(0x07);
300    pub const VERSION_4_2: CoreSpecificationVersion = CoreSpecificationVersion(0x08);
301    pub const VERSION_5_0: CoreSpecificationVersion = CoreSpecificationVersion(0x09);
302    pub const VERSION_5_1: CoreSpecificationVersion = CoreSpecificationVersion(0x0A);
303    pub const VERSION_5_2: CoreSpecificationVersion = CoreSpecificationVersion(0x0B);
304    pub const VERSION_5_3: CoreSpecificationVersion = CoreSpecificationVersion(0x0C);
305    pub const VERSION_5_4: CoreSpecificationVersion = CoreSpecificationVersion(0x0D);
306}
307
308unsafe impl ByteAlignedValue for CoreSpecificationVersion {}
309
310impl<'de> crate::FromHciBytes<'de> for &'de CoreSpecificationVersion {
311    #[inline(always)]
312    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
313        <CoreSpecificationVersion as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
314    }
315}
316
317param! {
318    #[derive(Default)]
319    enum LinkType {
320        #[default]
321        SyncData = 0,
322        AclData = 1,
323        IsoData = 2,
324    }
325}
326
327param_slice! {
328    [ConnHandleCompletedPackets; 4] {
329        handle[0]: ConnHandle,
330        num_completed_packets[2]: u16,
331    }
332}
333
334impl ConnHandleCompletedPackets {
335    /// Create a new instance.
336    pub fn new(handle: ConnHandle, num_completed_packets: u16) -> Self {
337        let mut dest = [0; 4];
338        handle.write_hci(&mut dest[0..2]).unwrap();
339        num_completed_packets.write_hci(&mut dest[2..4]).unwrap();
340        Self(dest)
341    }
342}
343
344#[cfg(test)]
345mod tests {
346    #[cfg(feature = "serde")]
347    use postcard;
348
349    use super::*;
350
351    #[test]
352    fn test_encode_decode_conn_handle_completed_packets() {
353        let completed = ConnHandleCompletedPackets::new(ConnHandle::new(42), 2334);
354
355        assert_eq!(completed.handle().unwrap(), ConnHandle::new(42));
356        assert_eq!(completed.num_completed_packets().unwrap(), 2334);
357    }
358
359    #[cfg(feature = "serde")]
360    #[test]
361    fn test_serialize_bdaddr() {
362        let bytes = [0x01, 0xaa, 0x55, 0x04, 0x05, 0xfe];
363
364        let address = BdAddr::new(bytes);
365
366        let mut buffer = [0u8; 32];
367        let vykort = postcard::to_slice(&address, &mut buffer).unwrap();
368
369        assert_eq!(vykort, &bytes);
370    }
371
372    #[cfg(feature = "serde")]
373    #[test]
374    fn test_deserialize_bdaddr() {
375        let bytes = [0xff, 0x5a, 0xa5, 0x00, 0x05, 0xfe];
376
377        let address = postcard::from_bytes::<BdAddr>(&bytes).unwrap();
378
379        let expected = BdAddr::new(bytes);
380
381        assert_eq!(address, expected);
382    }
383}