1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! Types related to the LE advertising interval.

use byteorder::{ByteOrder, LittleEndian};
use core::time::Duration;

/// Define an advertising interval range.
///
/// The advertising interval min shall be less than or equal to the advertising interval
/// max. The advertising interval min and advertising interval max should not be the same value
/// to enable the Controller to determine the best advertising interval given other activities,
/// though this implementation allows them to be equal.
///
/// For [high duty cycle directed
/// advertising](AdvertisingType::ConnectableDirectedHighDutyCycle), the advertising interval is
/// not used and shall be ignored.  This implementation sends 0 for both fields in that case.
///
/// The advertising interval min and advertising interval max shall not be set to less than 100
/// ms if the advertising type is [`ScannableUndirected`](AdvertisingType::ScannableUndirected)
/// or [`NonConnectableUndirected`](AdvertisingType::NonConnectableUndirected).  This
/// restriction is removed in version 5.0 of the spec.
#[derive(Clone, Debug)]
pub struct AdvertisingInterval {
    // The first field is the min; the second is the max
    interval: (Duration, Duration),
    _advertising_type: AdvertisingType,
}

impl AdvertisingInterval {
    /// Begins building an advertising interval.
    pub fn for_type(adv_type: AdvertisingType) -> AdvertisingIntervalBuilder {
        AdvertisingIntervalBuilder {
            advertising_type: adv_type,
        }
    }

    /// Serialize the interval into the given buffer.
    ///
    /// Serializes the minimum range of the interval (2 bytes), the maximum range of the interval (2
    /// bytes), and the advertising type (1 byte).
    ///
    /// If the advertising type is [high duty cycle
    /// directed](AdvertisingType::ConnectableDirectedHighDutyCycle), the advertising interval is
    /// not used and shall be ignored.  This implementation sends 0 for both fields in that case.
    ///
    /// # Panics
    ///
    /// - If the provided buffer is not at least 5 bytes long.
    pub fn into_bytes(&self, bytes: &mut [u8]) {
        if self._advertising_type == AdvertisingType::ConnectableDirectedHighDutyCycle {
            bytes[0..4].copy_from_slice(&[0; 4]);
        } else {
            LittleEndian::write_u16(&mut bytes[0..2], Self::as_u16(self.interval.0));
            LittleEndian::write_u16(&mut bytes[2..4], Self::as_u16(self.interval.1));
        }
        bytes[4] = self._advertising_type as u8;
    }

    fn as_u16(d: Duration) -> u16 {
        // T = 0.625 ms * N
        // so N = T / 0.625 ms
        //      = T / 625 us
        //
        // Note: 1600 = 1_000_000 / 625
        (1600 * d.as_secs() as u32 + (d.subsec_micros() / 625)) as u16
    }

    /// Returns the advertising type.
    pub fn advertising_type(&self) -> AdvertisingType {
        self._advertising_type
    }
}

/// Partially-specified advertising interval.
pub struct AdvertisingIntervalBuilder {
    advertising_type: AdvertisingType,
}

impl AdvertisingIntervalBuilder {
    /// Completes the advertising interval with the provided minimum and maximum values.
    ///
    /// # Errors
    ///
    /// - [TooShort](AdvertisingIntervalError::TooShort) if the minimum value is too small. For
    ///   Bluetooth specifications v4.x, if the advertising type is
    ///   [ScannableUndirected](AdvertisingType::ScannableUndirected), then the minimum value is 100
    ///   ms. In all other cases, the minimum value is 20 ms.
    /// - [TooLong](AdvertisingIntervalError::TooLong) if the maximum value is too large. The
    ///   maximum value is 10.24 seconds.
    /// - [Inverted](AdvertisingIntervalError::Inverted) if the minimum is greater than the
    ///   maximum.
    pub fn with_range(
        &self,
        min: Duration,
        max: Duration,
    ) -> Result<AdvertisingInterval, AdvertisingIntervalError> {
        const MIN: Duration = Duration::from_millis(20);
        if min < MIN {
            return Err(AdvertisingIntervalError::TooShort(min));
        }

        #[cfg(not(feature = "version-5-0"))]
        {
            if self.advertising_type == AdvertisingType::ScannableUndirected {
                const SCANNABLE_MIN: Duration = Duration::from_millis(100);
                if min < SCANNABLE_MIN {
                    return Err(AdvertisingIntervalError::TooShort(min));
                }
            }
        }

        const MAX: Duration = Duration::from_millis(10240);
        if max > MAX {
            return Err(AdvertisingIntervalError::TooLong(max));
        }

        if min > max {
            return Err(AdvertisingIntervalError::Inverted(min, max));
        }

        Ok(AdvertisingInterval {
            interval: (min, max),
            _advertising_type: self.advertising_type,
        })
    }

    /// Completes the advertising interval without a range.
    ///
    /// This is only valid if the advertising type is
    /// [ScannableUndirected](AdvertisingType::ScannableUndirected).
    ///
    /// # Errors
    ///
    /// - [NoRange](AdvertisingIntervalError::NoRange) if the advertising type is anything except
    ///   [ConnectableDirectedHighDutyCycle](AdvertisingType::ConnectableDirectedHighDutyCycle).
    pub fn build(&self) -> Result<AdvertisingInterval, AdvertisingIntervalError> {
        if self.advertising_type == AdvertisingType::ConnectableDirectedHighDutyCycle {
            Ok(AdvertisingInterval {
                interval: (Duration::from_secs(0), Duration::from_secs(0)),
                _advertising_type: self.advertising_type,
            })
        } else {
            Err(AdvertisingIntervalError::NoRange)
        }
    }
}

/// Potential errors that can occur when specifying an [AdvertisingInterval].
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AdvertisingIntervalError {
    /// The minimum value was too short. Includes the invalid value.
    TooShort(Duration),
    /// The maximum value was too long. Includes the invalid value.
    TooLong(Duration),
    /// The minimum value was greater than the maximum value. Includes the provided minimum and
    /// value, respectively.
    Inverted(Duration, Duration),
    /// The advertising interval was not given a range, and the type was not
    /// [ConnectableDirectedHighDutyCycle](AdvertisingType::ConnectableDirectedHighDutyCycle).
    NoRange,
}

/// The advertising type is used in the
/// [`AdvertisingParameters`]($crate::host::AdvertisingParameters) to determine the packet type that
/// is used for advertising when advertising is enabled.
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AdvertisingType {
    /// Connectable undirected advertising
    ConnectableUndirected = 0x00,
    /// Connectable high duty cycle directed advertising
    ConnectableDirectedHighDutyCycle = 0x01,
    /// Scannable undirected advertising
    ScannableUndirected = 0x02,
    /// Non connectable undirected advertising
    NonConnectableUndirected = 0x03,
    /// Connectable low duty cycle directed advertising
    ConnectableDirectedLowDutyCycle = 0x04,
}