stm32wb_hci/types/connection_interval.rs
1//! Types related to the connection interval.
2
3use byteorder::{ByteOrder, LittleEndian};
4use core::cmp;
5use core::time::Duration;
6
7/// Define a connection interval range with its latency and supervision timeout. This value is
8/// passed to the controller, which determines the [actual connection interval](crate::types::FixedConnectionInterval).
9#[derive(Copy, Clone, Debug)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub struct ConnectionInterval {
12 interval_: (Duration, Duration),
13 conn_latency_: u16,
14 supervision_timeout_: Duration,
15}
16
17impl ConnectionInterval {
18 /// Returns the connection interval.
19 pub fn interval(&self) -> (Duration, Duration) {
20 self.interval_
21 }
22
23 /// Returns the connection latency, in number of events.
24 pub fn conn_latency(&self) -> u16 {
25 self.conn_latency_
26 }
27
28 /// Returns the supervision timeout.
29 pub fn supervision_timeout(&self) -> Duration {
30 self.supervision_timeout_
31 }
32
33 /// Serializes the connection interval into the given byte buffer.
34 ///
35 /// The interval is serialized as:
36 /// - The minimum interval value, appropriately converted (2 bytes)
37 /// - The maximum interval value, appropriately converted (2 bytes)
38 /// - The connection latency (2 bytes)
39 /// - The supervision timeout, appropriately converted (2 bytes)
40 ///
41 /// # Panics
42 ///
43 /// The provided buffer must be at least 8 bytes long.
44 pub fn copy_into_slice(&self, bytes: &mut [u8]) {
45 assert!(bytes.len() >= 8);
46
47 LittleEndian::write_u16(&mut bytes[0..2], Self::interval_as_u16(self.interval_.0));
48 LittleEndian::write_u16(&mut bytes[2..4], Self::interval_as_u16(self.interval_.1));
49 LittleEndian::write_u16(&mut bytes[4..6], self.conn_latency_);
50 LittleEndian::write_u16(
51 &mut bytes[6..8],
52 Self::timeout_as_u16(self.supervision_timeout_),
53 );
54 }
55
56 /// Deserializes the connection interval from the given byte buffer.
57 ///
58 /// - The minimum interval value, appropriately converted (2 bytes)
59 /// - The maximum interval value, appropriately converted (2 bytes)
60 /// - The connection latency (2 bytes)
61 /// - The supervision timeout, appropriately converted (2 bytes)
62 ///
63 /// # Panics
64 ///
65 /// The provided buffer must be at least 8 bytes long.
66 ///
67 /// # Errors
68 ///
69 /// Any of the errors from the [builder](ConnectionIntervalBuilder::build) except for
70 /// Incomplete.
71 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ConnectionIntervalError> {
72 assert!(bytes.len() >= 8);
73
74 // Do the error checking with the standard connection interval builder. The min and max of
75 // the interval range are allowed to be equal.
76 let interval_min =
77 Duration::from_micros(1_250) * u32::from(LittleEndian::read_u16(&bytes[0..2]));
78 let interval_max =
79 Duration::from_micros(1_250) * u32::from(LittleEndian::read_u16(&bytes[2..4]));
80 let latency = LittleEndian::read_u16(&bytes[4..6]);
81 let timeout = Duration::from_millis(10) * u32::from(LittleEndian::read_u16(&bytes[6..8]));
82 ConnectionIntervalBuilder::new()
83 .with_range(interval_min, interval_max)
84 .with_latency(latency)
85 .with_supervision_timeout(timeout)
86 .build()
87 }
88
89 fn interval_as_u16(d: Duration) -> u16 {
90 // T ms = N * 1.25 ms
91 // N = T / 1.25 ms
92 // = T / (5/4) ms
93 // = 4 * T ms / 5 ms
94 //
95 // Note: 1000 * 4 / 5 = 800
96 ((800 * d.as_secs()) as u32 + 4 * d.subsec_millis() / 5) as u16
97 }
98
99 fn timeout_as_u16(d: Duration) -> u16 {
100 // T ms = N * 10 ms
101 // N = T ms / 10 ms
102 ((100 * d.as_secs()) as u32 + d.subsec_millis() / 10) as u16
103 }
104}
105
106/// Intermediate builder for the [`ConnectionInterval`].
107#[derive(Default)]
108#[cfg_attr(feature = "defmt", derive(defmt::Format))]
109pub struct ConnectionIntervalBuilder {
110 interval: Option<(Duration, Duration)>,
111 conn_latency: Option<u16>,
112 supervision_timeout: Option<Duration>,
113}
114
115impl ConnectionIntervalBuilder {
116 /// Initializes a new builder.
117 pub fn new() -> ConnectionIntervalBuilder {
118 ConnectionIntervalBuilder {
119 interval: None,
120 conn_latency: None,
121 supervision_timeout: None,
122 }
123 }
124
125 /// Sets the connection interval range.
126 ///
127 /// # Errors
128 ///
129 /// There are no errors from this function, but it may cause errors in
130 /// [build](ConnectionIntervalBuilder::build) if:
131 /// - `min` is greater than `max`
132 /// - Either `min` or `max` is less than 7.5 ms or more than 4 seconds.
133 /// - `max` leads to an invalid relative supervision timeout.
134 pub fn with_range(&mut self, min: Duration, max: Duration) -> &mut ConnectionIntervalBuilder {
135 self.interval = Some((min, max));
136 self
137 }
138
139 /// Sets the connection latency.
140 ///
141 /// # Errors
142 ///
143 /// There are no errors from this function, but it may cause errors in
144 /// [build](ConnectionIntervalBuilder::build) if:
145 /// - `latency` is 500 or greater.
146 /// - `latency` leads to an invalid relative supervision timeout.
147 pub fn with_latency(&mut self, latency: u16) -> &mut ConnectionIntervalBuilder {
148 self.conn_latency = Some(latency);
149 self
150 }
151
152 /// Sets the supervision timeout.
153 ///
154 /// # Errors
155 ///
156 /// There are no errors from this function, but it may cause errors in
157 /// [build](ConnectionIntervalBuilder::build) if:
158 /// - `timeout` less than 100 ms or greater than 32 seconds
159 /// - `timeout` results in an invalid relative supervision timeout.
160 pub fn with_supervision_timeout(
161 &mut self,
162 timeout: Duration,
163 ) -> &mut ConnectionIntervalBuilder {
164 self.supervision_timeout = Some(timeout);
165 self
166 }
167
168 /// Builds the connection interval if all parameters are valid.
169 ///
170 /// # Errors
171 ///
172 /// - [Incomplete](ConnectionIntervalError::Incomplete) if any of
173 /// [`with_range`](ConnectionIntervalBuilder::with_range),
174 /// [`with_latency`](ConnectionIntervalBuilder::with_latency), or
175 /// [`with_supervision_timeout`](ConnectionIntervalBuilder::with_supervision_timeout) have not
176 /// been called.
177 /// - [IntervalTooShort](ConnectionIntervalError::IntervalTooShort) if the minimum range value
178 /// is less than 7.5 ms.
179 /// - [IntervalTooLong](ConnectionIntervalError::IntervalTooLong) if the maximum range value
180 /// is greater than 4 seconds.
181 /// - [IntervalInverted](ConnectionIntervalError::IntervalInverted) if the minimum range value
182 /// is greater than the maximum.
183 /// - [BadConnectionLatency](ConnectionIntervalError::BadConnectionLatency) if the connection
184 /// latency is 500 or more.
185 /// - [SupervisionTimeoutTooShort](ConnectionIntervalError::SupervisionTimeoutTooShort) if the
186 /// supervision timeout is less than 100 ms, or if it is less than the computed minimum: (1 +
187 /// latency) * interval max * 2.
188 /// - [SupervisionTimeoutTooLong](ConnectionIntervalError::SupervisionTimeoutTooLong) if the
189 /// supervision timeout is more than 32 seconds.
190 /// - [ImpossibleSupervisionTimeout](ConnectionIntervalError::ImpossibleSupervisionTimeout) if
191 /// the computed minimum supervision timeout ((1 + latency) * interval max * 2) is 32 seconds
192 /// or more.
193 pub fn build(&self) -> Result<ConnectionInterval, ConnectionIntervalError> {
194 if self.interval.is_none()
195 || self.conn_latency.is_none()
196 || self.supervision_timeout.is_none()
197 {
198 return Err(ConnectionIntervalError::Incomplete);
199 }
200
201 let interval = self.interval.unwrap();
202 const INTERVAL_MIN: Duration = Duration::from_micros(7500);
203 if interval.0 < INTERVAL_MIN {
204 return Err(ConnectionIntervalError::IntervalTooShort(interval.0));
205 }
206
207 const INTERVAL_MAX: Duration = Duration::from_secs(4);
208 if interval.1 > INTERVAL_MAX {
209 return Err(ConnectionIntervalError::IntervalTooLong(interval.1));
210 }
211
212 if interval.0 > interval.1 {
213 return Err(ConnectionIntervalError::IntervalInverted(
214 interval.0, interval.1,
215 ));
216 }
217
218 let conn_latency = self.conn_latency.unwrap();
219 const LATENCY_MAX: u16 = 0x1F3;
220 if conn_latency > LATENCY_MAX {
221 return Err(ConnectionIntervalError::BadConnectionLatency(conn_latency));
222 }
223
224 let supervision_timeout = self.supervision_timeout.unwrap();
225 let computed_timeout_min = interval.1 * (1 + u32::from(conn_latency)) * 2;
226 const TIMEOUT_MAX: Duration = Duration::from_secs(32);
227 if computed_timeout_min >= TIMEOUT_MAX {
228 return Err(ConnectionIntervalError::ImpossibleSupervisionTimeout(
229 computed_timeout_min,
230 ));
231 }
232
233 const TIMEOUT_ABS_MIN: Duration = Duration::from_millis(100);
234 let timeout_min = cmp::max(computed_timeout_min, TIMEOUT_ABS_MIN);
235 if supervision_timeout <= timeout_min {
236 return Err(ConnectionIntervalError::SupervisionTimeoutTooShort(
237 supervision_timeout,
238 timeout_min,
239 ));
240 }
241
242 if supervision_timeout > TIMEOUT_MAX {
243 return Err(ConnectionIntervalError::SupervisionTimeoutTooLong(
244 supervision_timeout,
245 ));
246 }
247
248 Ok(ConnectionInterval {
249 interval_: interval,
250 conn_latency_: conn_latency,
251 supervision_timeout_: supervision_timeout,
252 })
253 }
254}
255
256/// Types of errors that can occure when creating a [`ConnectionInterval`].
257#[derive(Copy, Clone, Debug, PartialEq)]
258#[cfg_attr(feature = "defmt", derive(defmt::Format))]
259pub enum ConnectionIntervalError {
260 /// At least one of any of [`with_range`](ConnectionIntervalBuilder::with_range),
261 /// [`with_latency`](ConnectionIntervalBuilder::with_latency), or
262 /// [`with_supervision_timeout`](ConnectionIntervalBuilder::with_supervision_timeout) has not
263 /// been called.
264 Incomplete,
265 /// The minimum range value is less than 7.5 ms. Includes the invalid value.
266 IntervalTooShort(Duration),
267 /// The maximum range value is greater than 4 seconds. Includes the invalid value.
268 IntervalTooLong(Duration),
269 /// The minimum range value is greater than the maximum. Includes the provided minimum and
270 /// maximum, respectively.
271 IntervalInverted(Duration, Duration),
272 /// The connection latency is 500 or more. Includes the provided value.
273 BadConnectionLatency(u16),
274 /// The supervision timeout is less than 100 ms, or it is less than the computed minimum: (1 +
275 /// latency) * interval max * 2. The first value is the provided timeout; the second is the
276 /// required minimum.
277 SupervisionTimeoutTooShort(Duration, Duration),
278 /// The supervision timeout is more than 32 seconds. Includes the provided timeout.
279 SupervisionTimeoutTooLong(Duration),
280 /// The computed minimum supervision timeout ((1 + latency) * interval max * 2) is 32 seconds
281 /// or more. Includes the computed minimum.
282 ImpossibleSupervisionTimeout(Duration),
283}
284
285/// Define a connection interval with its latency and supervision timeout. This value is
286/// returned from the controller.
287#[derive(Copy, Clone, Debug)]
288#[cfg_attr(feature = "defmt", derive(defmt::Format))]
289pub struct FixedConnectionInterval {
290 interval_: Duration,
291 conn_latency_: u16,
292 supervision_timeout_: Duration,
293}
294
295impl FixedConnectionInterval {
296 /// Deserializes the connection interval from the given byte buffer.
297 ///
298 /// - The interval value, appropriately converted (2 bytes)
299 /// - The connection latency (2 bytes)
300 /// - The supervision timeout, appropriately converted (2 bytes)
301 ///
302 /// # Panics
303 ///
304 /// The provided buffer must be at least 6 bytes long.
305 ///
306 /// # Errors
307 ///
308 /// Any of the errors from the [builder](ConnectionIntervalBuilder::build) except for
309 /// Incomplete.
310 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ConnectionIntervalError> {
311 assert!(bytes.len() >= 6);
312
313 // Do the error checking with the standard connection interval builder. The min and max of
314 // the interval range are allowed to be equal.
315 let interval =
316 Duration::from_micros(1_250) * u32::from(LittleEndian::read_u16(&bytes[0..2]));
317 let latency = LittleEndian::read_u16(&bytes[2..4]);
318 let timeout = Duration::from_millis(10) * u32::from(LittleEndian::read_u16(&bytes[4..6]));
319 ConnectionIntervalBuilder::new()
320 .with_range(interval, interval)
321 .with_latency(latency)
322 .with_supervision_timeout(timeout)
323 .build()?;
324
325 Ok(FixedConnectionInterval {
326 interval_: interval,
327 conn_latency_: latency,
328 supervision_timeout_: timeout,
329 })
330 }
331
332 /// Returns the connection interval.
333 pub fn interval(&self) -> Duration {
334 self.interval_
335 }
336
337 /// Returns the connection latency, in number of events.
338 pub fn conn_latency(&self) -> u16 {
339 self.conn_latency_
340 }
341
342 /// Returns the supervision timeout.
343 pub fn supervision_timeout(&self) -> Duration {
344 self.supervision_timeout_
345 }
346}