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}