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}