bluetooth_hci/types/
advertising_interval.rs

1//! Types related to the LE advertising interval.
2
3use byteorder::{ByteOrder, LittleEndian};
4use core::time::Duration;
5
6/// Define an advertising interval range.
7///
8/// The advertising interval min shall be less than or equal to the advertising interval
9/// max. The advertising interval min and advertising interval max should not be the same value
10/// to enable the Controller to determine the best advertising interval given other activities,
11/// though this implementation allows them to be equal.
12///
13/// For [high duty cycle directed
14/// advertising](AdvertisingType::ConnectableDirectedHighDutyCycle), the advertising interval is
15/// not used and shall be ignored.  This implementation sends 0 for both fields in that case.
16///
17/// The advertising interval min and advertising interval max shall not be set to less than 100
18/// ms if the advertising type is [`ScannableUndirected`](AdvertisingType::ScannableUndirected)
19/// or [`NonConnectableUndirected`](AdvertisingType::NonConnectableUndirected).  This
20/// restriction is removed in version 5.0 of the spec.
21#[derive(Clone, Debug)]
22pub struct AdvertisingInterval {
23    // The first field is the min; the second is the max
24    interval: (Duration, Duration),
25    _advertising_type: AdvertisingType,
26}
27
28impl AdvertisingInterval {
29    /// Begins building an advertising interval.
30    pub fn for_type(adv_type: AdvertisingType) -> AdvertisingIntervalBuilder {
31        AdvertisingIntervalBuilder {
32            advertising_type: adv_type,
33        }
34    }
35
36    /// Serialize the interval into the given buffer.
37    ///
38    /// Serializes the minimum range of the interval (2 bytes), the maximum range of the interval (2
39    /// bytes), and the advertising type (1 byte).
40    ///
41    /// If the advertising type is [high duty cycle
42    /// directed](AdvertisingType::ConnectableDirectedHighDutyCycle), the advertising interval is
43    /// not used and shall be ignored.  This implementation sends 0 for both fields in that case.
44    ///
45    /// # Panics
46    ///
47    /// - If the provided buffer is not at least 5 bytes long.
48    pub fn copy_into_slice(&self, bytes: &mut [u8]) {
49        if self._advertising_type == AdvertisingType::ConnectableDirectedHighDutyCycle {
50            bytes[0..4].copy_from_slice(&[0; 4]);
51        } else {
52            LittleEndian::write_u16(&mut bytes[0..2], Self::duration_as_u16(self.interval.0));
53            LittleEndian::write_u16(&mut bytes[2..4], Self::duration_as_u16(self.interval.1));
54        }
55        bytes[4] = self._advertising_type as u8;
56    }
57
58    fn duration_as_u16(d: Duration) -> u16 {
59        // T = 0.625 ms * N
60        // so N = T / 0.625 ms
61        //      = T / 625 us
62        //
63        // Note: 1600 = 1_000_000 / 625
64        (1600 * d.as_secs() as u32 + (d.subsec_micros() / 625)) as u16
65    }
66
67    /// Returns the advertising type.
68    pub fn advertising_type(&self) -> AdvertisingType {
69        self._advertising_type
70    }
71}
72
73/// Partially-specified advertising interval.
74pub struct AdvertisingIntervalBuilder {
75    advertising_type: AdvertisingType,
76}
77
78impl AdvertisingIntervalBuilder {
79    /// Completes the advertising interval with the provided minimum and maximum values.
80    ///
81    /// # Errors
82    ///
83    /// - [TooShort](AdvertisingIntervalError::TooShort) if the minimum value is too small. For
84    ///   Bluetooth specifications v4.x, if the advertising type is
85    ///   [ScannableUndirected](AdvertisingType::ScannableUndirected), then the minimum value is 100
86    ///   ms. In all other cases, the minimum value is 20 ms.
87    /// - [TooLong](AdvertisingIntervalError::TooLong) if the maximum value is too large. The
88    ///   maximum value is 10.24 seconds.
89    /// - [Inverted](AdvertisingIntervalError::Inverted) if the minimum is greater than the
90    ///   maximum.
91    pub fn with_range(
92        &self,
93        min: Duration,
94        max: Duration,
95    ) -> Result<AdvertisingInterval, AdvertisingIntervalError> {
96        const MIN: Duration = Duration::from_millis(20);
97        if min < MIN {
98            return Err(AdvertisingIntervalError::TooShort(min));
99        }
100
101        #[cfg(not(feature = "version-5-0"))]
102        {
103            if self.advertising_type == AdvertisingType::ScannableUndirected {
104                const SCANNABLE_MIN: Duration = Duration::from_millis(100);
105                if min < SCANNABLE_MIN {
106                    return Err(AdvertisingIntervalError::TooShort(min));
107                }
108            }
109        }
110
111        const MAX: Duration = Duration::from_millis(10240);
112        if max > MAX {
113            return Err(AdvertisingIntervalError::TooLong(max));
114        }
115
116        if min > max {
117            return Err(AdvertisingIntervalError::Inverted(min, max));
118        }
119
120        Ok(AdvertisingInterval {
121            interval: (min, max),
122            _advertising_type: self.advertising_type,
123        })
124    }
125
126    /// Completes the advertising interval without a range.
127    ///
128    /// This is only valid if the advertising type is
129    /// [ScannableUndirected](AdvertisingType::ScannableUndirected).
130    ///
131    /// # Errors
132    ///
133    /// - [NoRange](AdvertisingIntervalError::NoRange) if the advertising type is anything except
134    ///   [ConnectableDirectedHighDutyCycle](AdvertisingType::ConnectableDirectedHighDutyCycle).
135    pub fn build(&self) -> Result<AdvertisingInterval, AdvertisingIntervalError> {
136        if self.advertising_type == AdvertisingType::ConnectableDirectedHighDutyCycle {
137            Ok(AdvertisingInterval {
138                interval: (Duration::from_secs(0), Duration::from_secs(0)),
139                _advertising_type: self.advertising_type,
140            })
141        } else {
142            Err(AdvertisingIntervalError::NoRange)
143        }
144    }
145}
146
147/// Potential errors that can occur when specifying an [`AdvertisingInterval`].
148#[derive(Copy, Clone, Debug, PartialEq)]
149pub enum AdvertisingIntervalError {
150    /// The minimum value was too short. Includes the invalid value.
151    TooShort(Duration),
152    /// The maximum value was too long. Includes the invalid value.
153    TooLong(Duration),
154    /// The minimum value was greater than the maximum value. Includes the provided minimum and
155    /// value, respectively.
156    Inverted(Duration, Duration),
157    /// The advertising interval was not given a range, and the type was not
158    /// [ConnectableDirectedHighDutyCycle](AdvertisingType::ConnectableDirectedHighDutyCycle).
159    NoRange,
160}
161
162/// The advertising type is used in the
163/// [`AdvertisingParameters`]($crate::host::AdvertisingParameters) to determine the packet type that
164/// is used for advertising when advertising is enabled.
165#[repr(u8)]
166#[derive(Copy, Clone, Debug, PartialEq)]
167pub enum AdvertisingType {
168    /// Connectable undirected advertising
169    ConnectableUndirected = 0x00,
170    /// Connectable high duty cycle directed advertising
171    ConnectableDirectedHighDutyCycle = 0x01,
172    /// Scannable undirected advertising
173    ScannableUndirected = 0x02,
174    /// Non connectable undirected advertising
175    NonConnectableUndirected = 0x03,
176    /// Connectable low duty cycle directed advertising
177    ConnectableDirectedLowDutyCycle = 0x04,
178}