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}