pub const ADVERTISING_ACCESS_ADDRESS: u32 = 0x8E89_BED6;
pub const ADV_CRC_INIT: u32 = 0x55_5555;
pub const ADV_CHANNEL_37: u8 = 37;
#[cfg(test)]
pub const ADV_CHANNEL_38: u8 = 38;
#[cfg(test)]
pub const ADV_CHANNEL_39: u8 = 39;
#[cfg(test)]
pub const ADV_CHANNEL_37_FREQUENCY_MHZ: u16 = 2402;
#[cfg(test)]
pub const ADV_CHANNEL_38_FREQUENCY_MHZ: u16 = 2426;
#[cfg(test)]
pub const ADV_CHANNEL_39_FREQUENCY_MHZ: u16 = 2480;
#[cfg(test)]
pub const ADVERTISING_CHANNELS: [u8; 3] = [ADV_CHANNEL_37, ADV_CHANNEL_38, ADV_CHANNEL_39];
#[cfg(test)]
pub const fn advertising_channel_frequency_mhz(channel: u8) -> Option<u16> {
match channel {
ADV_CHANNEL_37 => Some(ADV_CHANNEL_37_FREQUENCY_MHZ),
ADV_CHANNEL_38 => Some(ADV_CHANNEL_38_FREQUENCY_MHZ),
ADV_CHANNEL_39 => Some(ADV_CHANNEL_39_FREQUENCY_MHZ),
_ => None,
}
}
#[cfg(test)]
pub const fn advertising_frequency_channel(frequency_mhz: u16) -> Option<u8> {
match frequency_mhz {
ADV_CHANNEL_37_FREQUENCY_MHZ => Some(ADV_CHANNEL_37),
ADV_CHANNEL_38_FREQUENCY_MHZ => Some(ADV_CHANNEL_38),
ADV_CHANNEL_39_FREQUENCY_MHZ => Some(ADV_CHANNEL_39),
_ => None,
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BleAdvPduType {
AdvInd = 0x0,
AdvDirectInd = 0x1,
AdvNonconnInd = 0x2,
ScanReq = 0x3,
ScanRsp = 0x4,
ConnectInd = 0x5,
AdvScanInd = 0x6,
}
impl BleAdvPduType {
pub const fn from_u4(value: u8) -> Option<Self> {
match value {
0x0 => Some(Self::AdvInd),
0x1 => Some(Self::AdvDirectInd),
0x2 => Some(Self::AdvNonconnInd),
0x3 => Some(Self::ScanReq),
0x4 => Some(Self::ScanRsp),
0x5 => Some(Self::ConnectInd),
0x6 => Some(Self::AdvScanInd),
_ => None,
}
}
pub const fn as_u4(&self) -> u8 {
*self as u8
}
}
pub const AD_FLAGS: u8 = 0x01;
pub const AD_INCOMPLETE_16_BIT_SERVICE_UUIDS: u8 = 0x02;
pub const AD_COMPLETE_16_BIT_SERVICE_UUIDS: u8 = 0x03;
pub const AD_INCOMPLETE_32_BIT_SERVICE_UUIDS: u8 = 0x04;
pub const AD_COMPLETE_32_BIT_SERVICE_UUIDS: u8 = 0x05;
pub const AD_INCOMPLETE_128_BIT_SERVICE_UUIDS: u8 = 0x06;
pub const AD_COMPLETE_128_BIT_SERVICE_UUIDS: u8 = 0x07;
pub const AD_SHORTENED_LOCAL_NAME: u8 = 0x08;
pub const AD_COMPLETE_LOCAL_NAME: u8 = 0x09;
pub const AD_TX_POWER_LEVEL: u8 = 0x0A;
pub const AD_SERVICE_DATA_16_BIT_UUID: u8 = 0x16;
pub const AD_APPEARANCE: u8 = 0x19;
pub const AD_SERVICE_DATA_32_BIT_UUID: u8 = 0x20;
pub const AD_SERVICE_DATA_128_BIT_UUID: u8 = 0x21;
pub const AD_MANUFACTURER_SPECIFIC_DATA: u8 = 0xFF;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg(test)]
pub struct AdType(u8);
#[cfg(test)]
impl AdType {
pub const FLAGS: Self = Self(AD_FLAGS);
pub const INCOMPLETE_16_BIT_SERVICE_UUIDS: Self = Self(AD_INCOMPLETE_16_BIT_SERVICE_UUIDS);
pub const COMPLETE_16_BIT_SERVICE_UUIDS: Self = Self(AD_COMPLETE_16_BIT_SERVICE_UUIDS);
pub const INCOMPLETE_32_BIT_SERVICE_UUIDS: Self = Self(AD_INCOMPLETE_32_BIT_SERVICE_UUIDS);
pub const COMPLETE_32_BIT_SERVICE_UUIDS: Self = Self(AD_COMPLETE_32_BIT_SERVICE_UUIDS);
pub const INCOMPLETE_128_BIT_SERVICE_UUIDS: Self = Self(AD_INCOMPLETE_128_BIT_SERVICE_UUIDS);
pub const COMPLETE_128_BIT_SERVICE_UUIDS: Self = Self(AD_COMPLETE_128_BIT_SERVICE_UUIDS);
pub const SHORTENED_LOCAL_NAME: Self = Self(AD_SHORTENED_LOCAL_NAME);
pub const COMPLETE_LOCAL_NAME: Self = Self(AD_COMPLETE_LOCAL_NAME);
pub const TX_POWER_LEVEL: Self = Self(AD_TX_POWER_LEVEL);
pub const SERVICE_DATA_16_BIT_UUID: Self = Self(AD_SERVICE_DATA_16_BIT_UUID);
pub const APPEARANCE: Self = Self(AD_APPEARANCE);
pub const SERVICE_DATA_32_BIT_UUID: Self = Self(AD_SERVICE_DATA_32_BIT_UUID);
pub const SERVICE_DATA_128_BIT_UUID: Self = Self(AD_SERVICE_DATA_128_BIT_UUID);
pub const MANUFACTURER_SPECIFIC_DATA: Self = Self(AD_MANUFACTURER_SPECIFIC_DATA);
pub const fn from_u8(value: u8) -> Self {
Self(value)
}
pub const fn as_u8(self) -> u8 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ble_consts_advertising_values_match_manifest() {
assert_eq!(ADVERTISING_ACCESS_ADDRESS, 0x8E89_BED6);
assert_eq!(ADV_CRC_INIT, 0x55_5555);
assert_eq!(ADVERTISING_CHANNELS, [37, 38, 39]);
assert_eq!(advertising_channel_frequency_mhz(37), Some(2402));
assert_eq!(advertising_channel_frequency_mhz(38), Some(2426));
assert_eq!(advertising_channel_frequency_mhz(39), Some(2480));
assert_eq!(advertising_channel_frequency_mhz(36), None);
assert_eq!(advertising_frequency_channel(2402), Some(37));
assert_eq!(advertising_frequency_channel(2426), Some(38));
assert_eq!(advertising_frequency_channel(2480), Some(39));
assert_eq!(advertising_frequency_channel(2404), None);
}
#[test]
fn ble_consts_pdu_type_round_trips() {
let pdu_types = [
(0x0, BleAdvPduType::AdvInd),
(0x1, BleAdvPduType::AdvDirectInd),
(0x2, BleAdvPduType::AdvNonconnInd),
(0x3, BleAdvPduType::ScanReq),
(0x4, BleAdvPduType::ScanRsp),
(0x5, BleAdvPduType::ConnectInd),
(0x6, BleAdvPduType::AdvScanInd),
];
for (raw, pdu_type) in pdu_types {
assert_eq!(BleAdvPduType::from_u4(raw), Some(pdu_type));
assert_eq!(pdu_type.as_u4(), raw);
}
assert_eq!(BleAdvPduType::from_u4(0x7), None);
assert_eq!(BleAdvPduType::from_u4(0xF), None);
}
#[test]
fn ble_consts_ad_type_values_and_unknown_round_trip() {
assert_eq!(AdType::FLAGS.as_u8(), 0x01);
assert_eq!(AdType::INCOMPLETE_16_BIT_SERVICE_UUIDS.as_u8(), 0x02);
assert_eq!(AdType::COMPLETE_16_BIT_SERVICE_UUIDS.as_u8(), 0x03);
assert_eq!(AdType::INCOMPLETE_32_BIT_SERVICE_UUIDS.as_u8(), 0x04);
assert_eq!(AdType::COMPLETE_32_BIT_SERVICE_UUIDS.as_u8(), 0x05);
assert_eq!(AdType::INCOMPLETE_128_BIT_SERVICE_UUIDS.as_u8(), 0x06);
assert_eq!(AdType::COMPLETE_128_BIT_SERVICE_UUIDS.as_u8(), 0x07);
assert_eq!(AdType::SHORTENED_LOCAL_NAME.as_u8(), 0x08);
assert_eq!(AdType::COMPLETE_LOCAL_NAME.as_u8(), 0x09);
assert_eq!(AdType::TX_POWER_LEVEL.as_u8(), 0x0A);
assert_eq!(AdType::SERVICE_DATA_16_BIT_UUID.as_u8(), 0x16);
assert_eq!(AdType::APPEARANCE.as_u8(), 0x19);
assert_eq!(AdType::SERVICE_DATA_32_BIT_UUID.as_u8(), 0x20);
assert_eq!(AdType::SERVICE_DATA_128_BIT_UUID.as_u8(), 0x21);
assert_eq!(AdType::MANUFACTURER_SPECIFIC_DATA.as_u8(), 0xFF);
assert_eq!(AdType::from_u8(0x7F).as_u8(), 0x7F);
}
}