bluetooth_hci/types/
scan_window.rs

1//! Types related to the LE scanning window.
2
3use byteorder::{ByteOrder, LittleEndian};
4use core::time::Duration;
5
6/// Define a scanning window.
7///
8/// The controller runs LE scans every [`interval`](ScanWindow::interval), with scanning active
9/// during the [`window`](ScanWindow::window) in every interval.
10///
11/// The minimum time range is 2.5 ms, and the maximum is 10.24 s. The window must be shorter than or
12/// equal to the interval.
13#[derive(Clone, Debug, PartialEq)]
14pub struct ScanWindow {
15    interval_width: Duration,
16    window_width: Duration,
17}
18
19impl ScanWindow {
20    /// Returns the interval for the scanning window. The controller starts an LE scan every
21    /// interval.
22    pub fn interval(&self) -> Duration {
23        self.interval_width
24    }
25
26    /// Returns the amount of time the controller is scanning every interval.
27    pub fn window(&self) -> Duration {
28        self.window_width
29    }
30
31    /// Serializes the window into the given byte buffer.
32    ///
33    /// # Panics
34    ///
35    /// The buffer must be at least 4 bytes long.
36    pub fn copy_into_slice(&self, bytes: &mut [u8]) {
37        assert!(bytes.len() >= 4);
38
39        LittleEndian::write_u16(&mut bytes[0..2], Self::duration_as_u16(self.interval_width));
40        LittleEndian::write_u16(&mut bytes[2..4], Self::duration_as_u16(self.window_width));
41    }
42
43    /// Begins building a [ScanWindow]. The scan window has the given interval. Returns a
44    /// [builder](ScanWindowBuilder) that can be used to set the window duration.
45    ///
46    /// # Errors
47    ///
48    /// - [ScanWindowError::TooShort] if the provided interval is too short. It must be at least 2.5
49    ///   ms.
50    /// - [ScanWindowError::TooLong] if the provided interval is too long. It must be 10.24 seconds
51    ///   or less.
52    pub fn start_every(interval: Duration) -> Result<ScanWindowBuilder, ScanWindowError> {
53        Ok(ScanWindowBuilder {
54            interval: ScanWindow::validate(interval)?,
55        })
56    }
57
58    fn validate(d: Duration) -> Result<Duration, ScanWindowError> {
59        const MIN: Duration = Duration::from_micros(2500);
60        if d < MIN {
61            return Err(ScanWindowError::TooShort(d));
62        }
63
64        const MAX: Duration = Duration::from_millis(10240);
65        if d > MAX {
66            return Err(ScanWindowError::TooLong(d));
67        }
68
69        Ok(d)
70    }
71
72    fn duration_as_u16(d: Duration) -> u16 {
73        // T = 0.625 ms * N
74        // so N = T / 0.625 ms
75        //      = T / 625 us
76        //
77        // Note: 1600 = 1_000_000 / 625
78        (1600 * d.as_secs() as u32 + (d.subsec_micros() / 625)) as u16
79    }
80}
81
82/// Intermediate builder for the [`ScanWindow`].
83pub struct ScanWindowBuilder {
84    interval: Duration,
85}
86
87impl ScanWindowBuilder {
88    /// Completes building a [ScanWindow]. The scan window has the given window.
89    ///
90    /// # Errors
91    ///
92    /// - [ScanWindowError::TooShort] if the provided interval is too short. It must be at least 2.5
93    ///   ms.
94    /// - [ScanWindowError::TooLong] if the provided interval is too long. It must be 10.24 seconds
95    ///   or less.
96    /// - [ScanWindowError::Inverted] if the window is longer than the interval.
97    pub fn open_for(&self, window: Duration) -> Result<ScanWindow, ScanWindowError> {
98        if window > self.interval {
99            return Err(ScanWindowError::Inverted {
100                interval: self.interval,
101                window,
102            });
103        }
104
105        Ok(ScanWindow {
106            interval_width: self.interval,
107            window_width: ScanWindow::validate(window)?,
108        })
109    }
110}
111
112/// Types of errors that can occure when creating a [`ScanWindow`].
113#[derive(Copy, Clone, Debug, PartialEq)]
114pub enum ScanWindowError {
115    /// The duration is too short. Both the interval and duration must be at least 2.5 ms. Includes
116    /// the invalid duration.
117    TooShort(Duration),
118    /// The duration is too long. Both the interval and duration must be no more than 10.24
119    /// seconds. Includes the invalid duration.
120    TooLong(Duration),
121    /// The interval and window are inverted. That is, the interval is shorter than the window.
122    Inverted {
123        /// The provided interval, which is shorter than the window.
124        interval: Duration,
125        /// The provided window, which is longer than the interval.
126        window: Duration,
127    },
128}