1use 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#[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 pub fn new(val: [u8; 6]) -> Self {
76 Self(val)
77 }
78
79 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 pub fn new(val: u16) -> Self {
99 assert!(val <= 0xeff);
100 Self(val)
101 }
102
103 pub fn raw(&self) -> u16 {
105 self.0
106 }
107}
108
109#[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 pub const fn from_u8(val: u8) -> Self {
126 Self(val)
127 }
128
129 #[inline(always)]
131 pub fn from_micros(val: u64) -> Self {
132 Self::from_u8(unwrap!((val / u64::from(US)).try_into()))
133 }
134
135 #[inline(always)]
137 pub fn from_millis(val: u32) -> Self {
138 Self::from_micros(u64::from(val) * 1000)
139 }
140
141 #[inline(always)]
143 pub fn from_secs(val: u32) -> Self {
144 Self::from_micros(u64::from(val) * 1_000_000)
145 }
146
147 #[inline(always)]
149 pub fn as_u8(&self) -> u8 {
150 self.0
151 }
152
153 #[inline(always)]
155 pub fn as_micros(&self) -> u64 {
156 u64::from(self.as_u8()) * u64::from(US)
157 }
158
159 #[inline(always)]
161 pub fn as_millis(&self) -> u32 {
162 (self.as_micros() / 1000) as u32
163 }
164
165 #[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#[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 pub const fn from_u16(val: u16) -> Self {
202 Self(val)
203 }
204
205 #[inline(always)]
207 pub fn from_micros(val: u64) -> Self {
208 Self::from_u16(unwrap!((val / u64::from(US)).try_into()))
209 }
210
211 #[inline(always)]
213 pub fn from_millis(val: u32) -> Self {
214 Self::from_micros(u64::from(val) * 1000)
215 }
216
217 #[inline(always)]
219 pub fn from_secs(val: u32) -> Self {
220 Self::from_micros(u64::from(val) * 1_000_000)
221 }
222
223 #[inline(always)]
225 pub fn as_u16(&self) -> u16 {
226 self.0
227 }
228
229 #[inline(always)]
231 pub fn as_micros(&self) -> u64 {
232 u64::from(self.as_u16()) * u64::from(US)
233 }
234
235 #[inline(always)]
237 pub fn as_millis(&self) -> u32 {
238 unwrap!((self.as_micros() / 1000).try_into())
239 }
240
241 #[inline(always)]
243 pub fn as_secs(&self) -> u32 {
244 (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#[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 #[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 #[inline(always)]
294 pub fn from_micros(val: u64) -> Self {
295 Self::from_u32(unwrap!((val / u64::from(US)).try_into()))
296 }
297
298 #[inline(always)]
300 pub fn from_millis(val: u32) -> Self {
301 Self::from_micros(u64::from(val) * 1000)
302 }
303
304 #[inline(always)]
306 pub fn from_secs(val: u32) -> Self {
307 Self::from_micros(u64::from(val) * 1_000_000)
308 }
309
310 #[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 #[inline(always)]
318 pub fn as_millis(&self) -> u32 {
319 (self.as_micros() / 1000) as u32
321 }
322
323 #[inline(always)]
325 pub fn as_secs(&self) -> u32 {
326 (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 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}