bt_hci/
cmd.rs

1//! HCI commands [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-ee8bbec6-ebdd-b47d-41d5-a7e655cad979)
2
3use core::future::Future;
4
5use embedded_io::ErrorType;
6
7use crate::controller::{ControllerCmdAsync, ControllerCmdSync};
8use crate::{param, FixedSizeValue, FromHciBytes, HostToControllerPacket, PacketKind, WriteHci};
9
10pub mod controller_baseband;
11pub mod info;
12pub mod le;
13pub mod link_control;
14pub mod status;
15
16/// The 6-bit Opcode Group Field (OGF)
17///
18/// See Bluetooth Core Specification Vol 4, Part E, ยง5.4.1
19#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub struct OpcodeGroup(u8);
22
23impl OpcodeGroup {
24    /// Link Control commands [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-fe2a33d3-28f4-9fd1-4d08-62286985c05e)
25    pub const LINK_CONTROL: OpcodeGroup = OpcodeGroup(1);
26    /// Link Policy commands [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-a593fa1a-89f3-8042-5ebe-6da6174e2cf9)
27    pub const LINK_POLICY: OpcodeGroup = OpcodeGroup(2);
28    /// Controller & Baseband commands [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-5ced811b-a6ce-701a-16b2-70f2d9795c05)
29    pub const CONTROL_BASEBAND: OpcodeGroup = OpcodeGroup(3);
30    /// Informational parameters [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-42372304-c9ef-dcab-6905-4e5b64703d45)
31    pub const INFO_PARAMS: OpcodeGroup = OpcodeGroup(4);
32    /// Status parameters [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-40e8a930-65b3-c409-007e-388fd48e1041)
33    pub const STATUS_PARAMS: OpcodeGroup = OpcodeGroup(5);
34    /// Testing commands [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-ec2ddbf2-ae4c-ec45-7a06-94f8b3327220)
35    pub const TESTING: OpcodeGroup = OpcodeGroup(6);
36    /// LE Controller commands [๐Ÿ“–](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-0f07d2b9-81e3-6508-ee08-8c808e468fed)
37    pub const LE: OpcodeGroup = OpcodeGroup(8);
38    /// Vendor Specific Debug commands
39    pub const VENDOR_SPECIFIC: OpcodeGroup = OpcodeGroup(0x3f);
40
41    /// Create a new `OpcodeGroup` with the given value
42    pub const fn new(val: u8) -> Self {
43        Self(val)
44    }
45}
46
47param!(
48    /// The 2 byte Opcode uniquely identifying the type of a command
49    ///
50    /// See Bluetooth Core Specification Vol 4, Part E, ยง5.4.1
51    struct Opcode(u16)
52);
53
54impl Opcode {
55    /// Special opcode for command events with no associated command
56    pub const UNSOLICITED: Opcode = Opcode::new(OpcodeGroup(0), 0);
57
58    /// Create an `Opcode` with the given OGF and OCF values
59    pub const fn new(ogf: OpcodeGroup, ocf: u16) -> Self {
60        Self(((ogf.0 as u16) << 10) | ocf)
61    }
62
63    /// Get the OGF value of this Opcode
64    pub const fn group(self) -> OpcodeGroup {
65        OpcodeGroup((self.0 >> 10) as u8)
66    }
67
68    /// Get the OCF value of this Opcode
69    pub const fn cmd(self) -> u16 {
70        self.0 & 0x03ff
71    }
72
73    /// Get the raw 16-bit value for this Opcode
74    pub const fn to_raw(self) -> u16 {
75        self.0
76    }
77}
78
79/// An error type for HCI commands
80#[derive(Debug)]
81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82pub enum Error<E> {
83    /// HCI error.
84    Hci(param::Error),
85    /// I/O error.
86    Io(E),
87}
88
89impl<E> From<param::Error> for Error<E> {
90    fn from(e: param::Error) -> Self {
91        Self::Hci(e)
92    }
93}
94
95/// An object representing an HCI Command
96pub trait Cmd: WriteHci {
97    /// The opcode identifying this kind of HCI Command
98    const OPCODE: Opcode;
99
100    /// Parameters type for this command.
101    type Params: WriteHci;
102
103    /// Parameters expected for this command.
104    fn params(&self) -> &Self::Params;
105
106    /// The command packet header for this command
107    fn header(&self) -> [u8; 3] {
108        let opcode_bytes = Self::OPCODE.0.to_le_bytes();
109        [opcode_bytes[0], opcode_bytes[1], self.params().size() as u8]
110    }
111}
112
113impl<T: Cmd> HostToControllerPacket for T {
114    const KIND: PacketKind = PacketKind::Cmd;
115}
116
117/// A marker trait for objects representing HCI Commands that generate [`CommandStatus`](crate::event::CommandStatus)
118/// events
119pub trait AsyncCmd: Cmd {
120    /// Run the command on the provided controller.
121    fn exec<C: ControllerCmdAsync<Self>>(
122        &self,
123        controller: &C,
124    ) -> impl Future<Output = Result<(), Error<<C as ErrorType>::Error>>> {
125        controller.exec(self)
126    }
127}
128
129/// Type representing the buffer for a command response.
130pub trait CmdReturnBuf: Copy + AsRef<[u8]> + AsMut<[u8]> {
131    /// Length of buffer.
132    const LEN: usize;
133
134    /// Create a new instance of the buffer.
135    fn new() -> Self;
136}
137
138impl<const N: usize> CmdReturnBuf for [u8; N] {
139    const LEN: usize = N;
140
141    #[inline(always)]
142    fn new() -> Self {
143        [0; N]
144    }
145}
146
147/// A trait for objects representing HCI Commands that generate [`CommandComplete`](crate::event::CommandComplete)
148/// events
149pub trait SyncCmd: Cmd {
150    /// The type of the parameters for the [`CommandComplete`](crate::event::CommandComplete) event
151    type Return: for<'a> FromHciBytes<'a> + Copy;
152    /// Handle returned by this command.
153    type Handle: FixedSizeValue;
154    /// Return buffer used by this command.
155    type ReturnBuf: CmdReturnBuf;
156
157    /// Handle parameter for this command.
158    fn param_handle(&self) -> Self::Handle;
159
160    /// Extracts the [`Self::Handle`] from the return parameters for commands that return a handle.
161    ///
162    /// If the command takes a handle or BdAddr and returns it as the first parameter of the associated
163    /// [`CommandComplete`](crate::event::CommandComplete) event, this method will extract that handle from the return
164    /// parameters. This is needed to identify which command the `CommandComplete` event was for in the event that the
165    /// status of the command was an error.
166    ///
167    /// See Bluetooth Core Specification Vol 4, Part E, ยง4.5
168    fn return_handle(_data: &[u8]) -> Result<Self::Handle, crate::FromHciBytesError>;
169
170    /// Run the command on the provided controller.
171    fn exec<C: ControllerCmdSync<Self>>(
172        &self,
173        controller: &C,
174    ) -> impl Future<Output = Result<Self::Return, Error<<C as ErrorType>::Error>>> {
175        controller.exec(self)
176    }
177}
178
179#[doc(hidden)]
180#[macro_export]
181macro_rules! cmd {
182    (
183        $(#[$attrs:meta])*
184        $name:ident($group:ident, $cmd:expr) {
185            $params:ident$(<$life:lifetime>)? {
186                $($param_name:ident: $param_ty:ty,)+
187            }
188            $ret:ident {
189                $($ret_name:ident: $ret_ty:ty,)+
190            }
191            $(Handle = $handle_name:ident: $handle:ty;)?
192        }
193    ) => {
194        $crate::cmd! {
195            $(#[$attrs])*
196            $name($group, $cmd) {
197                $params$(<$life>)? {
198                    $($param_name: $param_ty,)+
199                }
200                Return = $ret;
201                $(Handle = $handle_name: $handle;)?
202            }
203        }
204
205        $crate::param! {
206            #[doc = "Return parameters for"]
207            $(#[$attrs])*
208            struct $ret {
209                $($handle_name: $handle,)?
210                $($ret_name: $ret_ty,)*
211            }
212        }
213    };
214    (
215        $(#[$attrs:meta])*
216        $name:ident($group:ident, $cmd:expr) {
217            Params = ();
218            $ret:ident {
219                $($ret_name:ident: $ret_ty:ty,)+
220            }
221        }
222    ) => {
223        $crate::cmd! {
224            $(#[$attrs])*
225            $name($group, $cmd) {
226                Params = ();
227                Return = $ret;
228            }
229        }
230
231        $crate::param! {
232            #[doc = "Return parameters for"]
233            $(#[$attrs])*
234            struct $ret {
235                $($ret_name: $ret_ty,)*
236            }
237        }
238    };
239    (
240        $(#[$attrs:meta])*
241        $name:ident($group:ident, $cmd:expr) {
242            Params$(<$life:lifetime>)? = $param:ty;
243            $ret:ident {
244                $($ret_name:ident: $ret_ty:ty,)+
245            }
246            $(Handle = $handle_name:ident: $handle:ty;)?
247        }
248    ) => {
249        $crate::cmd! {
250            $(#[$attrs])*
251            $name($group, $cmd) {
252                Params$(<$life>)? = $param;
253                Return = $ret;
254                $(Handle = $handle;)?
255            }
256        }
257
258        $crate::param! {
259            #[doc = "Return parameters for"]
260            $(#[$attrs])*
261            struct $ret {
262                $($handle_name: $handle,)?
263                $($ret_name: $ret_ty,)*
264            }
265        }
266    };
267    (
268        $(#[$attrs:meta])*
269        $name:ident($group:ident, $cmd:expr) {
270            $params:ident$(<$life:lifetime>)? {
271                $($param_name:ident: $param_ty:ty,)+
272            }
273            $(
274                Return = $ret:ty;
275                $(Handle = $handle_name:ident: $handle:ty;)?
276            )?
277        }
278    ) => {
279        $crate::cmd! {
280            BASE
281            $(#[$attrs])*
282            $name($group, $cmd) {
283                Params$(<$life>)? = $params$(<$life>)?;
284                $(
285                    Return = $ret;
286                    $(Handle = $handle;)?
287                )?
288            }
289        }
290
291        impl$(<$life>)? $name$(<$life>)? {
292            #[allow(clippy::too_many_arguments)]
293            /// Create a new instance of a command.
294            pub fn new($($($handle_name: $handle,)?)? $($param_name: $param_ty),+) -> Self {
295                Self($params {
296                    $($($handle_name,)?)?
297                    $($param_name,)*
298                })
299            }
300
301            $(
302                $(
303                    fn handle(&self) -> $handle {
304                        self.0.$handle_name
305                    }
306                )?
307            )?
308        }
309
310        $crate::param! {
311            #[doc = "Parameters for"]
312            $(#[$attrs])*
313            struct $params$(<$life>)? {
314                $($($handle_name: $handle,)?)?
315                $($param_name: $param_ty,)*
316            }
317        }
318    };
319    (
320        $(#[$attrs:meta])*
321        $name:ident($group:ident, $cmd:expr) {
322            Params = ();
323            $(Return = $ret:ty;)?
324        }
325    ) => {
326        $crate::cmd! {
327            BASE
328            $(#[$attrs])*
329            $name($group, $cmd) {
330                Params = ();
331                $(Return = $ret;)?
332            }
333        }
334
335        impl $name {
336            /// Create a new instance of this command.
337            pub fn new() -> Self {
338                Self(())
339            }
340        }
341
342        impl Default for $name {
343            fn default() -> Self {
344                Self(())
345            }
346        }
347    };
348    (
349        $(#[$attrs:meta])*
350        $name:ident($group:ident, $cmd:expr) {
351            Params$(<$life:lifetime>)? = $params:ty;
352            $(
353                Return = $ret:ty;
354                $(Handle = $handle:ty;)?
355            )?
356        }
357    ) => {
358        $crate::cmd! {
359            BASE
360            $(#[$attrs])*
361            $name($group, $cmd) {
362                Params$(<$life>)? = $params;
363                $(
364                    Return = $ret;
365                    $(Handle = $handle;)?
366                )?
367            }
368        }
369
370        impl$(<$life>)? $name$(<$life>)? {
371            /// Create a new instance of the command with the provided parameters.
372            pub fn new(param: $params) -> Self {
373                Self(param)
374            }
375
376            $(
377                $(
378                    fn handle(&self) -> $handle {
379                        self.0
380                    }
381                )?
382            )?
383        }
384    };
385    (
386        BASE
387        $(#[$attrs:meta])*
388        $name:ident($group:ident, $cmd:expr) {
389            Params$(<$life:lifetime>)? = $params:ty;
390            $(
391                Return = $ret:ty;
392                $(Handle = $handle:ty;)?
393            )?
394        }
395    ) => {
396        $(#[$attrs])*
397        #[repr(transparent)]
398        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
399        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
400        pub struct $name$(<$life>)?($params);
401
402        #[automatically_derived]
403        #[allow(unused_mut, unused_variables, unused_imports)]
404        impl$(<$life>)? $crate::cmd::Cmd for $name$(<$life>)? {
405            const OPCODE: $crate::cmd::Opcode = $crate::cmd::Opcode::new($crate::cmd::OpcodeGroup::$group, $cmd);
406            type Params = $params;
407
408            fn params(&self) -> &$params {
409                &self.0
410            }
411        }
412
413        #[automatically_derived]
414        impl$(<$life>)? From<$params> for $name$(<$life>)? {
415            fn from(params: $params) -> Self {
416                Self(params)
417            }
418        }
419
420        impl$(<$life>)? $crate::WriteHci for $name$(<$life>)? {
421            #[inline(always)]
422            fn size(&self) -> usize {
423                <$params as $crate::WriteHci>::size(&self.0) + 3
424            }
425
426            fn write_hci<W: embedded_io::Write>(&self, mut writer: W) -> Result<(), W::Error> {
427                writer.write_all(&<Self as $crate::cmd::Cmd>::header(self))?;
428                <$params as $crate::WriteHci>::write_hci(&self.0, writer)
429            }
430
431            async fn write_hci_async<W: embedded_io_async::Write>(&self, mut writer: W) -> Result<(), W::Error> {
432                writer.write_all(&<Self as $crate::cmd::Cmd>::header(self)).await?;
433                <$params as $crate::WriteHci>::write_hci_async(&self.0, writer).await
434            }
435        }
436
437        $crate::cmd! {
438            RETURN
439            $name$(<$life>)? {
440                $(
441                    Return = $ret;
442                    $(Handle = $handle;)?
443                )?
444            }
445        }
446    };
447    (
448        RETURN
449        $name:ident$(<$life:lifetime>)? {
450            Return = $ret:ty;
451            Handle = $handle:ty;
452        }
453    ) => {
454        impl$(<$life>)? $crate::cmd::SyncCmd for $name$(<$life>)? {
455            type Return = $ret;
456            type Handle = $handle;
457            type ReturnBuf = [u8; <$ret as $crate::ReadHci>::MAX_LEN];
458
459            fn param_handle(&self) -> Self::Handle {
460                self.handle()
461            }
462
463            fn return_handle(data: &[u8]) -> Result<Self::Handle, $crate::FromHciBytesError> {
464                <$handle as $crate::FromHciBytes>::from_hci_bytes(data).map(|(x, _)| x)
465            }
466        }
467    };
468    (
469        RETURN
470        $name:ident$(<$life:lifetime>)? {
471            Return = $ret:ty;
472        }
473    ) => {
474        impl$(<$life>)? $crate::cmd::SyncCmd for $name$(<$life>)? {
475            type Return = $ret;
476            type Handle = ();
477            type ReturnBuf = [u8; <$ret as $crate::ReadHci>::MAX_LEN];
478
479            fn param_handle(&self) {}
480
481            fn return_handle(_data: &[u8]) -> Result<Self::Handle, $crate::FromHciBytesError> {
482                Ok(())
483            }
484        }
485    };
486    (
487        RETURN
488        $name:ident$(<$life:lifetime>)? {
489        }
490    ) => {
491        impl$(<$life>)? $crate::cmd::AsyncCmd for $name$(<$life>)? {}
492    };
493}
494
495pub use cmd;