Skip to main content

bt_hci/param/
le.rs

1use core::iter::FusedIterator;
2
3use super::{param, param_slice, BdAddr, ConnHandle, Duration, RemainingBytes};
4use crate::{ByteAlignedValue, FixedSizeValue, FromHciBytes, FromHciBytesError, WriteHci};
5
6param!(struct AddrKind(u8));
7
8#[allow(missing_docs)]
9impl AddrKind {
10    pub const PUBLIC: AddrKind = AddrKind(0);
11    pub const RANDOM: AddrKind = AddrKind(1);
12    pub const RESOLVABLE_PRIVATE_OR_PUBLIC: AddrKind = AddrKind(2);
13    pub const RESOLVABLE_PRIVATE_OR_RANDOM: AddrKind = AddrKind(3);
14    pub const ANONYMOUS_ADV: AddrKind = AddrKind(0xff);
15
16    /// Create a new instance.
17    pub const fn new(v: u8) -> Self {
18        Self(v)
19    }
20
21    /// Get the inner representation.
22    pub fn as_raw(&self) -> u8 {
23        self.0
24    }
25}
26
27unsafe impl ByteAlignedValue for AddrKind {}
28
29impl<'de> crate::FromHciBytes<'de> for &'de AddrKind {
30    #[inline(always)]
31    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
32        <AddrKind as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
33    }
34}
35
36param! {
37    bitfield AdvChannelMap[1] {
38        (0, is_channel_37_enabled, enable_channel_37);
39        (1, is_channel_38_enabled, enable_channel_38);
40        (2, is_channel_39_enabled, enable_channel_39);
41    }
42}
43
44#[allow(missing_docs)]
45impl AdvChannelMap {
46    pub const ALL: AdvChannelMap = AdvChannelMap(0x07);
47    pub const CHANNEL_37: AdvChannelMap = AdvChannelMap(0x01);
48    pub const CHANNEL_38: AdvChannelMap = AdvChannelMap(0x02);
49    pub const CHANNEL_39: AdvChannelMap = AdvChannelMap(0x04);
50}
51
52param!(struct ChannelMap([u8; 5]));
53
54impl ChannelMap {
55    /// Create a new instance.
56    pub fn new() -> Self {
57        Self([0xff, 0xff, 0xff, 0xff, 0x1f])
58    }
59
60    /// Check if channel is marked as bad.
61    pub fn is_channel_bad(&self, channel: u8) -> bool {
62        let byte = usize::from(channel / 8);
63        let bit = channel % 8;
64        (self.0[byte] & (1 << bit)) == 0
65    }
66
67    /// Set channel to be marked as bad.
68    pub fn set_channel_bad(&mut self, channel: u8, bad: bool) {
69        let byte = usize::from(channel / 8);
70        let bit = channel % 8;
71        self.0[byte] = (self.0[byte] & !(1 << bit)) | (u8::from(!bad) << bit);
72    }
73}
74
75unsafe impl ByteAlignedValue for ChannelMap {}
76
77impl<'de> crate::FromHciBytes<'de> for &'de ChannelMap {
78    #[inline(always)]
79    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
80        <ChannelMap as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
81    }
82}
83
84param! {
85    #[derive(Default)]
86    enum AdvKind {
87        #[default]
88        AdvInd = 0,
89        AdvDirectIndHigh = 1,
90        AdvScanInd = 2,
91        AdvNonconnInd = 3,
92        AdvDirectIndLow = 4,
93    }
94}
95
96param! {
97    #[derive(Default)]
98    enum AdvFilterPolicy {
99        #[default]
100        Unfiltered = 0,
101        FilterScan = 1,
102        FilterConn = 2,
103        FilterConnAndScan = 3,
104    }
105}
106
107param! {
108    #[derive(Default)]
109    enum LeScanKind {
110        #[default]
111        Passive = 0,
112        Active = 1,
113    }
114}
115
116param! {
117    #[derive(Default)]
118    enum ScanningFilterPolicy {
119        #[default]
120        BasicUnfiltered = 0,
121        BasicFiltered = 1,
122        ExtUnfiltered = 2,
123        ExtFiltered = 3,
124    }
125}
126
127param! {
128    #[derive(Default)]
129    enum PhyKind {
130        #[default]
131        Le1M = 1,
132        Le2M = 2,
133        LeCoded = 3,
134        LeCodedS2 = 4,
135    }
136}
137
138param! {
139    bitfield SpacingTypes[2] {
140        (0, has_t_ifs_acl_cp, set_t_ifs_acl_cp);
141        (1, has_t_ifs_acl_pc, set_t_ifs_acl_pc);
142        (2, has_t_mces, set_t_mces);
143        (3, has_t_ifs_cis, set_t_ifs_cis);
144        (4, has_t_mss_cis, set_t_mss_cis);
145    }
146}
147
148param! {
149    enum FrameSpaceInitiator {
150        LocalHostInitiated = 0x00,
151        LocalControllerInitiated = 0x01,
152        PeerInitiated = 0x02,
153    }
154}
155
156param! {
157    bitfield AllPhys[1] {
158        (0, has_no_tx_phy_preference, set_has_no_tx_phy_preference);
159        (1, has_no_rx_phy_preference, set_has_no_rx_phy_preference);
160    }
161}
162
163param! {
164    bitfield PhyMask[1] {
165        (0, has_le_1m_phy, set_le_1m_phy);
166        (1, has_le_2m_phy, set_le_2m_phy);
167        (2, has_le_coded_phy, set_le_coded_phy);
168    }
169}
170
171/// Preferences when one can choose the phy.
172#[derive(Default)]
173#[repr(u16, align(1))]
174#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
175#[cfg_attr(feature = "defmt", derive(defmt::Format))]
176#[allow(missing_docs)]
177pub enum PhyOptions {
178    #[default]
179    NoPreferredCoding = 0,
180    S2CodingPreferred = 1,
181    S8CodingPreferred = 2,
182}
183
184unsafe impl FixedSizeValue for PhyOptions {
185    #[inline(always)]
186    fn is_valid(data: &[u8]) -> bool {
187        data[0] == 0 || data[0] == 1 || data[0] == 2
188    }
189}
190
191unsafe impl ByteAlignedValue for PhyOptions {}
192
193impl<'de> FromHciBytes<'de> for &'de PhyOptions {
194    #[inline(always)]
195    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), FromHciBytesError> {
196        <PhyOptions as ByteAlignedValue>::ref_from_hci_bytes(data)
197    }
198}
199
200/// PHY preference or requirement during extended advertisement (BLE5.4)
201#[derive(Default)]
202#[repr(u8)]
203#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
204#[cfg_attr(feature = "defmt", derive(defmt::Format))]
205#[allow(missing_docs)]
206pub enum AdvPhyOptions {
207    #[default]
208    NoPreferredCoding = 0,
209    S2CodingPreferred = 1,
210    S8CodingPreferred = 2,
211    S2CodingRequired = 3,
212    S8CodingRequired = 4,
213}
214
215unsafe impl FixedSizeValue for AdvPhyOptions {
216    #[inline(always)]
217    fn is_valid(data: &[u8]) -> bool {
218        data[0] == 0 || data[0] == 1 || data[0] == 2 || data[0] == 3 || data[0] == 4
219    }
220}
221
222unsafe impl ByteAlignedValue for AdvPhyOptions {}
223
224impl<'de> FromHciBytes<'de> for &'de AdvPhyOptions {
225    #[inline(always)]
226    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), FromHciBytesError> {
227        <AdvPhyOptions as ByteAlignedValue>::ref_from_hci_bytes(data)
228    }
229}
230
231param! {
232    struct ScanningPhy {
233        active_scan: bool,
234        scan_interval: Duration<625>,
235        scan_window: Duration<625>,
236    }
237}
238
239param! {
240    struct InitiatingPhy {
241        scan_interval: Duration<625>,
242        scan_window: Duration<625>,
243        conn_interval_min: Duration<1_250>,
244        conn_interval_max: Duration<1_250>,
245        max_latency: u16,
246        supervision_timeout: Duration<10_000>,
247        min_ce_len: Duration<625>,
248        max_ce_len: Duration<625>,
249    }
250}
251
252param! {
253    struct ConnIntervalGroup {
254        min: Duration<125>,
255        max: Duration<125>,
256        stride: Duration<125>,
257    }
258}
259
260/// Parameters for different phy representations.
261#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
262#[cfg_attr(feature = "defmt", derive(defmt::Format))]
263pub struct PhyParams<T> {
264    /// 1M phy parameters.
265    pub le_1m_phy: Option<T>,
266    /// 2M phy parameters.
267    pub le_2m_phy: Option<T>,
268    /// Coded phy parameters.
269    pub le_coded_phy: Option<T>,
270}
271
272impl<T> PhyParams<T> {
273    /// Get the mask associated with the parameters.
274    pub fn scanning_phys(&self) -> PhyMask {
275        PhyMask::new()
276            .set_le_1m_phy(self.le_1m_phy.is_some())
277            .set_le_2m_phy(self.le_2m_phy.is_some())
278            .set_le_coded_phy(self.le_coded_phy.is_some())
279    }
280}
281
282impl<T: WriteHci> WriteHci for PhyParams<T> {
283    #[inline(always)]
284    fn size(&self) -> usize {
285        1 + self.le_1m_phy.size() + self.le_2m_phy.size() + self.le_coded_phy.size()
286    }
287
288    #[inline(always)]
289    fn write_hci<W: embedded_io::Write>(&self, mut writer: W) -> Result<(), W::Error> {
290        self.scanning_phys().write_hci(&mut writer)?;
291        self.le_1m_phy.write_hci(&mut writer)?;
292        self.le_2m_phy.write_hci(&mut writer)?;
293        self.le_coded_phy.write_hci(&mut writer)?;
294        Ok(())
295    }
296
297    #[inline(always)]
298    async fn write_hci_async<W: ::embedded_io_async::Write>(&self, mut writer: W) -> Result<(), W::Error> {
299        self.scanning_phys().write_hci_async(&mut writer).await?;
300        self.le_1m_phy.write_hci_async(&mut writer).await?;
301        self.le_2m_phy.write_hci_async(&mut writer).await?;
302        self.le_coded_phy.write_hci_async(&mut writer).await?;
303        Ok(())
304    }
305}
306
307param!(struct AdvHandle(u8));
308
309impl AdvHandle {
310    /// Create a new instance.
311    pub const fn new(v: u8) -> Self {
312        Self(v)
313    }
314
315    /// Get the inner representation.
316    pub fn as_raw(&self) -> u8 {
317        self.0
318    }
319}
320
321unsafe impl ByteAlignedValue for AdvHandle {}
322
323impl<'de> crate::FromHciBytes<'de> for &'de AdvHandle {
324    #[inline(always)]
325    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
326        <AdvHandle as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
327    }
328}
329
330param! {
331    bitfield AdvEventProps[2] {
332        (0, connectable_adv, set_connectable_adv);
333        (1, scannable_adv, set_scannable_adv);
334        (2, directed_adv, set_directed_adv);
335        (3, high_duty_cycle_directed_connectable_adv, set_high_duty_cycle_directed_connectable_adv);
336        (4, legacy_adv, set_legacy_adv);
337        (5, anonymous_adv, set_anonymous_adv);
338        (6, include_tx_power, set_include_tx_power);
339    }
340}
341
342param! {
343    #[derive(Default)]
344    enum Operation {
345        #[default]
346        IntermediateFragment = 0,
347        FirstFragment = 1,
348        LastFragment = 2,
349        Complete = 3,
350        Unchanged = 4,
351    }
352}
353
354param! {
355    struct AdvSet {
356        adv_handle: AdvHandle,
357        duration: Duration<10_000>,
358        max_ext_adv_events: u8,
359    }
360}
361
362param_slice!(&'a [AdvSet]);
363
364param! {
365    bitfield PeriodicAdvProps[2] {
366        (6, is_tx_power_included, include_tx_power);
367    }
368}
369
370param! {
371    #[derive(Default)]
372    enum FilterDuplicates {
373        #[default]
374        Disabled = 0,
375        Enabled = 1,
376        EnabledPerScanPeriod = 2,
377    }
378}
379
380param! {
381    bitfield LePeriodicAdvCreateSyncOptions[1] {
382        (0, is_using_periodic_adv_list, use_periodic_adv_list);
383        (1, is_reporting_initially_disabled, disable_initial_reporting);
384        (2, is_duplicate_filtering_enabled, enable_duplicate_filtering);
385    }
386}
387
388param! {
389    bitfield CteMask[1] {
390        (0, is_aoa_cte, set_aoa_cte);
391        (1, is_aod_1us_cte, set_aod_1us_cte);
392        (2, is_aod_2us_cte, set_aod_2us_cte);
393        (3, is_type_3_cte, set_type_3_cte);
394        (4, is_non_cte, set_non_cte);
395    }
396}
397
398param!(struct SyncHandle(u16));
399
400param!(struct BigHandle(u8));
401
402param! {
403    #[derive(Default)]
404    enum PrivacyMode {
405        #[default]
406        Network = 0,
407        Device = 1,
408    }
409}
410
411param! {
412    #[derive(Default)]
413    enum CteKind {
414        #[default]
415        AoA = 0,
416        AoD1Us = 1,
417        AoD2Us = 2,
418        NoCte = 0xff,
419    }
420}
421
422param! {
423    bitfield SwitchingSamplingRates[1] {
424        (0, is_1us_aod_tx, set_1us_aod_tx);
425        (1, is_1us_aod_rx, set_1us_aod_rx);
426        (2, is_1us_aoa_rx, set_1us_aoa_rx);
427    }
428}
429
430param! {
431    bitfield LePeriodicAdvReceiveEnable[1] {
432        (0, is_reporting, set_reporting);
433        (1, is_duplicate_filtering, set_duplicate_filtering);
434    }
435}
436
437param! {
438    #[derive(Default)]
439    enum LePeriodicAdvSyncTransferMode {
440        #[default]
441        NoSync = 0,
442        SyncRx = 1,
443        SyncRxReport = 2,
444        SyncRxReportFilterDuplicates = 3,
445    }
446}
447
448param!(struct LeHostFeature(u8));
449
450#[allow(missing_docs)]
451impl LeHostFeature {
452    pub const CONN_ISO_STREAM: LeHostFeature = LeHostFeature(32);
453    pub const CONN_SUBRATING: LeHostFeature = LeHostFeature(38);
454    pub const ADV_CODING_SELECTION: LeHostFeature = LeHostFeature(41);
455    pub const CHANNEL_SOUNDING: LeHostFeature = LeHostFeature(47);
456}
457
458unsafe impl ByteAlignedValue for LeHostFeature {}
459
460impl<'de> FromHciBytes<'de> for &'de LeHostFeature {
461    #[inline(always)]
462    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), FromHciBytesError> {
463        <LeHostFeature as ByteAlignedValue>::ref_from_hci_bytes(data)
464    }
465}
466
467param!(struct LeHostFeatureV2(u16));
468
469#[allow(missing_docs)]
470impl LeHostFeatureV2 {
471    pub const CONN_ISO_STREAM: LeHostFeatureV2 = LeHostFeatureV2(32);
472    pub const CONN_SUBRATING: LeHostFeatureV2 = LeHostFeatureV2(38);
473    pub const ADV_CODING_SELECTION: LeHostFeatureV2 = LeHostFeatureV2(41);
474    pub const CHANNEL_SOUNDING: LeHostFeatureV2 = LeHostFeatureV2(47);
475    pub const SHORTER_CONNECTION_INTERVALS: LeHostFeatureV2 = LeHostFeatureV2(73);
476}
477
478impl From<LeHostFeature> for LeHostFeatureV2 {
479    fn from(value: LeHostFeature) -> Self {
480        LeHostFeatureV2(value.0.into())
481    }
482}
483
484param! {
485    bitfield LeDataRelatedAddrChangeReasons[1] {
486        (0, change_on_adv_data_change, set_change_addr_on_adv_data_changes);
487        (1, change_on_scan_response_data_change, set_change_addr_on_scan_response_data_changes);
488    }
489}
490
491param! {
492    #[derive(Default)]
493    enum LeConnRole {
494        #[default]
495        Central = 0,
496        Peripheral = 1,
497    }
498}
499
500param! {
501    #[derive(Default)]
502    enum ClockAccuracy {
503        #[default]
504        Ppm500 = 0,
505        Ppm250 = 1,
506        Ppm150 = 2,
507        Ppm100 = 3,
508        Ppm75 = 4,
509        Ppm50 = 5,
510        Ppm30 = 6,
511        Ppm20 = 7,
512    }
513}
514
515param! {
516    struct LeAdvertisingReportParam<'a> {
517        event_type: u8,
518        addr_kind: AddrKind,
519        addr: BdAddr,
520        data: &'a [u8],
521        rssi: i8,
522    }
523}
524
525param_slice! {
526    [LeDirectedAdvertisingReportParam; 16] {
527        event_type[0]: u8,
528        addr_kind[1]: AddrKind,
529        addr[2]: BdAddr,
530        direct_addr_kind[8]: AddrKind,
531        direct_addr[9]: BdAddr,
532        rssi[15]: i8,
533    }
534}
535
536param_slice! {
537    [LeIQSample; 2] {
538        i_sample[0]: i8,
539        q_sample[1]: i8,
540    }
541}
542
543param_slice! {
544    [BisConnHandle; 2] {
545        handle[0]: ConnHandle,
546    }
547}
548
549param! {
550    #[derive(Default)]
551    enum DataStatus {
552        #[default]
553        Complete = 0,
554        Incomplete = 1,
555        Failed = 0xff,
556    }
557}
558
559param! {
560    #[derive(Default)]
561    enum PacketStatus {
562        #[default]
563        CrcCorrect = 0,
564        CrcIncorrectUsedLength = 1,
565        CrcIncorrectUsedOther = 2,
566        InsufficientResources = 0xff,
567    }
568}
569
570param! {
571    #[derive(Default)]
572    enum TxStatus {
573        #[default]
574        Transmitted = 0,
575        NotTransmitted = 1,
576    }
577}
578
579param! {
580    #[derive(Default)]
581    enum ZoneEntered {
582        #[default]
583        Low = 0,
584        Middle = 1,
585        High = 2,
586    }
587}
588
589param! {
590    #[derive(Default)]
591    enum LeTxPowerReportingReason {
592        #[default]
593        LocalTxPowerChanged = 0,
594        RemoteTxPowerChanged = 1,
595        LeReadRemoteTxPowerLevelCompleted = 2,
596    }
597}
598
599param! {
600    #[derive(Default)]
601    enum LeAdvEventKind {
602        #[default]
603        AdvInd = 0,
604        AdvDirectInd = 1,
605        AdvScanInd = 2,
606        AdvNonconnInd = 3,
607        ScanRsp = 4,
608    }
609}
610
611param! {
612    bitfield LeExtAdvEventKind[2] {
613        (0, connectable, set_connectable);
614        (1, scannable, set_scannable);
615        (2, directed, set_directed);
616        (3, scan_response, set_scan_response);
617        (4, legacy, set_legacy);
618    }
619}
620
621/// Advertising data status.
622#[allow(missing_docs)]
623pub enum LeExtAdvDataStatus {
624    Complete,
625    IncompleteMoreExpected,
626    IncompleteTruncated,
627    Reserved,
628}
629
630impl LeExtAdvEventKind {
631    /// Get data status.
632    pub fn data_status(&self) -> LeExtAdvDataStatus {
633        let data_status = (self.0[0] >> 5) & 0x03;
634        match data_status {
635            0 => LeExtAdvDataStatus::Complete,
636            1 => LeExtAdvDataStatus::IncompleteMoreExpected,
637            2 => LeExtAdvDataStatus::IncompleteTruncated,
638            _ => LeExtAdvDataStatus::Reserved,
639        }
640    }
641
642    /// Set data status.
643    pub fn set_data_status(mut self, status: LeExtAdvDataStatus) -> Self {
644        let value = match status {
645            LeExtAdvDataStatus::Complete => 0,
646            LeExtAdvDataStatus::IncompleteMoreExpected => 1,
647            LeExtAdvDataStatus::IncompleteTruncated => 2,
648            LeExtAdvDataStatus::Reserved => 3,
649        };
650        self.0[0] &= !(0x03 << 5);
651        self.0[0] |= value << 5;
652        self
653    }
654}
655
656param! {
657    struct LeAdvReport<'a> {
658        event_kind: LeAdvEventKind,
659        addr_kind: AddrKind,
660        addr: BdAddr,
661        data: &'a [u8],
662        rssi: i8,
663    }
664}
665
666param! {
667    struct LeAdvReports<'a> {
668        num_reports: u8,
669        bytes: RemainingBytes<'a>,
670    }
671}
672
673impl LeAdvReports<'_> {
674    /// Check if there are more reports available.
675    pub fn is_empty(&self) -> bool {
676        self.num_reports == 0
677    }
678
679    /// Number of advertising reports.
680    pub fn len(&self) -> usize {
681        usize::from(self.num_reports)
682    }
683
684    /// Create an iterator over the advertising reports.
685    pub fn iter(&self) -> LeAdvReportsIter<'_> {
686        LeAdvReportsIter {
687            len: self.len(),
688            bytes: &self.bytes,
689        }
690    }
691}
692
693/// An iterator for advertising reports.
694pub struct LeAdvReportsIter<'a> {
695    len: usize,
696    bytes: &'a [u8],
697}
698
699impl<'a> Iterator for LeAdvReportsIter<'a> {
700    type Item = Result<LeAdvReport<'a>, FromHciBytesError>;
701
702    fn next(&mut self) -> Option<Self::Item> {
703        if self.len == 0 {
704            None
705        } else {
706            match LeAdvReport::from_hci_bytes(self.bytes) {
707                Ok((report, rest)) => {
708                    self.bytes = rest;
709                    self.len -= 1;
710                    Some(Ok(report))
711                }
712                Err(err) => {
713                    self.len = 0;
714                    Some(Err(err))
715                }
716            }
717        }
718    }
719
720    fn size_hint(&self) -> (usize, Option<usize>) {
721        (self.len, Some(self.len))
722    }
723}
724
725impl ExactSizeIterator for LeAdvReportsIter<'_> {
726    fn len(&self) -> usize {
727        self.len
728    }
729}
730
731impl FusedIterator for LeAdvReportsIter<'_> {}
732
733#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
734#[cfg_attr(feature = "defmt", derive(defmt::Format))]
735#[allow(missing_docs)]
736pub struct LeExtAdvReport<'a> {
737    pub event_kind: LeExtAdvEventKind,
738    pub addr_kind: AddrKind,
739    pub addr: BdAddr,
740    pub primary_adv_phy: PhyKind,
741    pub secondary_adv_phy: Option<PhyKind>,
742    pub adv_sid: u8,
743    pub tx_power: i8,
744    pub rssi: i8,
745    pub adv_interval: Duration<1_250>,
746    pub direct_addr_kind: AddrKind,
747    pub direct_addr: BdAddr,
748    pub data: &'a [u8],
749}
750
751impl WriteHci for LeExtAdvReport<'_> {
752    #[inline(always)]
753    fn size(&self) -> usize {
754        WriteHci::size(&self.event_kind)
755            + WriteHci::size(&self.addr_kind)
756            + WriteHci::size(&self.addr)
757            + WriteHci::size(&self.primary_adv_phy)
758            + 1 //secondary_adv_phy
759            + WriteHci::size(&self.adv_sid)
760            + WriteHci::size(&self.tx_power)
761            + WriteHci::size(&self.rssi)
762            + WriteHci::size(&self.adv_interval)
763            + WriteHci::size(&self.direct_addr_kind)
764            + WriteHci::size(&self.direct_addr)
765            + WriteHci::size(&self.data)
766    }
767    #[inline(always)]
768    fn write_hci<W: ::embedded_io::Write>(&self, mut writer: W) -> Result<(), W::Error> {
769        self.event_kind.write_hci(&mut writer)?;
770        self.addr_kind.write_hci(&mut writer)?;
771        self.addr.write_hci(&mut writer)?;
772        self.primary_adv_phy.write_hci(&mut writer)?;
773        match self.secondary_adv_phy {
774            None => 0u8.write_hci(&mut writer)?,
775            Some(val) => val.write_hci(&mut writer)?,
776        };
777        self.adv_sid.write_hci(&mut writer)?;
778        self.tx_power.write_hci(&mut writer)?;
779        self.rssi.write_hci(&mut writer)?;
780        self.adv_interval.write_hci(&mut writer)?;
781        self.direct_addr_kind.write_hci(&mut writer)?;
782        self.direct_addr.write_hci(&mut writer)?;
783        self.data.write_hci(&mut writer)?;
784        Ok(())
785    }
786    #[inline(always)]
787    async fn write_hci_async<W: ::embedded_io_async::Write>(&self, mut writer: W) -> Result<(), W::Error> {
788        self.event_kind.write_hci_async(&mut writer).await?;
789        self.addr_kind.write_hci_async(&mut writer).await?;
790        self.addr.write_hci_async(&mut writer).await?;
791        self.primary_adv_phy.write_hci_async(&mut writer).await?;
792        match self.secondary_adv_phy {
793            None => 0u8.write_hci_async(&mut writer).await?,
794            Some(val) => val.write_hci_async(&mut writer).await?,
795        };
796        self.adv_sid.write_hci_async(&mut writer).await?;
797        self.tx_power.write_hci_async(&mut writer).await?;
798        self.rssi.write_hci_async(&mut writer).await?;
799        self.adv_interval.write_hci_async(&mut writer).await?;
800        self.direct_addr_kind.write_hci_async(&mut writer).await?;
801        self.direct_addr.write_hci_async(&mut writer).await?;
802        self.data.write_hci_async(&mut writer).await?;
803        Ok(())
804    }
805}
806
807impl<'de> crate::FromHciBytes<'de> for LeExtAdvReport<'de> {
808    #[allow(unused_variables)]
809    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
810        let (event_kind, data) = <LeExtAdvEventKind as crate::FromHciBytes>::from_hci_bytes(data)?;
811        let (addr_kind, data) = <AddrKind as crate::FromHciBytes>::from_hci_bytes(data)?;
812        let (addr, data) = <BdAddr as crate::FromHciBytes>::from_hci_bytes(data)?;
813        let (primary_adv_phy, data) = <PhyKind as crate::FromHciBytes>::from_hci_bytes(data)?;
814        let (secondary_adv_phy, data) = if data[0] == 0 {
815            (None, &data[1..])
816        } else {
817            let (ret, rest) = <PhyKind as crate::FromHciBytes>::from_hci_bytes(data)?;
818            (Some(ret), rest)
819        };
820        let (adv_sid, data) = <u8 as crate::FromHciBytes>::from_hci_bytes(data)?;
821        let (tx_power, data) = <i8 as crate::FromHciBytes>::from_hci_bytes(data)?;
822        let (rssi, data) = <i8 as crate::FromHciBytes>::from_hci_bytes(data)?;
823        let (adv_interval, data) = <Duration<1_250> as crate::FromHciBytes>::from_hci_bytes(data)?;
824        let (direct_addr_kind, data) = <AddrKind as crate::FromHciBytes>::from_hci_bytes(data)?;
825        let (direct_addr, data) = <BdAddr as crate::FromHciBytes>::from_hci_bytes(data)?;
826        let (data, rest) = <&'de [u8] as crate::FromHciBytes>::from_hci_bytes(data)?;
827        Ok((
828            Self {
829                event_kind,
830                addr_kind,
831                addr,
832                primary_adv_phy,
833                secondary_adv_phy,
834                adv_sid,
835                tx_power,
836                rssi,
837                adv_interval,
838                direct_addr_kind,
839                direct_addr,
840                data,
841            },
842            rest,
843        ))
844    }
845}
846
847param! {
848    struct LeExtAdvReports<'a> {
849        num_reports: u8,
850        bytes: RemainingBytes<'a>,
851    }
852}
853
854impl LeExtAdvReports<'_> {
855    /// Check if there are more reports available.
856    pub fn is_empty(&self) -> bool {
857        self.num_reports == 0
858    }
859
860    /// Number of advertising reports.
861    pub fn len(&self) -> usize {
862        usize::from(self.num_reports)
863    }
864
865    /// Create an iterator over the advertising reports.
866    pub fn iter(&self) -> LeExtAdvReportsIter<'_> {
867        LeExtAdvReportsIter {
868            len: self.len(),
869            bytes: &self.bytes,
870        }
871    }
872}
873
874/// An iterator for extended advertising reports.
875pub struct LeExtAdvReportsIter<'a> {
876    len: usize,
877    bytes: &'a [u8],
878}
879
880impl<'a> Iterator for LeExtAdvReportsIter<'a> {
881    type Item = Result<LeExtAdvReport<'a>, FromHciBytesError>;
882
883    fn next(&mut self) -> Option<Self::Item> {
884        if self.len == 0 {
885            None
886        } else {
887            match LeExtAdvReport::from_hci_bytes(self.bytes) {
888                Ok((report, rest)) => {
889                    self.bytes = rest;
890                    self.len -= 1;
891                    Some(Ok(report))
892                }
893                Err(err) => {
894                    self.len = 0;
895                    Some(Err(err))
896                }
897            }
898        }
899    }
900
901    fn size_hint(&self) -> (usize, Option<usize>) {
902        (self.len, Some(self.len))
903    }
904}
905
906impl ExactSizeIterator for LeExtAdvReportsIter<'_> {
907    fn len(&self) -> usize {
908        self.len
909    }
910}
911
912impl FusedIterator for LeExtAdvReportsIter<'_> {}
913
914param! {
915    struct LePeriodicAdvSubeventData<'a> {
916        subevent: u8,
917        response_slot_start: u8,
918        response_slot_count: u8,
919        subevent_data: &'a [u8],
920    }
921}
922
923impl<'a, 'b: 'a> WriteHci for &'a [LePeriodicAdvSubeventData<'b>] {
924    #[inline(always)]
925    fn size(&self) -> usize {
926        1 + self.iter().map(WriteHci::size).sum::<usize>()
927    }
928    #[inline(always)]
929    fn write_hci<W: ::embedded_io::Write>(&self, mut writer: W) -> Result<(), W::Error> {
930        writer.write_all(&[self.len() as u8])?;
931        for x in self.iter() {
932            <LePeriodicAdvSubeventData as WriteHci>::write_hci(x, &mut writer)?;
933        }
934        Ok(())
935    }
936    #[inline(always)]
937    async fn write_hci_async<W: ::embedded_io_async::Write>(&self, mut writer: W) -> Result<(), W::Error> {
938        writer.write_all(&[self.len() as u8]).await?;
939        for x in self.iter() {
940            <LePeriodicAdvSubeventData as WriteHci>::write_hci_async(x, &mut writer).await?;
941        }
942        Ok(())
943    }
944}
945
946#[allow(missing_docs)]
947fn read_n<T: ByteAlignedValue>(data: &[u8], n: usize) -> Result<(&[T], &[u8]), FromHciBytesError> {
948    let size = n * core::mem::size_of::<T>();
949    if data.len() < size {
950        return Err(FromHciBytesError::InvalidSize);
951    }
952    let (bytes, rest) = data.split_at(size);
953    let slice = unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const T, n) };
954    Ok((slice, rest))
955}
956
957param! {
958    struct LePeriodicAdvertisingResponseReport<'a> {
959        tx_power: i8,
960        rssi: i8,
961        cte_type: CteKind,
962        response_slot: u8,
963        data_status: DataStatus,
964        data_length: u8,
965        data: &'a [u8],
966    }
967}
968
969/// Container for periodic advertising response report data.
970#[derive(Debug, Clone, Hash)]
971#[cfg_attr(feature = "defmt", derive(defmt::Format))]
972pub struct LePeriodicAdvertisingResponseReports<'a> {
973    num_responses: u8,
974    tx_power: &'a [i8],
975    rssi: &'a [i8],
976    cte_type: &'a [CteKind],
977    response_slot: &'a [u8],
978    data_status: &'a [DataStatus],
979    data_length: &'a [u8],
980    data: &'a [u8],
981}
982
983impl<'a> LePeriodicAdvertisingResponseReports<'a> {
984    /// Returns `true` if there are no responses.
985    pub fn is_empty(&self) -> bool {
986        self.num_responses == 0
987    }
988
989    /// Returns the number of responses.
990    pub fn len(&self) -> usize {
991        usize::from(self.num_responses)
992    }
993
994    /// Returns the response entry at the given index, or `None` if out of bounds.
995    pub fn get(&self, index: usize) -> Option<LePeriodicAdvertisingResponseReport<'a>> {
996        if index >= self.len() {
997            return None;
998        }
999        let data_offset: usize = self.data_length[..index].iter().map(|&l| l as usize).sum();
1000        let data_len = self.data_length[index] as usize;
1001        Some(LePeriodicAdvertisingResponseReport {
1002            tx_power: self.tx_power[index],
1003            rssi: self.rssi[index],
1004            cte_type: self.cte_type[index],
1005            response_slot: self.response_slot[index],
1006            data_status: self.data_status[index],
1007            data_length: self.data_length[index],
1008            data: &self.data[data_offset..data_offset + data_len],
1009        })
1010    }
1011
1012    /// Returns an iterator over all response entries.
1013    pub fn iter(&self) -> LePeriodicAdvertisingResponseReportsIter<'_> {
1014        LePeriodicAdvertisingResponseReportsIter {
1015            reports: self,
1016            index: 0,
1017        }
1018    }
1019}
1020
1021impl<'de> FromHciBytes<'de> for LePeriodicAdvertisingResponseReports<'de> {
1022    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), FromHciBytesError> {
1023        let (num_responses, data) = u8::from_hci_bytes(data)?;
1024        let n = num_responses as usize;
1025
1026        let (tx_power, data) = read_n::<i8>(data, n)?;
1027        let (rssi, data) = read_n::<i8>(data, n)?;
1028        let (cte_type, data) = read_n::<CteKind>(data, n)?;
1029        let (response_slot, data) = read_n::<u8>(data, n)?;
1030        let (data_status, data) = read_n::<DataStatus>(data, n)?;
1031        let (data_length, data) = read_n::<u8>(data, n)?;
1032
1033        Ok((
1034            Self {
1035                num_responses,
1036                tx_power,
1037                rssi,
1038                cte_type,
1039                response_slot,
1040                data_status,
1041                data_length,
1042                data,
1043            },
1044            &[],
1045        ))
1046    }
1047}
1048
1049/// An iterator over the LePeriodicAdvertisingResponse reports.
1050pub struct LePeriodicAdvertisingResponseReportsIter<'a> {
1051    reports: &'a LePeriodicAdvertisingResponseReports<'a>,
1052    index: usize,
1053}
1054
1055impl<'a> Iterator for LePeriodicAdvertisingResponseReportsIter<'a> {
1056    type Item = LePeriodicAdvertisingResponseReport<'a>;
1057
1058    fn next(&mut self) -> Option<Self::Item> {
1059        let entry = self.reports.get(self.index)?;
1060        self.index += 1;
1061        Some(entry)
1062    }
1063
1064    fn size_hint(&self) -> (usize, Option<usize>) {
1065        let remaining = self.reports.len() - self.index;
1066        (remaining, Some(remaining))
1067    }
1068}
1069
1070impl ExactSizeIterator for LePeriodicAdvertisingResponseReportsIter<'_> {
1071    fn len(&self) -> usize {
1072        self.reports.len() - self.index
1073    }
1074}
1075
1076impl FusedIterator for LePeriodicAdvertisingResponseReportsIter<'_> {}
1077
1078param! {
1079    struct LeCsSubeventStepEntry<'a> {
1080        step_mode: u8,
1081        step_channel: u8,
1082        step_data_length: u8,
1083        step_data: &'a [u8],
1084    }
1085}
1086
1087/// Container for CS subevent step data.
1088///
1089/// Parses the column-major wire format:
1090/// `num_steps_reported | step_mode[] | step_channel[] | step_data_length[] | step_data[]`
1091///
1092/// Entries are accessed via [`get`](LeCsSubeventStepData::get)
1093/// or [`iter`](LeCsSubeventStepData::iter).
1094#[derive(Debug, Clone, Hash)]
1095#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1096pub struct LeCsSubeventStepData<'a> {
1097    num_steps_reported: u8,
1098    step_mode: &'a [u8],
1099    step_channel: &'a [u8],
1100    step_data_length: &'a [u8],
1101    step_data: &'a [u8],
1102}
1103
1104impl<'a> LeCsSubeventStepData<'a> {
1105    /// Returns `true` if there are no steps.
1106    pub fn is_empty(&self) -> bool {
1107        self.num_steps_reported == 0
1108    }
1109
1110    /// Returns the number of steps.
1111    pub fn len(&self) -> usize {
1112        usize::from(self.num_steps_reported)
1113    }
1114
1115    /// Returns the step entry at the given index, or `None` if out of bounds.
1116    pub fn get(&self, index: usize) -> Option<LeCsSubeventStepEntry<'a>> {
1117        if index >= self.len() {
1118            return None;
1119        }
1120        let data_offset: usize = self.step_data_length[..index].iter().map(|&l| l as usize).sum();
1121        let data_len = self.step_data_length[index] as usize;
1122        Some(LeCsSubeventStepEntry {
1123            step_mode: self.step_mode[index],
1124            step_channel: self.step_channel[index],
1125            step_data_length: self.step_data_length[index],
1126            step_data: &self.step_data[data_offset..data_offset + data_len],
1127        })
1128    }
1129
1130    /// Returns an iterator over all step entries.
1131    pub fn iter(&self) -> LeCsSubeventStepDataIter<'_> {
1132        LeCsSubeventStepDataIter { data: self, index: 0 }
1133    }
1134}
1135
1136impl<'de> FromHciBytes<'de> for LeCsSubeventStepData<'de> {
1137    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), FromHciBytesError> {
1138        let (num_steps_reported, data) = u8::from_hci_bytes(data)?;
1139        let n = num_steps_reported as usize;
1140
1141        let (step_mode, data) = read_n::<u8>(data, n)?;
1142        let (step_channel, data) = read_n::<u8>(data, n)?;
1143        let (step_data_length, data) = read_n::<u8>(data, n)?;
1144
1145        Ok((
1146            Self {
1147                num_steps_reported,
1148                step_mode,
1149                step_channel,
1150                step_data_length,
1151                step_data: data,
1152            },
1153            &[],
1154        ))
1155    }
1156}
1157
1158/// An iterator over LeCsSubeventStepEntry values.
1159pub struct LeCsSubeventStepDataIter<'a> {
1160    data: &'a LeCsSubeventStepData<'a>,
1161    index: usize,
1162}
1163
1164impl<'a> Iterator for LeCsSubeventStepDataIter<'a> {
1165    type Item = LeCsSubeventStepEntry<'a>;
1166
1167    fn next(&mut self) -> Option<Self::Item> {
1168        let entry = self.data.get(self.index)?;
1169        self.index += 1;
1170        Some(entry)
1171    }
1172
1173    fn size_hint(&self) -> (usize, Option<usize>) {
1174        let remaining = self.data.len() - self.index;
1175        (remaining, Some(remaining))
1176    }
1177}
1178
1179impl ExactSizeIterator for LeCsSubeventStepDataIter<'_> {
1180    fn len(&self) -> usize {
1181        self.data.len() - self.index
1182    }
1183}
1184
1185impl FusedIterator for LeCsSubeventStepDataIter<'_> {}
1186
1187param! {
1188    #[derive(Default)]
1189    enum DoneStatus {
1190        #[default]
1191        Complete = 0,
1192        Partial = 1,
1193        Aborted = 0xf,
1194    }
1195}
1196
1197/// Procedure abort reason.
1198#[repr(u8)]
1199#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1200#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1201pub enum ProcedureAbortReason {
1202    #[default]
1203    /// Report with no abort.
1204    NoAbort = 0x0,
1205    /// Abort because of local Host or remote request.
1206    HostRequest = 0x1,
1207    /// Abort because filtered channel map has less than 15 channels.
1208    FilteredChannelMap = 0x2,
1209    /// Abort because the channel map update instant has passed.
1210    ChannelMapInstantPassed = 0x3,
1211    /// Abort because of unspecified reasons.
1212    Unspecified = 0xf,
1213}
1214
1215impl From<u8> for ProcedureAbortReason {
1216    fn from(v: u8) -> Self {
1217        match v {
1218            0x0 => Self::NoAbort,
1219            0x1 => Self::HostRequest,
1220            0x2 => Self::FilteredChannelMap,
1221            0x3 => Self::ChannelMapInstantPassed,
1222            _ => Self::Unspecified,
1223        }
1224    }
1225}
1226
1227/// Subevent abort reason.
1228#[repr(u8)]
1229#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1230#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1231pub enum SubeventAbortReason {
1232    #[default]
1233    /// Report with no abort.
1234    NoAbort = 0x0,
1235    /// Abort because of local Host or remote request.
1236    HostRequest = 0x1,
1237    /// Abort because no CS_SYNC (mode-0) received.
1238    NoCsSync = 0x2,
1239    /// Abort because of scheduling conflicts or limited resources.
1240    SchedulingConflict = 0x3,
1241    /// Abort because of unspecified reasons.
1242    Unspecified = 0xf,
1243}
1244
1245impl From<u8> for SubeventAbortReason {
1246    fn from(v: u8) -> Self {
1247        match v {
1248            0x0 => Self::NoAbort,
1249            0x1 => Self::HostRequest,
1250            0x2 => Self::NoCsSync,
1251            0x3 => Self::SchedulingConflict,
1252            _ => Self::Unspecified,
1253        }
1254    }
1255}
1256
1257/// Abort reason for CS subevent result, packed as two 4-bit nibbles.
1258///
1259/// Bits 0-3: procedure abort reason ([`ProcedureAbortReason`])
1260/// Bits 4-7: subevent abort reason ([`SubeventAbortReason`])
1261#[repr(transparent)]
1262#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1263#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1264pub struct PackedAbortReasons(u8);
1265
1266impl PackedAbortReasons {
1267    /// Returns the procedure-level abort reason.
1268    pub fn procedure_reason(&self) -> ProcedureAbortReason {
1269        ProcedureAbortReason::from(self.0 & 0x0f)
1270    }
1271
1272    /// Returns the subevent-level abort reason.
1273    pub fn subevent_reason(&self) -> SubeventAbortReason {
1274        SubeventAbortReason::from((self.0 >> 4) & 0x0f)
1275    }
1276}
1277
1278unsafe impl FixedSizeValue for PackedAbortReasons {
1279    fn is_valid(_data: &[u8]) -> bool {
1280        true
1281    }
1282}
1283
1284/// Frequency compensation value in units of 0.01 ppm.
1285#[repr(transparent)]
1286#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1287#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1288pub struct FrequencyCompensation(u16);
1289
1290impl FrequencyCompensation {
1291    /// Returns the raw 16-bit value.
1292    pub fn as_raw(&self) -> u16 {
1293        self.0
1294    }
1295
1296    /// Returns `true` if the value is available.
1297    pub fn is_available(&self) -> bool {
1298        self.0 != 0xC000
1299    }
1300
1301    /// Returns the frequency compensation in 0.01 ppm units, or `None` if not available.
1302    pub fn as_ppm_x100(&self) -> Option<i16> {
1303        if !self.is_available() {
1304            return None;
1305        }
1306        let val = self.0 & 0x7FFF;
1307        Some(((val << 1) as i16) >> 1)
1308    }
1309}
1310
1311unsafe impl FixedSizeValue for FrequencyCompensation {
1312    fn is_valid(_data: &[u8]) -> bool {
1313        true
1314    }
1315}
1316
1317// ============================================================================
1318// LE Audio / Isochronous parameters (Bluetooth Core Specification v5.4+)
1319// ============================================================================
1320
1321param!(
1322    /// CIG ID (0x00 – 0xEF)
1323    struct CigId(u8)
1324);
1325
1326#[allow(missing_docs)]
1327impl CigId {
1328    /// Create a new instance.
1329    pub const fn new(v: u8) -> Self {
1330        Self(v)
1331    }
1332
1333    /// Get the inner representation.
1334    pub fn as_raw(&self) -> u8 {
1335        self.0
1336    }
1337}
1338
1339unsafe impl ByteAlignedValue for CigId {}
1340
1341impl<'de> crate::FromHciBytes<'de> for &'de CigId {
1342    #[inline(always)]
1343    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1344        <CigId as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1345    }
1346}
1347
1348param!(
1349    /// CIS ID (0x00 – 0xEF)
1350    struct CisId(u8)
1351);
1352
1353#[allow(missing_docs)]
1354impl CisId {
1355    /// Create a new instance.
1356    pub const fn new(v: u8) -> Self {
1357        Self(v)
1358    }
1359
1360    /// Get the inner representation.
1361    pub fn as_raw(&self) -> u8 {
1362        self.0
1363    }
1364}
1365
1366unsafe impl ByteAlignedValue for CisId {}
1367
1368impl<'de> crate::FromHciBytes<'de> for &'de CisId {
1369    #[inline(always)]
1370    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1371        <CisId as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1372    }
1373}
1374
1375param! {
1376    /// Per-CIS configuration for `LE Set CIG Parameters`.
1377    struct CisConfig {
1378        cis_id: CisId,
1379        max_sdu_c_to_p: u16,
1380        max_sdu_p_to_c: u16,
1381        phy_c_to_p: PhyMask,
1382        phy_p_to_c: PhyMask,
1383        rtn_c_to_p: u8,
1384        rtn_p_to_c: u8,
1385    }
1386}
1387
1388param_slice!(&'a [CisConfig]);
1389
1390param! {
1391    /// Per-CIS configuration for `LE Set CIG Parameters Test`.
1392    struct CisConfigTest {
1393        cis_id: CisId,
1394        max_sdu_c_to_p: u16,
1395        max_sdu_p_to_c: u16,
1396        max_pdu_c_to_p: u16,
1397        max_pdu_p_to_c: u16,
1398        phy_c_to_p: PhyMask,
1399        phy_p_to_c: PhyMask,
1400        bn_c_to_p: u8,
1401        bn_p_to_c: u8,
1402    }
1403}
1404
1405param_slice!(&'a [CisConfigTest]);
1406
1407param! {
1408    /// CIS-to-ACL handle mapping for `LE Create CIS`.
1409    struct CisConnConfig {
1410        cis_handle: ConnHandle,
1411        acl_handle: ConnHandle,
1412    }
1413}
1414
1415param_slice!(&'a [CisConnConfig]);
1416
1417param! {
1418    /// Data path direction.
1419    #[derive(Default)]
1420    enum DataPathDirection {
1421        #[default]
1422        Input = 0,
1423        Output = 1,
1424    }
1425}
1426
1427param!(
1428    /// Data path identifier.
1429    ///
1430    /// - `0x00` = HCI
1431    /// - `0x01`–`0xFE` = Logical channel number (vendor-specific)
1432    /// - `0xFF` = Audio test mode
1433    struct DataPathId(u8)
1434);
1435
1436#[allow(missing_docs)]
1437impl DataPathId {
1438    /// HCI data path.
1439    pub const HCI: DataPathId = DataPathId(0x00);
1440    /// Audio test mode.
1441    pub const AUDIO_TEST_MODE: DataPathId = DataPathId(0xFF);
1442
1443    /// Create a new instance.
1444    pub const fn new(v: u8) -> Self {
1445        Self(v)
1446    }
1447
1448    /// Get the inner representation.
1449    pub fn as_raw(&self) -> u8 {
1450        self.0
1451    }
1452}
1453
1454unsafe impl ByteAlignedValue for DataPathId {}
1455
1456impl<'de> crate::FromHciBytes<'de> for &'de DataPathId {
1457    #[inline(always)]
1458    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1459        <DataPathId as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1460    }
1461}
1462
1463param! {
1464    /// Codec ID (5 octets).
1465    #[derive(Default)]
1466    struct CodecId {
1467        coding_format: u8,
1468        company_id: u16,
1469        vendor_specific_codec_id: u16,
1470    }
1471}
1472
1473param! {
1474    /// Logical transport type.
1475    #[derive(Default)]
1476    enum LogicalTransportType {
1477        #[default]
1478        BrEdrAcl = 0,
1479        BrEdrSco = 1,
1480        LeCis = 2,
1481        LeBis = 3,
1482    }
1483}
1484
1485param!(
1486    /// Broadcast code (16 octets).
1487    struct BroadcastCode([u8; 16])
1488);
1489
1490#[allow(missing_docs)]
1491impl BroadcastCode {
1492    /// Create a new instance.
1493    pub const fn new(v: [u8; 16]) -> Self {
1494        Self(v)
1495    }
1496
1497    /// Get the byte representation.
1498    pub fn raw(&self) -> &[u8] {
1499        &self.0[..]
1500    }
1501}
1502
1503unsafe impl ByteAlignedValue for BroadcastCode {}
1504
1505impl<'de> crate::FromHciBytes<'de> for &'de BroadcastCode {
1506    #[inline(always)]
1507    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1508        <BroadcastCode as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1509    }
1510}
1511
1512param! {
1513    /// BIG encryption mode.
1514    #[derive(Default)]
1515    enum EncryptionMode {
1516        #[default]
1517        Unencrypted = 0,
1518        Encrypted = 1,
1519    }
1520}
1521
1522param! {
1523    /// CIS/BIG packing method.
1524    #[derive(Default)]
1525    enum Packing {
1526        #[default]
1527        Sequential = 0,
1528        Interleaved = 1,
1529    }
1530}
1531
1532param! {
1533    /// CIS/BIG framing mode.
1534    #[derive(Default)]
1535    enum Framing {
1536        #[default]
1537        Unframed = 0,
1538        Framed = 1,
1539    }
1540}
1541
1542param! {
1543    /// ISO test payload type.
1544    #[derive(Default)]
1545    enum PayloadType {
1546        #[default]
1547        ZeroLength = 0,
1548        VariableLength = 1,
1549        MaximumLength = 2,
1550    }
1551}
1552
1553#[cfg(test)]
1554mod tests {
1555    use super::*;
1556
1557    #[test]
1558    fn test_ext_adv_event_kind() {
1559        let k = LeExtAdvEventKind::new().set_connectable(true);
1560        assert_eq!(k.0[0], 0b0000001);
1561        let k = k.set_data_status(LeExtAdvDataStatus::Complete);
1562        assert_eq!(k.0[0], 0b0000001);
1563        let k = k.set_data_status(LeExtAdvDataStatus::IncompleteMoreExpected);
1564        assert_eq!(k.0[0], 0b0100001);
1565        let k = k.set_data_status(LeExtAdvDataStatus::IncompleteTruncated);
1566        assert_eq!(k.0[0], 0b1000001);
1567    }
1568
1569    #[test]
1570    fn test_channel_map_new() {
1571        let m = ChannelMap::new();
1572        for chan in 0..37 {
1573            assert!(!m.is_channel_bad(chan));
1574        }
1575
1576        for chan in 37..40 {
1577            assert!(m.is_channel_bad(chan));
1578        }
1579    }
1580
1581    #[test]
1582    fn test_frequency_compensation_positive() {
1583        let fc = FrequencyCompensation(0x2710);
1584        assert!(fc.is_available());
1585        assert_eq!(fc.as_ppm_x100(), Some(10000));
1586        assert_eq!(fc.as_raw(), 0x2710);
1587    }
1588
1589    #[test]
1590    fn test_frequency_compensation_negative() {
1591        let fc = FrequencyCompensation(0x58F0);
1592        assert!(fc.is_available());
1593        assert_eq!(fc.as_ppm_x100(), Some(-10000));
1594        assert_eq!(fc.as_raw(), 0x58F0);
1595    }
1596
1597    #[test]
1598    fn test_frequency_compensation_not_available() {
1599        let fc = FrequencyCompensation(0xC000);
1600        assert!(!fc.is_available());
1601        assert_eq!(fc.as_ppm_x100(), None);
1602        assert_eq!(fc.as_raw(), 0xC000);
1603    }
1604}