Skip to main content

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