Skip to main content

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
65impl<'a> RemainingBytes<'a> {
66    pub(crate) fn into_inner(self) -> &'a [u8] {
67        self.0
68    }
69}
70
71param!(struct BdAddr([u8; 6]));
72
73impl BdAddr {
74    /// Create a new instance.
75    pub fn new(val: [u8; 6]) -> Self {
76        Self(val)
77    }
78
79    /// Get the byte representation.
80    pub fn raw(&self) -> &[u8] {
81        &self.0[..]
82    }
83}
84
85unsafe impl ByteAlignedValue for BdAddr {}
86
87impl<'de> crate::FromHciBytes<'de> for &'de BdAddr {
88    #[inline(always)]
89    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
90        <BdAddr as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
91    }
92}
93
94param!(struct ConnHandle(u16));
95
96impl ConnHandle {
97    /// Create a new instance.
98    pub fn new(val: u16) -> Self {
99        assert!(val <= 0xeff);
100        Self(val)
101    }
102
103    /// Get the underlying representation.
104    pub fn raw(&self) -> u16 {
105        self.0
106    }
107}
108
109/// An 8-bit duration. The `US` generic parameter indicates the timebase in µs.
110#[repr(transparent)]
111#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
112#[cfg_attr(feature = "defmt", derive(defmt::Format))]
113pub struct DurationU8<const US: u32 = 125>(u8);
114
115unsafe impl<const US: u32> FixedSizeValue for DurationU8<US> {
116    #[inline(always)]
117    fn is_valid(_data: &[u8]) -> bool {
118        true
119    }
120}
121
122impl<const US: u32> DurationU8<US> {
123    #[inline(always)]
124    /// Create a new instance from raw value.
125    pub const fn from_u8(val: u8) -> Self {
126        Self(val)
127    }
128
129    /// Create an instance from microseconds.
130    #[inline(always)]
131    pub fn from_micros(val: u64) -> Self {
132        Self::from_u8(unwrap!((val / u64::from(US)).try_into()))
133    }
134
135    /// Create an instance from milliseconds.
136    #[inline(always)]
137    pub fn from_millis(val: u32) -> Self {
138        Self::from_micros(u64::from(val) * 1000)
139    }
140
141    /// Create an instance from seconds.
142    #[inline(always)]
143    pub fn from_secs(val: u32) -> Self {
144        Self::from_micros(u64::from(val) * 1_000_000)
145    }
146
147    /// Get the underlying representation.
148    #[inline(always)]
149    pub fn as_u8(&self) -> u8 {
150        self.0
151    }
152
153    /// Get value as microseconds.
154    #[inline(always)]
155    pub fn as_micros(&self) -> u64 {
156        u64::from(self.as_u8()) * u64::from(US)
157    }
158
159    /// Get value as milliseconds.
160    #[inline(always)]
161    pub fn as_millis(&self) -> u32 {
162        (self.as_micros() / 1000) as u32
163    }
164
165    /// Get value as seconds.
166    #[inline(always)]
167    pub fn as_secs(&self) -> u32 {
168        (self.as_micros() / 1_000_000) as u32
169    }
170}
171
172#[cfg(feature = "embassy-time")]
173impl<const US: u32> From<embassy_time::Duration> for DurationU8<US> {
174    fn from(duration: embassy_time::Duration) -> Self {
175        Self::from_micros(duration.as_micros())
176    }
177}
178
179impl<const US: u32> From<core::time::Duration> for DurationU8<US> {
180    fn from(duration: core::time::Duration) -> Self {
181        Self::from_micros(duration.as_micros().try_into().unwrap())
182    }
183}
184
185/// A 16-bit duration. The `US` generic parameter indicates the timebase in µs.
186#[repr(transparent)]
187#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
188#[cfg_attr(feature = "defmt", derive(defmt::Format))]
189pub struct Duration<const US: u32 = 625>(u16);
190
191unsafe impl<const US: u32> FixedSizeValue for Duration<US> {
192    #[inline(always)]
193    fn is_valid(_data: &[u8]) -> bool {
194        true
195    }
196}
197
198impl<const US: u32> Duration<US> {
199    #[inline(always)]
200    /// Create a new instance from raw value.
201    pub const fn from_u16(val: u16) -> Self {
202        Self(val)
203    }
204
205    /// Create an instance from microseconds.
206    #[inline(always)]
207    pub fn from_micros(val: u64) -> Self {
208        Self::from_u16(unwrap!((val / u64::from(US)).try_into()))
209    }
210
211    /// Create an instance from milliseconds.
212    #[inline(always)]
213    pub fn from_millis(val: u32) -> Self {
214        Self::from_micros(u64::from(val) * 1000)
215    }
216
217    /// Create an instance from seconds.
218    #[inline(always)]
219    pub fn from_secs(val: u32) -> Self {
220        Self::from_micros(u64::from(val) * 1_000_000)
221    }
222
223    /// Get the underlying representation.
224    #[inline(always)]
225    pub fn as_u16(&self) -> u16 {
226        self.0
227    }
228
229    /// Get value as microseconds.
230    #[inline(always)]
231    pub fn as_micros(&self) -> u64 {
232        u64::from(self.as_u16()) * u64::from(US)
233    }
234
235    /// Get value as milliseconds.
236    #[inline(always)]
237    pub fn as_millis(&self) -> u32 {
238        unwrap!((self.as_micros() / 1000).try_into())
239    }
240
241    /// Get value as seconds.
242    #[inline(always)]
243    pub fn as_secs(&self) -> u32 {
244        // (u16::MAX * u32::MAX / 1_000_000) < u32::MAX so this is safe
245        (self.as_micros() / 1_000_000) as u32
246    }
247}
248
249#[cfg(feature = "embassy-time")]
250impl<const US: u32> From<embassy_time::Duration> for Duration<US> {
251    fn from(duration: embassy_time::Duration) -> Self {
252        Self::from_micros(duration.as_micros())
253    }
254}
255
256impl<const US: u32> From<core::time::Duration> for Duration<US> {
257    fn from(duration: core::time::Duration) -> Self {
258        Self::from_micros(duration.as_micros().try_into().unwrap())
259    }
260}
261
262/// A 24-bit isochronous duration (in microseconds)
263#[repr(transparent)]
264#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
265#[cfg_attr(feature = "defmt", derive(defmt::Format))]
266pub struct ExtDuration<const US: u16 = 1>([u8; 3]);
267
268unsafe impl<const US: u16> FixedSizeValue for ExtDuration<US> {
269    #[inline(always)]
270    fn is_valid(_data: &[u8]) -> bool {
271        true
272    }
273}
274
275unsafe impl<const US: u16> ByteAlignedValue for ExtDuration<US> {}
276
277impl<'de, const US: u16> FromHciBytes<'de> for &'de ExtDuration<US> {
278    #[inline(always)]
279    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
280        <ExtDuration<US> as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
281    }
282}
283
284impl<const US: u16> ExtDuration<US> {
285    /// Create a new instance from raw value.
286    #[inline(always)]
287    pub fn from_u32(val: u32) -> Self {
288        assert!(val < (1 << 24));
289        Self(*unwrap!(val.to_le_bytes().first_chunk()))
290    }
291
292    /// Create an instance from microseconds.
293    #[inline(always)]
294    pub fn from_micros(val: u64) -> Self {
295        Self::from_u32(unwrap!((val / u64::from(US)).try_into()))
296    }
297
298    /// Create an instance from milliseconds.
299    #[inline(always)]
300    pub fn from_millis(val: u32) -> Self {
301        Self::from_micros(u64::from(val) * 1000)
302    }
303
304    /// Create an instance from seconds.
305    #[inline(always)]
306    pub fn from_secs(val: u32) -> Self {
307        Self::from_micros(u64::from(val) * 1_000_000)
308    }
309
310    /// Get value as microseconds.
311    #[inline(always)]
312    pub fn as_micros(&self) -> u64 {
313        u64::from_le_bytes([self.0[0], self.0[1], self.0[2], 0, 0, 0, 0, 0]) * u64::from(US)
314    }
315
316    /// Get value as milliseconds.
317    #[inline(always)]
318    pub fn as_millis(&self) -> u32 {
319        // ((1 << 24 - 1) * u16::MAX / 1_000) < u32::MAX so this is safe
320        (self.as_micros() / 1000) as u32
321    }
322
323    /// Get value as seconds.
324    #[inline(always)]
325    pub fn as_secs(&self) -> u32 {
326        // ((1 << 24 - 1) * u16::MAX / 1_000_000) < u32::MAX so this is safe
327        (self.as_micros() / 1_000_000) as u32
328    }
329}
330
331#[cfg(feature = "embassy-time")]
332impl<const US: u16> From<embassy_time::Duration> for ExtDuration<US> {
333    fn from(duration: embassy_time::Duration) -> Self {
334        Self::from_micros(duration.as_micros())
335    }
336}
337
338impl<const US: u16> From<core::time::Duration> for ExtDuration<US> {
339    fn from(duration: core::time::Duration) -> Self {
340        Self::from_micros(duration.as_micros().try_into().unwrap())
341    }
342}
343
344param!(
345    enum DisconnectReason {
346        AuthenticationFailure = 0x05,
347        RemoteUserTerminatedConn = 0x13,
348        RemoteDeviceTerminatedConnLowResources = 0x14,
349        RemoteDeviceTerminatedConnPowerOff = 0x15,
350        UnsupportedRemoteFeature = 0x1A,
351        PairingWithUnitKeyNotSupported = 0x29,
352        UnacceptableConnParameters = 0x3b,
353    }
354);
355
356param!(
357    enum RemoteConnectionParamsRejectReason {
358        UnacceptableConnParameters = 0x3b,
359    }
360);
361
362param! {
363    #[derive(Default)]
364    enum PowerLevelKind {
365        #[default]
366        Current = 0,
367        Maximum = 1,
368    }
369}
370
371param! {
372    #[derive(Default)]
373    enum ControllerToHostFlowControl {
374        #[default]
375        Off = 0,
376        AclOnSyncOff = 1,
377        AclOffSyncOn = 2,
378        BothOn = 3,
379    }
380}
381
382param!(struct CoreSpecificationVersion(u8));
383
384#[allow(missing_docs)]
385impl CoreSpecificationVersion {
386    pub const VERSION_1_0B: CoreSpecificationVersion = CoreSpecificationVersion(0x00);
387    pub const VERSION_1_1: CoreSpecificationVersion = CoreSpecificationVersion(0x01);
388    pub const VERSION_1_2: CoreSpecificationVersion = CoreSpecificationVersion(0x02);
389    pub const VERSION_2_0_EDR: CoreSpecificationVersion = CoreSpecificationVersion(0x03);
390    pub const VERSION_2_1_EDR: CoreSpecificationVersion = CoreSpecificationVersion(0x04);
391    pub const VERSION_3_0_HS: CoreSpecificationVersion = CoreSpecificationVersion(0x05);
392    pub const VERSION_4_0: CoreSpecificationVersion = CoreSpecificationVersion(0x06);
393    pub const VERSION_4_1: CoreSpecificationVersion = CoreSpecificationVersion(0x07);
394    pub const VERSION_4_2: CoreSpecificationVersion = CoreSpecificationVersion(0x08);
395    pub const VERSION_5_0: CoreSpecificationVersion = CoreSpecificationVersion(0x09);
396    pub const VERSION_5_1: CoreSpecificationVersion = CoreSpecificationVersion(0x0A);
397    pub const VERSION_5_2: CoreSpecificationVersion = CoreSpecificationVersion(0x0B);
398    pub const VERSION_5_3: CoreSpecificationVersion = CoreSpecificationVersion(0x0C);
399    pub const VERSION_5_4: CoreSpecificationVersion = CoreSpecificationVersion(0x0D);
400}
401
402unsafe impl ByteAlignedValue for CoreSpecificationVersion {}
403
404impl<'de> crate::FromHciBytes<'de> for &'de CoreSpecificationVersion {
405    #[inline(always)]
406    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
407        <CoreSpecificationVersion as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
408    }
409}
410
411param! {
412    #[derive(Default)]
413    enum LinkType {
414        #[default]
415        SyncData = 0,
416        AclData = 1,
417        IsoData = 2,
418    }
419}
420
421param_slice! {
422    [ConnHandleCompletedPackets; 4] {
423        handle[0]: ConnHandle,
424        num_completed_packets[2]: u16,
425    }
426}
427
428impl ConnHandleCompletedPackets {
429    /// Create a new instance.
430    pub fn new(handle: ConnHandle, num_completed_packets: u16) -> Self {
431        let mut dest = [0; 4];
432        handle.write_hci(&mut dest[0..2]).unwrap();
433        num_completed_packets.write_hci(&mut dest[2..4]).unwrap();
434        Self(dest)
435    }
436}
437
438#[cfg(test)]
439mod tests {
440    #[cfg(feature = "serde")]
441    use postcard;
442
443    use super::*;
444
445    #[test]
446    fn test_encode_decode_conn_handle_completed_packets() {
447        let completed = ConnHandleCompletedPackets::new(ConnHandle::new(42), 2334);
448
449        assert_eq!(completed.handle().unwrap(), ConnHandle::new(42));
450        assert_eq!(completed.num_completed_packets().unwrap(), 2334);
451    }
452
453    #[cfg(feature = "serde")]
454    #[test]
455    fn test_serialize_bdaddr() {
456        let bytes = [0x01, 0xaa, 0x55, 0x04, 0x05, 0xfe];
457
458        let address = BdAddr::new(bytes);
459
460        let mut buffer = [0u8; 32];
461        let vykort = postcard::to_slice(&address, &mut buffer).unwrap();
462
463        assert_eq!(vykort, &bytes);
464    }
465
466    #[cfg(feature = "serde")]
467    #[test]
468    fn test_deserialize_bdaddr() {
469        let bytes = [0xff, 0x5a, 0xa5, 0x00, 0x05, 0xfe];
470
471        let address = postcard::from_bytes::<BdAddr>(&bytes).unwrap();
472
473        let expected = BdAddr::new(bytes);
474
475        assert_eq!(address, expected);
476    }
477}