Skip to main content

trouble_host/
advertise.rs

1//! Advertisement config.
2use bt_hci::param::{AddrKind, AdvEventProps, BdAddr};
3pub use bt_hci::param::{AdvChannelMap, AdvFilterPolicy, AdvHandle, AdvSet, PhyKind};
4use embassy_time::Duration;
5
6use crate::cursor::{ReadCursor, WriteCursor};
7use crate::types::uuid::Uuid;
8use crate::{bt_hci_duration, codec, Address};
9
10/// Transmit power levels.
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12#[derive(Eq, PartialEq, Copy, Clone, Debug)]
13#[repr(i8)]
14#[allow(missing_docs)]
15pub enum TxPower {
16    Minus40dBm = -40,
17    Minus20dBm = -20,
18    Minus16dBm = -16,
19    Minus12dBm = -12,
20    Minus8dBm = -8,
21    Minus4dBm = -4,
22    ZerodBm = 0,
23    Plus2dBm = 2,
24    Plus3dBm = 3,
25    Plus4dBm = 4,
26    Plus5dBm = 5,
27    Plus6dBm = 6,
28    Plus7dBm = 7,
29    Plus8dBm = 8,
30    Plus10dBm = 10,
31    Plus12dBm = 12,
32    Plus14dBm = 14,
33    Plus16dBm = 16,
34    Plus18dBm = 18,
35    Plus20dBm = 20,
36}
37
38/// Configuriation for a single advertisement set.
39#[derive(Debug, Clone)]
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41pub struct AdvertisementSet<'d> {
42    /// Parameters for the advertisement.
43    pub params: AdvertisementParameters,
44    /// Advertisement data.
45    pub data: Advertisement<'d>,
46    /// Override the random address for this advertising set.
47    /// `None` = use host default address.
48    pub address: Option<BdAddr>,
49}
50
51impl<'d> AdvertisementSet<'d> {
52    /// Create a new advertisement set that can be passed to advertisement functions.
53    pub fn handles<const N: usize>(sets: &[AdvertisementSet<'d>; N]) -> [AdvSet; N] {
54        const NEW_SET: AdvSet = AdvSet {
55            adv_handle: AdvHandle::new(0),
56            duration: bt_hci::param::Duration::from_u16(0),
57            max_ext_adv_events: 0,
58        };
59
60        let mut ret = [NEW_SET; N];
61        for (i, set) in sets.iter().enumerate() {
62            ret[i].adv_handle = AdvHandle::new(i as u8);
63            ret[i].duration = bt_hci_duration(set.params.timeout.unwrap_or(embassy_time::Duration::from_micros(0)));
64            ret[i].max_ext_adv_events = set.params.max_events.unwrap_or(0);
65        }
66        ret
67    }
68}
69
70/// Parameters for an advertisement.
71#[cfg_attr(feature = "defmt", derive(defmt::Format))]
72#[derive(Copy, Clone, Debug)]
73pub struct AdvertisementParameters {
74    /// Phy selection
75    pub primary_phy: PhyKind,
76
77    /// Secondary phy selection
78    pub secondary_phy: PhyKind,
79
80    /// Transmission power
81    pub tx_power: TxPower,
82
83    /// Timeout duration
84    pub timeout: Option<Duration>,
85
86    /// Max advertising events
87    pub max_events: Option<u8>,
88
89    /// Minimum advertising interval
90    pub interval_min: Duration,
91
92    /// Maximum advertising interval
93    pub interval_max: Duration,
94
95    /// Which advertising channels to use
96    pub channel_map: Option<AdvChannelMap>,
97
98    /// Filtering policy
99    pub filter_policy: AdvFilterPolicy,
100
101    /// Fragmentation preference
102    pub fragment: bool,
103
104    /// Override the own address type for this advertising set.
105    /// `None` = use host default (derived from host address + privacy state).
106    pub own_addr_kind: Option<AddrKind>,
107}
108
109impl Default for AdvertisementParameters {
110    fn default() -> Self {
111        Self {
112            primary_phy: PhyKind::Le1M,
113            secondary_phy: PhyKind::Le1M,
114            tx_power: TxPower::ZerodBm,
115            timeout: None,
116            max_events: None,
117            interval_min: Duration::from_millis(160),
118            interval_max: Duration::from_millis(160),
119            filter_policy: AdvFilterPolicy::default(),
120            channel_map: None,
121            fragment: false,
122            own_addr_kind: None,
123        }
124    }
125}
126
127#[derive(Debug, Clone, Copy)]
128#[cfg_attr(feature = "defmt", derive(defmt::Format))]
129pub(crate) struct RawAdvertisement<'d> {
130    pub(crate) props: AdvEventProps,
131    pub(crate) adv_data: &'d [u8],
132    pub(crate) scan_data: &'d [u8],
133    pub(crate) peer: Option<Address>,
134}
135
136impl Default for RawAdvertisement<'_> {
137    fn default() -> Self {
138        Self {
139            props: AdvEventProps::new()
140                .set_connectable_adv(true)
141                .set_scannable_adv(true)
142                .set_legacy_adv(true),
143            adv_data: &[],
144            scan_data: &[],
145            peer: None,
146        }
147    }
148}
149
150/// Advertisement payload depending on which advertisement kind requested.
151#[derive(Debug, Clone, Copy)]
152#[cfg_attr(feature = "defmt", derive(defmt::Format))]
153pub enum Advertisement<'d> {
154    /// Connectable and scannable undirected advertisement.
155    ConnectableScannableUndirected {
156        /// Advertisement data.
157        adv_data: &'d [u8],
158        /// Scan data.
159        scan_data: &'d [u8],
160    },
161    /// Connectable and non-scannable directed advertisement.
162    ConnectableNonscannableDirected {
163        /// Address of the peer to direct the advertisement to.
164        peer: Address,
165    },
166    /// Connectable and non-scannable directed advertisement with high duty cycle.
167    ConnectableNonscannableDirectedHighDuty {
168        /// Address of the peer to direct the advertisement to.
169        peer: Address,
170    },
171    /// Nonconnectable and scannable undirected advertisement.
172    NonconnectableScannableUndirected {
173        /// Advertisement data.
174        adv_data: &'d [u8],
175        /// Scan data.
176        scan_data: &'d [u8],
177    },
178    /// Nonconnectable and nonscannable undirected advertisement.
179    NonconnectableNonscannableUndirected {
180        /// Advertisement data.
181        adv_data: &'d [u8],
182    },
183    /// Extended connectable and non-scannable undirected advertisement.
184    ExtConnectableNonscannableUndirected {
185        /// Advertisement data.
186        adv_data: &'d [u8],
187    },
188    /// Extended connectable and non-scannable directed advertisement.
189    ExtConnectableNonscannableDirected {
190        /// Address of the peer to direct the advertisement to.
191        peer: Address,
192        /// Advertisement data.
193        adv_data: &'d [u8],
194    },
195    /// Extended nonconnectable and scannable undirected advertisement.
196    ExtNonconnectableScannableUndirected {
197        /// Scan data.
198        scan_data: &'d [u8],
199    },
200    /// Extended nonconnectable and scannable directed advertisement.
201    ExtNonconnectableScannableDirected {
202        /// Address of the peer to direct the advertisement to.
203        peer: Address,
204        /// Scan data.
205        scan_data: &'d [u8],
206    },
207    /// Extended nonconnectable and nonscannable undirected advertisement.
208    ExtNonconnectableNonscannableUndirected {
209        /// Whether the advertisement is anonymous.
210        anonymous: bool,
211        /// Advertisement data.
212        adv_data: &'d [u8],
213    },
214    /// Extended nonconnectable and nonscannable directed advertisement.
215    ExtNonconnectableNonscannableDirected {
216        /// Whether the advertisement is anonymous.
217        anonymous: bool,
218        /// Address of the peer to direct the advertisement to.
219        peer: Address,
220        /// Advertisement data.
221        adv_data: &'d [u8],
222    },
223}
224
225impl<'d> Advertisement<'d> {
226    pub(crate) fn is_valid(&self) -> bool {
227        match self {
228            Advertisement::ConnectableScannableUndirected { adv_data, .. }
229            | Advertisement::ExtConnectableNonscannableUndirected { adv_data }
230            | Advertisement::ExtConnectableNonscannableDirected { adv_data, .. } => {
231                // Connectable advertisements must have flags as the first advertisement data with the LE Only flag set
232                adv_data.len() >= 3 && adv_data[0] == 2 && adv_data[1] == 1 && (adv_data[2] & BR_EDR_NOT_SUPPORTED) != 0
233            }
234            _ => true,
235        }
236    }
237}
238impl<'d> From<Advertisement<'d>> for RawAdvertisement<'d> {
239    fn from(val: Advertisement<'d>) -> RawAdvertisement<'d> {
240        match val {
241            Advertisement::ConnectableScannableUndirected { adv_data, scan_data } => RawAdvertisement {
242                props: AdvEventProps::new()
243                    .set_connectable_adv(true)
244                    .set_scannable_adv(true)
245                    .set_anonymous_adv(false)
246                    .set_legacy_adv(true),
247                adv_data,
248                scan_data,
249                peer: None,
250            },
251            Advertisement::ConnectableNonscannableDirected { peer } => RawAdvertisement {
252                props: AdvEventProps::new()
253                    .set_connectable_adv(true)
254                    .set_scannable_adv(false)
255                    .set_directed_adv(true)
256                    .set_anonymous_adv(false)
257                    .set_legacy_adv(true),
258                adv_data: &[],
259                scan_data: &[],
260                peer: Some(peer),
261            },
262            Advertisement::ConnectableNonscannableDirectedHighDuty { peer } => RawAdvertisement {
263                props: AdvEventProps::new()
264                    .set_connectable_adv(true)
265                    .set_scannable_adv(false)
266                    .set_high_duty_cycle_directed_connectable_adv(true)
267                    .set_anonymous_adv(false)
268                    .set_legacy_adv(true),
269                adv_data: &[],
270                scan_data: &[],
271                peer: Some(peer),
272            },
273            Advertisement::NonconnectableScannableUndirected { adv_data, scan_data } => RawAdvertisement {
274                props: AdvEventProps::new()
275                    .set_connectable_adv(false)
276                    .set_scannable_adv(true)
277                    .set_anonymous_adv(false)
278                    .set_legacy_adv(true),
279                adv_data,
280                scan_data,
281                peer: None,
282            },
283            Advertisement::NonconnectableNonscannableUndirected { adv_data } => RawAdvertisement {
284                props: AdvEventProps::new()
285                    .set_connectable_adv(false)
286                    .set_scannable_adv(false)
287                    .set_anonymous_adv(false)
288                    .set_legacy_adv(true),
289                adv_data,
290                scan_data: &[],
291                peer: None,
292            },
293            Advertisement::ExtConnectableNonscannableUndirected { adv_data } => RawAdvertisement {
294                props: AdvEventProps::new().set_connectable_adv(true).set_scannable_adv(false),
295                adv_data,
296                scan_data: &[],
297                peer: None,
298            },
299            Advertisement::ExtConnectableNonscannableDirected { adv_data, peer } => RawAdvertisement {
300                props: AdvEventProps::new().set_connectable_adv(true).set_scannable_adv(false),
301                adv_data,
302                scan_data: &[],
303                peer: Some(peer),
304            },
305
306            Advertisement::ExtNonconnectableScannableUndirected { scan_data } => RawAdvertisement {
307                props: AdvEventProps::new().set_connectable_adv(false).set_scannable_adv(true),
308                adv_data: &[],
309                scan_data,
310                peer: None,
311            },
312            Advertisement::ExtNonconnectableScannableDirected { scan_data, peer } => RawAdvertisement {
313                props: AdvEventProps::new()
314                    .set_connectable_adv(false)
315                    .set_scannable_adv(true)
316                    .set_directed_adv(true),
317                adv_data: &[],
318                scan_data,
319                peer: Some(peer),
320            },
321            Advertisement::ExtNonconnectableNonscannableUndirected { adv_data, anonymous } => RawAdvertisement {
322                props: AdvEventProps::new()
323                    .set_connectable_adv(false)
324                    .set_scannable_adv(false)
325                    .set_anonymous_adv(anonymous)
326                    .set_directed_adv(false),
327                adv_data,
328                scan_data: &[],
329                peer: None,
330            },
331            Advertisement::ExtNonconnectableNonscannableDirected {
332                adv_data,
333                peer,
334                anonymous,
335            } => RawAdvertisement {
336                props: AdvEventProps::new()
337                    .set_connectable_adv(false)
338                    .set_scannable_adv(false)
339                    .set_anonymous_adv(anonymous)
340                    .set_directed_adv(true),
341                adv_data,
342                scan_data: &[],
343                peer: Some(peer),
344            },
345        }
346    }
347}
348
349/// Le advertisement.
350pub const AD_FLAG_LE_LIMITED_DISCOVERABLE: u8 = 0b00000001;
351
352/// Discoverable flag.
353pub const LE_GENERAL_DISCOVERABLE: u8 = 0b00000010;
354
355/// BR/EDR not supported.
356pub const BR_EDR_NOT_SUPPORTED: u8 = 0b00000100;
357
358/// Simultaneous LE and BR/EDR to same device capable (controller).
359pub const SIMUL_LE_BR_CONTROLLER: u8 = 0b00001000;
360
361/// Simultaneous LE and BR/EDR to same device capable (Host).
362pub const SIMUL_LE_BR_HOST: u8 = 0b00010000;
363
364/// Error encoding advertisement data.
365#[derive(Debug, Copy, Clone, PartialEq)]
366#[cfg_attr(feature = "defmt", derive(defmt::Format))]
367pub enum AdvertisementDataError {
368    /// Advertisement data too long for buffer.
369    TooLong,
370}
371
372/// Advertisement data structure.
373#[derive(Debug, Copy, Clone)]
374#[cfg_attr(feature = "defmt", derive(defmt::Format))]
375pub enum AdStructure<'a> {
376    /// Device flags and baseband capabilities.
377    ///
378    /// This should be sent if any flags apply to the device. If not (ie. the value sent would be
379    /// 0), this may be omitted.
380    ///
381    /// Must not be used in scan response data.
382    Flags(u8),
383
384    /// Incomplete List of 16-bit service UUIDs.
385    /// The UUID data matches the ble network's endian order (should be little endian).
386    IncompleteServiceUuids16(&'a [[u8; 2]]),
387
388    /// Complete List of 16-bit service UUIDs.
389    /// The UUID data matches the ble network's endian order (should be little endian).
390    CompleteServiceUuids16(&'a [[u8; 2]]),
391
392    /// Incomplete List of 32-bit service UUIDs.
393    /// The UUID data matches the ble network's endian order (should be little endian).
394    IncompleteServiceUuids32(&'a [[u8; 4]]),
395
396    /// Complete List of 32-bit service UUIDs.
397    /// The UUID data matches the ble network's endian order (should be little endian).
398    CompleteServiceUuids32(&'a [[u8; 4]]),
399
400    /// Incomplete List of 128-bit service UUIDs.
401    /// The UUID data matches the ble network's endian order (should be little endian).
402    IncompleteServiceUuids128(&'a [[u8; 16]]),
403
404    /// Complete List of 128-bit service UUIDs.
405    /// The UUID data matches the ble network's endian order (should be little endian).
406    CompleteServiceUuids128(&'a [[u8; 16]]),
407
408    /// Sets the Tx Power Level in dBm.
409    TxPowerLevel(i8),
410
411    /// Service data with 16-bit service UUID.
412    /// The UUID data matches the ble network's endian order (should be little endian).
413    ServiceData16 {
414        /// The 16-bit service UUID.
415        uuid: [u8; 2],
416        /// The associated service data. May be empty.
417        data: &'a [u8],
418    },
419
420    /// Sets the full (unabbreviated) device name.
421    ///
422    /// This will be shown to the user when this device is found.
423    CompleteLocalName(&'a [u8]),
424
425    /// Sets the shortened device name.
426    ShortenedLocalName(&'a [u8]),
427
428    /// Set manufacturer specific data
429    ManufacturerSpecificData {
430        /// Company identifier.
431        company_identifier: u16,
432        /// Payload data.
433        payload: &'a [u8],
434    },
435
436    /// An unknown or unimplemented AD structure stored as raw bytes.
437    Unknown {
438        /// Type byte.
439        ty: u8,
440        /// Raw data transmitted after the type.
441        data: &'a [u8],
442    },
443}
444
445impl AdStructure<'_> {
446    /// Encode a slice of advertisement structures into a buffer.
447    pub fn encode_slice(data: &[AdStructure<'_>], dest: &mut [u8]) -> Result<usize, codec::Error> {
448        let mut w = WriteCursor::new(dest);
449        for item in data.iter() {
450            item.encode(&mut w)?;
451        }
452        Ok(w.len())
453    }
454
455    pub(crate) fn encode(&self, w: &mut WriteCursor<'_>) -> Result<(), codec::Error> {
456        match self {
457            AdStructure::Flags(flags) => {
458                w.append(&[0x02, 0x01, *flags])?;
459            }
460            AdStructure::IncompleteServiceUuids16(uuids) => {
461                w.append(&[(uuids.len() * 2 + 1) as u8, 0x02])?;
462                for uuid in uuids.iter() {
463                    w.write_ref(&Uuid::Uuid16(*uuid))?;
464                }
465            }
466            AdStructure::CompleteServiceUuids16(uuids) => {
467                w.append(&[(uuids.len() * 2 + 1) as u8, 0x03])?;
468                for uuid in uuids.iter() {
469                    w.write_ref(&Uuid::Uuid16(*uuid))?;
470                }
471            }
472            AdStructure::IncompleteServiceUuids32(uuids) => {
473                w.append(&[(uuids.len() * 4 + 1) as u8, 0x04])?;
474                for uuid in uuids.iter() {
475                    w.write_ref(&Uuid::Uuid32(*uuid))?;
476                }
477            }
478            AdStructure::CompleteServiceUuids32(uuids) => {
479                w.append(&[(uuids.len() * 4 + 1) as u8, 0x05])?;
480                for uuid in uuids.iter() {
481                    w.write_ref(&Uuid::Uuid32(*uuid))?;
482                }
483            }
484            AdStructure::IncompleteServiceUuids128(uuids) => {
485                w.append(&[(uuids.len() * 16 + 1) as u8, 0x06])?;
486                for uuid in uuids.iter() {
487                    w.write_ref(&Uuid::Uuid128(*uuid))?;
488                }
489            }
490            AdStructure::CompleteServiceUuids128(uuids) => {
491                w.append(&[(uuids.len() * 16 + 1) as u8, 0x07])?;
492                for uuid in uuids.iter() {
493                    w.write_ref(&Uuid::Uuid128(*uuid))?;
494                }
495            }
496            AdStructure::ShortenedLocalName(name) => {
497                w.append(&[(name.len() + 1) as u8, 0x08])?;
498                w.append(name)?;
499            }
500            AdStructure::CompleteLocalName(name) => {
501                w.append(&[(name.len() + 1) as u8, 0x09])?;
502                w.append(name)?;
503            }
504            AdStructure::TxPowerLevel(power) => {
505                w.append(&[0x02, 0x0a, *power as u8])?;
506            }
507            AdStructure::ServiceData16 { uuid, data } => {
508                w.append(&[(data.len() + 3) as u8, 0x16])?;
509                w.write(Uuid::Uuid16(*uuid))?;
510                w.append(data)?;
511            }
512            AdStructure::ManufacturerSpecificData {
513                company_identifier,
514                payload,
515            } => {
516                w.append(&[(payload.len() + 3) as u8, 0xff])?;
517                w.write(*company_identifier)?;
518                w.append(payload)?;
519            }
520            AdStructure::Unknown { ty, data } => {
521                w.append(&[(data.len() + 1) as u8, *ty])?;
522                w.append(data)?;
523            }
524        }
525        Ok(())
526    }
527
528    /// Decode a slice of advertisement structures from a buffer.
529    pub fn decode(data: &[u8]) -> impl Iterator<Item = Result<AdStructure<'_>, codec::Error>> {
530        AdStructureIter {
531            cursor: ReadCursor::new(data),
532        }
533    }
534}
535
536/// Iterator over advertisement structures.
537pub struct AdStructureIter<'d> {
538    cursor: ReadCursor<'d>,
539}
540
541impl<'d> AdStructureIter<'d> {
542    fn read(&mut self) -> Result<AdStructure<'d>, codec::Error> {
543        let len: u8 = self.cursor.read()?;
544        if len < 2 {
545            return Err(codec::Error::InvalidValue);
546        }
547        let code: u8 = self.cursor.read()?;
548        let data = self.cursor.slice(len as usize - 1)?;
549        // These codes correspond to the table in 2.3 Common Data Types table `assigned_numbers/core/ad_types.yaml` from <https://www.bluetooth.com/specifications/assigned-numbers/>
550        // Look there for more information on each.
551        match code {
552            // Flags
553            0x01 => Ok(AdStructure::Flags(data[0])),
554            // Incomplete List of 16-bit Service or Service Class UUIDs
555            0x02 => match zerocopy::FromBytes::ref_from_bytes(data) {
556                Ok(x) => Ok(AdStructure::IncompleteServiceUuids16(x)),
557                Err(e) => {
558                    let _ = zerocopy::SizeError::from(e);
559                    Err(codec::Error::InvalidValue)
560                }
561            },
562            // Complete List of 16-bit Service or Service Class UUIDs
563            0x03 => match zerocopy::FromBytes::ref_from_bytes(data) {
564                Ok(x) => Ok(AdStructure::CompleteServiceUuids16(x)),
565                Err(e) => {
566                    let _ = zerocopy::SizeError::from(e);
567                    Err(codec::Error::InvalidValue)
568                }
569            },
570            // Incomplete List of 32-bit Service or Service Class UUIDs
571            0x04 => match zerocopy::FromBytes::ref_from_bytes(data) {
572                Ok(x) => Ok(AdStructure::IncompleteServiceUuids32(x)),
573                Err(e) => {
574                    let _ = zerocopy::SizeError::from(e);
575                    Err(codec::Error::InvalidValue)
576                }
577            },
578            // Complete List of 32-bit Service or Service Class UUIDs
579            0x05 => match zerocopy::FromBytes::ref_from_bytes(data) {
580                Ok(x) => Ok(AdStructure::CompleteServiceUuids32(x)),
581                Err(e) => {
582                    let _ = zerocopy::SizeError::from(e);
583                    Err(codec::Error::InvalidValue)
584                }
585            },
586            // Incomplete List of 128-bit Service or Service Class UUIDs
587            0x06 => match zerocopy::FromBytes::ref_from_bytes(data) {
588                Ok(x) => Ok(AdStructure::IncompleteServiceUuids128(x)),
589                Err(e) => {
590                    let _ = zerocopy::SizeError::from(e);
591                    Err(codec::Error::InvalidValue)
592                }
593            },
594            // Complete List of 128-bit Service or Service Class UUIDs
595            0x07 => match zerocopy::FromBytes::ref_from_bytes(data) {
596                Ok(x) => Ok(AdStructure::CompleteServiceUuids128(x)),
597                Err(e) => {
598                    let _ = zerocopy::SizeError::from(e);
599                    Err(codec::Error::InvalidValue)
600                }
601            },
602            // Shortened Local Name
603            0x08 => Ok(AdStructure::ShortenedLocalName(data)),
604            // Complete Local Name
605            0x09 => Ok(AdStructure::CompleteLocalName(data)),
606            // Tx Power Level
607            0x0a => Ok(AdStructure::TxPowerLevel(data[0] as i8)),
608            /*
609            0x0D Class of Device
610            0x0E Simple Pairing Hash C-192
611            0x0F Simple Pairing Randomizer R-192
612            0x10 Device ID Device: ID Profile (when used in EIR data)
613            0x10 Security Manager TK Value when used in OOB data blocks
614            0x11 Security Manager Out of Band Flags
615            0x12 Peripheral Connection Interval Range
616            0x14 List of 16-bit Service Solicitation UUIDs
617            0x15 List of 128-bit Service Solicitation UUIDs
618            */
619            // Service Data - 16-bit UUID
620            0x16 => {
621                if data.len() < 2 {
622                    return Err(codec::Error::InvalidValue);
623                }
624                let uuid = data[0..2].try_into().unwrap();
625                Ok(AdStructure::ServiceData16 { uuid, data: &data[2..] })
626            }
627            /*
628            0x17 Public Target Address
629            0x18 Random Target Address
630            0x19 Appearance
631            0x1A Advertising Interval
632            0x1B LE Bluetooth Device Address
633            0x1C LE Role
634            0x1D Simple Pairing Hash C-256
635            0x1E Simple Pairing Randomizer R-256
636            0x1F List of 32-bit Service Solicitation UUIDs
637            0x20 Service Data - 32-bit UUID
638            0x21 Service Data - 128-bit UUID
639            0x22 LE Secure Connections Confirmation Value
640            0x23 LE Secure Connections Random Value
641            0x24 URI
642            0x25 Indoor Positioning
643            0x26 Transport Discovery Data
644            0x27 LE Supported Features
645            0x28 Channel Map Update Indication
646            0x29 PB-ADV
647            0x2A Mesh Message
648            0x2B Mesh Beacon
649            0x2C BIGInfo
650            0x2D Broadcast_Code
651            0x2E Resolvable Set Identifier
652            0x2F Advertising Interval - long
653            0x30 Broadcast_Name
654            0x31 Encrypted Advertising Data
655            0x32 Periodic Advertising Response Timing
656            0x34 Electronic Shelf Label
657            0x3D 3D Information Data
658            */
659            // Manufacturer Specific Data
660            0xff if data.len() >= 2 => Ok(AdStructure::ManufacturerSpecificData {
661                company_identifier: u16::from_le_bytes([data[0], data[1]]),
662                payload: &data[2..],
663            }),
664            ty => Ok(AdStructure::Unknown { ty, data }),
665        }
666    }
667}
668
669impl<'d> Iterator for AdStructureIter<'d> {
670    type Item = Result<AdStructure<'d>, codec::Error>;
671    fn next(&mut self) -> Option<Self::Item> {
672        if self.cursor.available() == 0 {
673            return None;
674        }
675        Some(self.read())
676    }
677}
678
679#[cfg(test)]
680mod tests {
681    use super::*;
682
683    #[test]
684    fn adv_name_truncate() {
685        let mut adv_data = [0; 31];
686        assert!(AdStructure::encode_slice(
687            &[
688                AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
689                AdStructure::IncompleteServiceUuids16(&[[0x0f, 0x18]]),
690                AdStructure::CompleteLocalName(b"12345678901234567890123"),
691            ],
692            &mut adv_data[..],
693        )
694        .is_err());
695    }
696}