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(u16, align(1))]
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(u16));
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! {
449    bitfield LeDataRelatedAddrChangeReasons[1] {
450        (0, change_on_adv_data_change, set_change_addr_on_adv_data_changes);
451        (1, change_on_scan_response_data_change, set_change_addr_on_scan_response_data_changes);
452    }
453}
454
455param! {
456    #[derive(Default)]
457    enum LeConnRole {
458        #[default]
459        Central = 0,
460        Peripheral = 1,
461    }
462}
463
464param! {
465    #[derive(Default)]
466    enum ClockAccuracy {
467        #[default]
468        Ppm500 = 0,
469        Ppm250 = 1,
470        Ppm150 = 2,
471        Ppm100 = 3,
472        Ppm75 = 4,
473        Ppm50 = 5,
474        Ppm30 = 6,
475        Ppm20 = 7,
476    }
477}
478
479param! {
480    struct LeAdvertisingReportParam<'a> {
481        event_type: u8,
482        addr_kind: AddrKind,
483        addr: BdAddr,
484        data: &'a [u8],
485        rssi: i8,
486    }
487}
488
489param_slice! {
490    [LeDirectedAdvertisingReportParam; 16] {
491        event_type[0]: u8,
492        addr_kind[1]: AddrKind,
493        addr[2]: BdAddr,
494        direct_addr_kind[8]: AddrKind,
495        direct_addr[9]: BdAddr,
496        rssi[15]: i8,
497    }
498}
499
500param_slice! {
501    [LeIQSample; 2] {
502        i_sample[0]: i8,
503        q_sample[1]: i8,
504    }
505}
506
507param_slice! {
508    [BisConnHandle; 2] {
509        handle[0]: ConnHandle,
510    }
511}
512
513param! {
514    #[derive(Default)]
515    enum DataStatus {
516        #[default]
517        Complete = 0,
518        Incomplete = 1,
519        Failed = 0xff,
520    }
521}
522
523param! {
524    #[derive(Default)]
525    enum PacketStatus {
526        #[default]
527        CrcCorrect = 0,
528        CrcIncorrectUsedLength = 1,
529        CrcIncorrectUsedOther = 2,
530        InsufficientResources = 0xff,
531    }
532}
533
534param! {
535    #[derive(Default)]
536    enum TxStatus {
537        #[default]
538        Transmitted = 0,
539        NotTransmitted = 1,
540    }
541}
542
543param! {
544    #[derive(Default)]
545    enum ZoneEntered {
546        #[default]
547        Low = 0,
548        Middle = 1,
549        High = 2,
550    }
551}
552
553param! {
554    #[derive(Default)]
555    enum LeTxPowerReportingReason {
556        #[default]
557        LocalTxPowerChanged = 0,
558        RemoteTxPowerChanged = 1,
559        LeReadRemoteTxPowerLevelCompleted = 2,
560    }
561}
562
563param! {
564    #[derive(Default)]
565    enum LeAdvEventKind {
566        #[default]
567        AdvInd = 0,
568        AdvDirectInd = 1,
569        AdvScanInd = 2,
570        AdvNonconnInd = 3,
571        ScanRsp = 4,
572    }
573}
574
575param! {
576    bitfield LeExtAdvEventKind[2] {
577        (0, connectable, set_connectable);
578        (1, scannable, set_scannable);
579        (2, directed, set_directed);
580        (3, scan_response, set_scan_response);
581        (4, legacy, set_legacy);
582    }
583}
584
585/// Advertising data status.
586#[allow(missing_docs)]
587pub enum LeExtAdvDataStatus {
588    Complete,
589    IncompleteMoreExpected,
590    IncompleteTruncated,
591    Reserved,
592}
593
594impl LeExtAdvEventKind {
595    /// Get data status.
596    pub fn data_status(&self) -> LeExtAdvDataStatus {
597        let data_status = (self.0[0] >> 5) & 0x03;
598        match data_status {
599            0 => LeExtAdvDataStatus::Complete,
600            1 => LeExtAdvDataStatus::IncompleteMoreExpected,
601            2 => LeExtAdvDataStatus::IncompleteTruncated,
602            _ => LeExtAdvDataStatus::Reserved,
603        }
604    }
605
606    /// Set data status.
607    pub fn set_data_status(mut self, status: LeExtAdvDataStatus) -> Self {
608        let value = match status {
609            LeExtAdvDataStatus::Complete => 0,
610            LeExtAdvDataStatus::IncompleteMoreExpected => 1,
611            LeExtAdvDataStatus::IncompleteTruncated => 2,
612            LeExtAdvDataStatus::Reserved => 3,
613        };
614        self.0[0] &= !(0x03 << 5);
615        self.0[0] |= value << 5;
616        self
617    }
618}
619
620param! {
621    struct LeAdvReport<'a> {
622        event_kind: LeAdvEventKind,
623        addr_kind: AddrKind,
624        addr: BdAddr,
625        data: &'a [u8],
626        rssi: i8,
627    }
628}
629
630param! {
631    struct LeAdvReports<'a> {
632        num_reports: u8,
633        bytes: RemainingBytes<'a>,
634    }
635}
636
637impl LeAdvReports<'_> {
638    /// Check if there are more reports available.
639    pub fn is_empty(&self) -> bool {
640        self.num_reports == 0
641    }
642
643    /// Number of advertising reports.
644    pub fn len(&self) -> usize {
645        usize::from(self.num_reports)
646    }
647
648    /// Create an iterator over the advertising reports.
649    pub fn iter(&self) -> LeAdvReportsIter<'_> {
650        LeAdvReportsIter {
651            len: self.len(),
652            bytes: &self.bytes,
653        }
654    }
655}
656
657/// An iterator for advertising reports.
658pub struct LeAdvReportsIter<'a> {
659    len: usize,
660    bytes: &'a [u8],
661}
662
663impl<'a> Iterator for LeAdvReportsIter<'a> {
664    type Item = Result<LeAdvReport<'a>, FromHciBytesError>;
665
666    fn next(&mut self) -> Option<Self::Item> {
667        if self.len == 0 {
668            None
669        } else {
670            match LeAdvReport::from_hci_bytes(self.bytes) {
671                Ok((report, rest)) => {
672                    self.bytes = rest;
673                    self.len -= 1;
674                    Some(Ok(report))
675                }
676                Err(err) => {
677                    self.len = 0;
678                    Some(Err(err))
679                }
680            }
681        }
682    }
683
684    fn size_hint(&self) -> (usize, Option<usize>) {
685        (self.len, Some(self.len))
686    }
687}
688
689impl ExactSizeIterator for LeAdvReportsIter<'_> {
690    fn len(&self) -> usize {
691        self.len
692    }
693}
694
695impl FusedIterator for LeAdvReportsIter<'_> {}
696
697#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
698#[cfg_attr(feature = "defmt", derive(defmt::Format))]
699#[allow(missing_docs)]
700pub struct LeExtAdvReport<'a> {
701    pub event_kind: LeExtAdvEventKind,
702    pub addr_kind: AddrKind,
703    pub addr: BdAddr,
704    pub primary_adv_phy: PhyKind,
705    pub secondary_adv_phy: Option<PhyKind>,
706    pub adv_sid: u8,
707    pub tx_power: i8,
708    pub rssi: i8,
709    pub adv_interval: Duration<1_250>,
710    pub direct_addr_kind: AddrKind,
711    pub direct_addr: BdAddr,
712    pub data: &'a [u8],
713}
714
715impl WriteHci for LeExtAdvReport<'_> {
716    #[inline(always)]
717    fn size(&self) -> usize {
718        WriteHci::size(&self.event_kind)
719            + WriteHci::size(&self.addr_kind)
720            + WriteHci::size(&self.addr)
721            + WriteHci::size(&self.primary_adv_phy)
722            + 1 //secondary_adv_phy
723            + WriteHci::size(&self.adv_sid)
724            + WriteHci::size(&self.tx_power)
725            + WriteHci::size(&self.rssi)
726            + WriteHci::size(&self.adv_interval)
727            + WriteHci::size(&self.direct_addr_kind)
728            + WriteHci::size(&self.direct_addr)
729            + WriteHci::size(&self.data)
730    }
731    #[inline(always)]
732    fn write_hci<W: ::embedded_io::Write>(&self, mut writer: W) -> Result<(), W::Error> {
733        self.event_kind.write_hci(&mut writer)?;
734        self.addr_kind.write_hci(&mut writer)?;
735        self.addr.write_hci(&mut writer)?;
736        self.primary_adv_phy.write_hci(&mut writer)?;
737        match self.secondary_adv_phy {
738            None => 0u8.write_hci(&mut writer)?,
739            Some(val) => val.write_hci(&mut writer)?,
740        };
741        self.adv_sid.write_hci(&mut writer)?;
742        self.tx_power.write_hci(&mut writer)?;
743        self.rssi.write_hci(&mut writer)?;
744        self.adv_interval.write_hci(&mut writer)?;
745        self.direct_addr_kind.write_hci(&mut writer)?;
746        self.direct_addr.write_hci(&mut writer)?;
747        self.data.write_hci(&mut writer)?;
748        Ok(())
749    }
750    #[inline(always)]
751    async fn write_hci_async<W: ::embedded_io_async::Write>(&self, mut writer: W) -> Result<(), W::Error> {
752        self.event_kind.write_hci_async(&mut writer).await?;
753        self.addr_kind.write_hci_async(&mut writer).await?;
754        self.addr.write_hci_async(&mut writer).await?;
755        self.primary_adv_phy.write_hci_async(&mut writer).await?;
756        match self.secondary_adv_phy {
757            None => 0u8.write_hci_async(&mut writer).await?,
758            Some(val) => val.write_hci_async(&mut writer).await?,
759        };
760        self.adv_sid.write_hci_async(&mut writer).await?;
761        self.tx_power.write_hci_async(&mut writer).await?;
762        self.rssi.write_hci_async(&mut writer).await?;
763        self.adv_interval.write_hci_async(&mut writer).await?;
764        self.direct_addr_kind.write_hci_async(&mut writer).await?;
765        self.direct_addr.write_hci_async(&mut writer).await?;
766        self.data.write_hci_async(&mut writer).await?;
767        Ok(())
768    }
769}
770
771impl<'de> crate::FromHciBytes<'de> for LeExtAdvReport<'de> {
772    #[allow(unused_variables)]
773    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
774        let (event_kind, data) = <LeExtAdvEventKind as crate::FromHciBytes>::from_hci_bytes(data)?;
775        let (addr_kind, data) = <AddrKind as crate::FromHciBytes>::from_hci_bytes(data)?;
776        let (addr, data) = <BdAddr as crate::FromHciBytes>::from_hci_bytes(data)?;
777        let (primary_adv_phy, data) = <PhyKind as crate::FromHciBytes>::from_hci_bytes(data)?;
778        let (secondary_adv_phy, data) = if data[0] == 0 {
779            (None, &data[1..])
780        } else {
781            let (ret, rest) = <PhyKind as crate::FromHciBytes>::from_hci_bytes(data)?;
782            (Some(ret), rest)
783        };
784        let (adv_sid, data) = <u8 as crate::FromHciBytes>::from_hci_bytes(data)?;
785        let (tx_power, data) = <i8 as crate::FromHciBytes>::from_hci_bytes(data)?;
786        let (rssi, data) = <i8 as crate::FromHciBytes>::from_hci_bytes(data)?;
787        let (adv_interval, data) = <Duration<1_250> as crate::FromHciBytes>::from_hci_bytes(data)?;
788        let (direct_addr_kind, data) = <AddrKind as crate::FromHciBytes>::from_hci_bytes(data)?;
789        let (direct_addr, data) = <BdAddr as crate::FromHciBytes>::from_hci_bytes(data)?;
790        let (data, rest) = <&'de [u8] as crate::FromHciBytes>::from_hci_bytes(data)?;
791        Ok((
792            Self {
793                event_kind,
794                addr_kind,
795                addr,
796                primary_adv_phy,
797                secondary_adv_phy,
798                adv_sid,
799                tx_power,
800                rssi,
801                adv_interval,
802                direct_addr_kind,
803                direct_addr,
804                data,
805            },
806            rest,
807        ))
808    }
809}
810
811param! {
812    struct LeExtAdvReports<'a> {
813        num_reports: u8,
814        bytes: RemainingBytes<'a>,
815    }
816}
817
818impl LeExtAdvReports<'_> {
819    /// Check if there are more reports available.
820    pub fn is_empty(&self) -> bool {
821        self.num_reports == 0
822    }
823
824    /// Number of advertising reports.
825    pub fn len(&self) -> usize {
826        usize::from(self.num_reports)
827    }
828
829    /// Create an iterator over the advertising reports.
830    pub fn iter(&self) -> LeExtAdvReportsIter<'_> {
831        LeExtAdvReportsIter {
832            len: self.len(),
833            bytes: &self.bytes,
834        }
835    }
836}
837
838/// An iterator for extended advertising reports.
839pub struct LeExtAdvReportsIter<'a> {
840    len: usize,
841    bytes: &'a [u8],
842}
843
844impl<'a> Iterator for LeExtAdvReportsIter<'a> {
845    type Item = Result<LeExtAdvReport<'a>, FromHciBytesError>;
846
847    fn next(&mut self) -> Option<Self::Item> {
848        if self.len == 0 {
849            None
850        } else {
851            match LeExtAdvReport::from_hci_bytes(self.bytes) {
852                Ok((report, rest)) => {
853                    self.bytes = rest;
854                    self.len -= 1;
855                    Some(Ok(report))
856                }
857                Err(err) => {
858                    self.len = 0;
859                    Some(Err(err))
860                }
861            }
862        }
863    }
864
865    fn size_hint(&self) -> (usize, Option<usize>) {
866        (self.len, Some(self.len))
867    }
868}
869
870impl ExactSizeIterator for LeExtAdvReportsIter<'_> {
871    fn len(&self) -> usize {
872        self.len
873    }
874}
875
876impl FusedIterator for LeExtAdvReportsIter<'_> {}
877
878param! {
879    struct LePeriodicAdvSubeventData<'a> {
880        subevent: u8,
881        response_slot_start: u8,
882        response_slot_count: u8,
883        subevent_data: &'a [u8],
884    }
885}
886
887impl<'a, 'b: 'a> WriteHci for &'a [LePeriodicAdvSubeventData<'b>] {
888    #[inline(always)]
889    fn size(&self) -> usize {
890        1 + self.iter().map(WriteHci::size).sum::<usize>()
891    }
892    #[inline(always)]
893    fn write_hci<W: ::embedded_io::Write>(&self, mut writer: W) -> Result<(), W::Error> {
894        writer.write_all(&[self.len() as u8])?;
895        for x in self.iter() {
896            <LePeriodicAdvSubeventData as WriteHci>::write_hci(x, &mut writer)?;
897        }
898        Ok(())
899    }
900    #[inline(always)]
901    async fn write_hci_async<W: ::embedded_io_async::Write>(&self, mut writer: W) -> Result<(), W::Error> {
902        writer.write_all(&[self.len() as u8]).await?;
903        for x in self.iter() {
904            <LePeriodicAdvSubeventData as WriteHci>::write_hci_async(x, &mut writer).await?;
905        }
906        Ok(())
907    }
908}
909
910#[allow(missing_docs)]
911fn read_n<T: ByteAlignedValue>(data: &[u8], n: usize) -> Result<(&[T], &[u8]), FromHciBytesError> {
912    let size = n * core::mem::size_of::<T>();
913    if data.len() < size {
914        return Err(FromHciBytesError::InvalidSize);
915    }
916    let (bytes, rest) = data.split_at(size);
917    let slice = unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const T, n) };
918    Ok((slice, rest))
919}
920
921param! {
922    struct LePeriodicAdvertisingResponseReport<'a> {
923        tx_power: i8,
924        rssi: i8,
925        cte_type: CteKind,
926        response_slot: u8,
927        data_status: DataStatus,
928        data_length: u8,
929        data: &'a [u8],
930    }
931}
932
933/// Container for periodic advertising response report data.
934#[derive(Debug, Clone, Hash)]
935#[cfg_attr(feature = "defmt", derive(defmt::Format))]
936pub struct LePeriodicAdvertisingResponseReports<'a> {
937    num_responses: u8,
938    tx_power: &'a [i8],
939    rssi: &'a [i8],
940    cte_type: &'a [CteKind],
941    response_slot: &'a [u8],
942    data_status: &'a [DataStatus],
943    data_length: &'a [u8],
944    data: &'a [u8],
945}
946
947impl<'a> LePeriodicAdvertisingResponseReports<'a> {
948    /// Returns `true` if there are no responses.
949    pub fn is_empty(&self) -> bool {
950        self.num_responses == 0
951    }
952
953    /// Returns the number of responses.
954    pub fn len(&self) -> usize {
955        usize::from(self.num_responses)
956    }
957
958    /// Returns the response entry at the given index, or `None` if out of bounds.
959    pub fn get(&self, index: usize) -> Option<LePeriodicAdvertisingResponseReport<'a>> {
960        if index >= self.len() {
961            return None;
962        }
963        let data_offset: usize = self.data_length[..index].iter().map(|&l| l as usize).sum();
964        let data_len = self.data_length[index] as usize;
965        Some(LePeriodicAdvertisingResponseReport {
966            tx_power: self.tx_power[index],
967            rssi: self.rssi[index],
968            cte_type: self.cte_type[index],
969            response_slot: self.response_slot[index],
970            data_status: self.data_status[index],
971            data_length: self.data_length[index],
972            data: &self.data[data_offset..data_offset + data_len],
973        })
974    }
975
976    /// Returns an iterator over all response entries.
977    pub fn iter(&self) -> LePeriodicAdvertisingResponseReportsIter<'_> {
978        LePeriodicAdvertisingResponseReportsIter {
979            reports: self,
980            index: 0,
981        }
982    }
983}
984
985impl<'de> FromHciBytes<'de> for LePeriodicAdvertisingResponseReports<'de> {
986    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), FromHciBytesError> {
987        let (num_responses, data) = u8::from_hci_bytes(data)?;
988        let n = num_responses as usize;
989
990        let (tx_power, data) = read_n::<i8>(data, n)?;
991        let (rssi, data) = read_n::<i8>(data, n)?;
992        let (cte_type, data) = read_n::<CteKind>(data, n)?;
993        let (response_slot, data) = read_n::<u8>(data, n)?;
994        let (data_status, data) = read_n::<DataStatus>(data, n)?;
995        let (data_length, data) = read_n::<u8>(data, n)?;
996
997        Ok((
998            Self {
999                num_responses,
1000                tx_power,
1001                rssi,
1002                cte_type,
1003                response_slot,
1004                data_status,
1005                data_length,
1006                data,
1007            },
1008            &[],
1009        ))
1010    }
1011}
1012
1013/// An iterator over the LePeriodicAdvertisingResponse reports.
1014pub struct LePeriodicAdvertisingResponseReportsIter<'a> {
1015    reports: &'a LePeriodicAdvertisingResponseReports<'a>,
1016    index: usize,
1017}
1018
1019impl<'a> Iterator for LePeriodicAdvertisingResponseReportsIter<'a> {
1020    type Item = LePeriodicAdvertisingResponseReport<'a>;
1021
1022    fn next(&mut self) -> Option<Self::Item> {
1023        let entry = self.reports.get(self.index)?;
1024        self.index += 1;
1025        Some(entry)
1026    }
1027
1028    fn size_hint(&self) -> (usize, Option<usize>) {
1029        let remaining = self.reports.len() - self.index;
1030        (remaining, Some(remaining))
1031    }
1032}
1033
1034impl ExactSizeIterator for LePeriodicAdvertisingResponseReportsIter<'_> {
1035    fn len(&self) -> usize {
1036        self.reports.len() - self.index
1037    }
1038}
1039
1040impl FusedIterator for LePeriodicAdvertisingResponseReportsIter<'_> {}
1041
1042param! {
1043    struct LeCsSubeventStepEntry<'a> {
1044        step_mode: u8,
1045        step_channel: u8,
1046        step_data_length: u8,
1047        step_data: &'a [u8],
1048    }
1049}
1050
1051/// Container for CS subevent step data.
1052///
1053/// Parses the column-major wire format:
1054/// `num_steps_reported | step_mode[] | step_channel[] | step_data_length[] | step_data[]`
1055///
1056/// Entries are accessed via [`get`](LeCsSubeventStepData::get)
1057/// or [`iter`](LeCsSubeventStepData::iter).
1058#[derive(Debug, Clone, Hash)]
1059#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1060pub struct LeCsSubeventStepData<'a> {
1061    num_steps_reported: u8,
1062    step_mode: &'a [u8],
1063    step_channel: &'a [u8],
1064    step_data_length: &'a [u8],
1065    step_data: &'a [u8],
1066}
1067
1068impl<'a> LeCsSubeventStepData<'a> {
1069    /// Returns `true` if there are no steps.
1070    pub fn is_empty(&self) -> bool {
1071        self.num_steps_reported == 0
1072    }
1073
1074    /// Returns the number of steps.
1075    pub fn len(&self) -> usize {
1076        usize::from(self.num_steps_reported)
1077    }
1078
1079    /// Returns the step entry at the given index, or `None` if out of bounds.
1080    pub fn get(&self, index: usize) -> Option<LeCsSubeventStepEntry<'a>> {
1081        if index >= self.len() {
1082            return None;
1083        }
1084        let data_offset: usize = self.step_data_length[..index].iter().map(|&l| l as usize).sum();
1085        let data_len = self.step_data_length[index] as usize;
1086        Some(LeCsSubeventStepEntry {
1087            step_mode: self.step_mode[index],
1088            step_channel: self.step_channel[index],
1089            step_data_length: self.step_data_length[index],
1090            step_data: &self.step_data[data_offset..data_offset + data_len],
1091        })
1092    }
1093
1094    /// Returns an iterator over all step entries.
1095    pub fn iter(&self) -> LeCsSubeventStepDataIter<'_> {
1096        LeCsSubeventStepDataIter { data: self, index: 0 }
1097    }
1098}
1099
1100impl<'de> FromHciBytes<'de> for LeCsSubeventStepData<'de> {
1101    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), FromHciBytesError> {
1102        let (num_steps_reported, data) = u8::from_hci_bytes(data)?;
1103        let n = num_steps_reported as usize;
1104
1105        let (step_mode, data) = read_n::<u8>(data, n)?;
1106        let (step_channel, data) = read_n::<u8>(data, n)?;
1107        let (step_data_length, data) = read_n::<u8>(data, n)?;
1108
1109        Ok((
1110            Self {
1111                num_steps_reported,
1112                step_mode,
1113                step_channel,
1114                step_data_length,
1115                step_data: data,
1116            },
1117            &[],
1118        ))
1119    }
1120}
1121
1122/// An iterator over LeCsSubeventStepEntry values.
1123pub struct LeCsSubeventStepDataIter<'a> {
1124    data: &'a LeCsSubeventStepData<'a>,
1125    index: usize,
1126}
1127
1128impl<'a> Iterator for LeCsSubeventStepDataIter<'a> {
1129    type Item = LeCsSubeventStepEntry<'a>;
1130
1131    fn next(&mut self) -> Option<Self::Item> {
1132        let entry = self.data.get(self.index)?;
1133        self.index += 1;
1134        Some(entry)
1135    }
1136
1137    fn size_hint(&self) -> (usize, Option<usize>) {
1138        let remaining = self.data.len() - self.index;
1139        (remaining, Some(remaining))
1140    }
1141}
1142
1143impl ExactSizeIterator for LeCsSubeventStepDataIter<'_> {
1144    fn len(&self) -> usize {
1145        self.data.len() - self.index
1146    }
1147}
1148
1149impl FusedIterator for LeCsSubeventStepDataIter<'_> {}
1150
1151param! {
1152    #[derive(Default)]
1153    enum DoneStatus {
1154        #[default]
1155        Complete = 0,
1156        Partial = 1,
1157        Aborted = 0xf,
1158    }
1159}
1160
1161/// Procedure abort reason.
1162#[repr(u8)]
1163#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1164#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1165pub enum ProcedureAbortReason {
1166    #[default]
1167    /// Report with no abort.
1168    NoAbort = 0x0,
1169    /// Abort because of local Host or remote request.
1170    HostRequest = 0x1,
1171    /// Abort because filtered channel map has less than 15 channels.
1172    FilteredChannelMap = 0x2,
1173    /// Abort because the channel map update instant has passed.
1174    ChannelMapInstantPassed = 0x3,
1175    /// Abort because of unspecified reasons.
1176    Unspecified = 0xf,
1177}
1178
1179impl From<u8> for ProcedureAbortReason {
1180    fn from(v: u8) -> Self {
1181        match v {
1182            0x0 => Self::NoAbort,
1183            0x1 => Self::HostRequest,
1184            0x2 => Self::FilteredChannelMap,
1185            0x3 => Self::ChannelMapInstantPassed,
1186            _ => Self::Unspecified,
1187        }
1188    }
1189}
1190
1191/// Subevent abort reason.
1192#[repr(u8)]
1193#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1194#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1195pub enum SubeventAbortReason {
1196    #[default]
1197    /// Report with no abort.
1198    NoAbort = 0x0,
1199    /// Abort because of local Host or remote request.
1200    HostRequest = 0x1,
1201    /// Abort because no CS_SYNC (mode-0) received.
1202    NoCsSync = 0x2,
1203    /// Abort because of scheduling conflicts or limited resources.
1204    SchedulingConflict = 0x3,
1205    /// Abort because of unspecified reasons.
1206    Unspecified = 0xf,
1207}
1208
1209impl From<u8> for SubeventAbortReason {
1210    fn from(v: u8) -> Self {
1211        match v {
1212            0x0 => Self::NoAbort,
1213            0x1 => Self::HostRequest,
1214            0x2 => Self::NoCsSync,
1215            0x3 => Self::SchedulingConflict,
1216            _ => Self::Unspecified,
1217        }
1218    }
1219}
1220
1221/// Abort reason for CS subevent result, packed as two 4-bit nibbles.
1222///
1223/// Bits 0-3: procedure abort reason ([`ProcedureAbortReason`])
1224/// Bits 4-7: subevent abort reason ([`SubeventAbortReason`])
1225#[repr(transparent)]
1226#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1227#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1228pub struct PackedAbortReasons(u8);
1229
1230impl PackedAbortReasons {
1231    /// Returns the procedure-level abort reason.
1232    pub fn procedure_reason(&self) -> ProcedureAbortReason {
1233        ProcedureAbortReason::from(self.0 & 0x0f)
1234    }
1235
1236    /// Returns the subevent-level abort reason.
1237    pub fn subevent_reason(&self) -> SubeventAbortReason {
1238        SubeventAbortReason::from((self.0 >> 4) & 0x0f)
1239    }
1240}
1241
1242unsafe impl FixedSizeValue for PackedAbortReasons {
1243    fn is_valid(_data: &[u8]) -> bool {
1244        true
1245    }
1246}
1247
1248/// Frequency compensation value in units of 0.01 ppm.
1249#[repr(transparent)]
1250#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1251#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1252pub struct FrequencyCompensation(u16);
1253
1254impl FrequencyCompensation {
1255    /// Returns the raw 16-bit value.
1256    pub fn as_raw(&self) -> u16 {
1257        self.0
1258    }
1259
1260    /// Returns `true` if the value is available.
1261    pub fn is_available(&self) -> bool {
1262        self.0 != 0xC000
1263    }
1264
1265    /// Returns the frequency compensation in 0.01 ppm units, or `None` if not available.
1266    pub fn as_ppm_x100(&self) -> Option<i16> {
1267        if !self.is_available() {
1268            return None;
1269        }
1270        let val = self.0 & 0x7FFF;
1271        Some(((val << 1) as i16) >> 1)
1272    }
1273}
1274
1275unsafe impl FixedSizeValue for FrequencyCompensation {
1276    fn is_valid(_data: &[u8]) -> bool {
1277        true
1278    }
1279}
1280
1281// ============================================================================
1282// LE Audio / Isochronous parameters (Bluetooth Core Specification v5.4+)
1283// ============================================================================
1284
1285param!(
1286    /// CIG ID (0x00 – 0xEF)
1287    struct CigId(u8)
1288);
1289
1290#[allow(missing_docs)]
1291impl CigId {
1292    /// Create a new instance.
1293    pub const fn new(v: u8) -> Self {
1294        Self(v)
1295    }
1296
1297    /// Get the inner representation.
1298    pub fn as_raw(&self) -> u8 {
1299        self.0
1300    }
1301}
1302
1303unsafe impl ByteAlignedValue for CigId {}
1304
1305impl<'de> crate::FromHciBytes<'de> for &'de CigId {
1306    #[inline(always)]
1307    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1308        <CigId as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1309    }
1310}
1311
1312param!(
1313    /// CIS ID (0x00 – 0xEF)
1314    struct CisId(u8)
1315);
1316
1317#[allow(missing_docs)]
1318impl CisId {
1319    /// Create a new instance.
1320    pub const fn new(v: u8) -> Self {
1321        Self(v)
1322    }
1323
1324    /// Get the inner representation.
1325    pub fn as_raw(&self) -> u8 {
1326        self.0
1327    }
1328}
1329
1330unsafe impl ByteAlignedValue for CisId {}
1331
1332impl<'de> crate::FromHciBytes<'de> for &'de CisId {
1333    #[inline(always)]
1334    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1335        <CisId as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1336    }
1337}
1338
1339param! {
1340    /// Per-CIS configuration for `LE Set CIG Parameters`.
1341    struct CisConfig {
1342        cis_id: CisId,
1343        max_sdu_c_to_p: u16,
1344        max_sdu_p_to_c: u16,
1345        phy_c_to_p: PhyMask,
1346        phy_p_to_c: PhyMask,
1347        rtn_c_to_p: u8,
1348        rtn_p_to_c: u8,
1349    }
1350}
1351
1352param_slice!(&'a [CisConfig]);
1353
1354param! {
1355    /// Per-CIS configuration for `LE Set CIG Parameters Test`.
1356    struct CisConfigTest {
1357        cis_id: CisId,
1358        max_sdu_c_to_p: u16,
1359        max_sdu_p_to_c: u16,
1360        max_pdu_c_to_p: u16,
1361        max_pdu_p_to_c: u16,
1362        phy_c_to_p: PhyMask,
1363        phy_p_to_c: PhyMask,
1364        bn_c_to_p: u8,
1365        bn_p_to_c: u8,
1366    }
1367}
1368
1369param_slice!(&'a [CisConfigTest]);
1370
1371param! {
1372    /// CIS-to-ACL handle mapping for `LE Create CIS`.
1373    struct CisConnConfig {
1374        cis_handle: ConnHandle,
1375        acl_handle: ConnHandle,
1376    }
1377}
1378
1379param_slice!(&'a [CisConnConfig]);
1380
1381param! {
1382    /// Data path direction.
1383    #[derive(Default)]
1384    enum DataPathDirection {
1385        #[default]
1386        Input = 0,
1387        Output = 1,
1388    }
1389}
1390
1391param!(
1392    /// Data path identifier.
1393    ///
1394    /// - `0x00` = HCI
1395    /// - `0x01`–`0xFE` = Logical channel number (vendor-specific)
1396    /// - `0xFF` = Audio test mode
1397    struct DataPathId(u8)
1398);
1399
1400#[allow(missing_docs)]
1401impl DataPathId {
1402    /// HCI data path.
1403    pub const HCI: DataPathId = DataPathId(0x00);
1404    /// Audio test mode.
1405    pub const AUDIO_TEST_MODE: DataPathId = DataPathId(0xFF);
1406
1407    /// Create a new instance.
1408    pub const fn new(v: u8) -> Self {
1409        Self(v)
1410    }
1411
1412    /// Get the inner representation.
1413    pub fn as_raw(&self) -> u8 {
1414        self.0
1415    }
1416}
1417
1418unsafe impl ByteAlignedValue for DataPathId {}
1419
1420impl<'de> crate::FromHciBytes<'de> for &'de DataPathId {
1421    #[inline(always)]
1422    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1423        <DataPathId as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1424    }
1425}
1426
1427param! {
1428    /// Codec ID (5 octets).
1429    struct CodecId {
1430        coding_format: u8,
1431        company_id: u16,
1432        vendor_specific_codec_id: u16,
1433    }
1434}
1435
1436param!(
1437    /// Broadcast code (16 octets).
1438    struct BroadcastCode([u8; 16])
1439);
1440
1441#[allow(missing_docs)]
1442impl BroadcastCode {
1443    /// Create a new instance.
1444    pub const fn new(v: [u8; 16]) -> Self {
1445        Self(v)
1446    }
1447
1448    /// Get the byte representation.
1449    pub fn raw(&self) -> &[u8] {
1450        &self.0[..]
1451    }
1452}
1453
1454unsafe impl ByteAlignedValue for BroadcastCode {}
1455
1456impl<'de> crate::FromHciBytes<'de> for &'de BroadcastCode {
1457    #[inline(always)]
1458    fn from_hci_bytes(data: &'de [u8]) -> Result<(Self, &'de [u8]), crate::FromHciBytesError> {
1459        <BroadcastCode as crate::ByteAlignedValue>::ref_from_hci_bytes(data)
1460    }
1461}
1462
1463param! {
1464    /// BIG encryption mode.
1465    #[derive(Default)]
1466    enum EncryptionMode {
1467        #[default]
1468        Unencrypted = 0,
1469        Encrypted = 1,
1470    }
1471}
1472
1473param! {
1474    /// CIS/BIG packing method.
1475    #[derive(Default)]
1476    enum Packing {
1477        #[default]
1478        Sequential = 0,
1479        Interleaved = 1,
1480    }
1481}
1482
1483param! {
1484    /// CIS/BIG framing mode.
1485    #[derive(Default)]
1486    enum Framing {
1487        #[default]
1488        Unframed = 0,
1489        Framed = 1,
1490    }
1491}
1492
1493param! {
1494    /// ISO test payload type.
1495    #[derive(Default)]
1496    enum PayloadType {
1497        #[default]
1498        ZeroLength = 0,
1499        VariableLength = 1,
1500        MaximumLength = 2,
1501    }
1502}
1503
1504#[cfg(test)]
1505mod tests {
1506    use super::*;
1507
1508    #[test]
1509    fn test_ext_adv_event_kind() {
1510        let k = LeExtAdvEventKind::new().set_connectable(true);
1511        assert_eq!(k.0[0], 0b0000001);
1512        let k = k.set_data_status(LeExtAdvDataStatus::Complete);
1513        assert_eq!(k.0[0], 0b0000001);
1514        let k = k.set_data_status(LeExtAdvDataStatus::IncompleteMoreExpected);
1515        assert_eq!(k.0[0], 0b0100001);
1516        let k = k.set_data_status(LeExtAdvDataStatus::IncompleteTruncated);
1517        assert_eq!(k.0[0], 0b1000001);
1518    }
1519
1520    #[test]
1521    fn test_channel_map_new() {
1522        let m = ChannelMap::new();
1523        for chan in 0..37 {
1524            assert!(!m.is_channel_bad(chan));
1525        }
1526
1527        for chan in 37..40 {
1528            assert!(m.is_channel_bad(chan));
1529        }
1530    }
1531
1532    #[test]
1533    fn test_frequency_compensation_positive() {
1534        let fc = FrequencyCompensation(0x2710);
1535        assert!(fc.is_available());
1536        assert_eq!(fc.as_ppm_x100(), Some(10000));
1537        assert_eq!(fc.as_raw(), 0x2710);
1538    }
1539
1540    #[test]
1541    fn test_frequency_compensation_negative() {
1542        let fc = FrequencyCompensation(0x58F0);
1543        assert!(fc.is_available());
1544        assert_eq!(fc.as_ppm_x100(), Some(-10000));
1545        assert_eq!(fc.as_raw(), 0x58F0);
1546    }
1547
1548    #[test]
1549    fn test_frequency_compensation_not_available() {
1550        let fc = FrequencyCompensation(0xC000);
1551        assert!(!fc.is_available());
1552        assert_eq!(fc.as_ppm_x100(), None);
1553        assert_eq!(fc.as_raw(), 0xC000);
1554    }
1555}