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