use crate::Error;
use bytemuck::{Pod, Zeroable};
const BLUETOOTH_CONFIG_MAGIC: u32 = 0x4254_4C45;
const BLUETOOTH_DEVICE_LIST_MAGIC: u32 = 0x4254_4C53;
const BLUETOOTH_CONNECTION_STATE_MAGIC: u32 = 0x4254_4353;
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct BluetoothDeviceList {
magic: u32, devices: [BluetoothDeviceInfo; 10], device_count: u8, _padding: [u8; 3], }
impl Default for BluetoothDeviceList {
fn default() -> Self {
Self {
magic: BLUETOOTH_DEVICE_LIST_MAGIC,
devices: Default::default(),
device_count: 0,
_padding: [0; 3],
}
}
}
impl BluetoothDeviceList {
pub fn add_device(&mut self, device_config: BluetoothDeviceInfo) -> Result<(), Error> {
if self.device_count as usize >= self.devices.len() {
return Err(Error::DeviceListFull);
}
self.devices[self.device_count as usize] = device_config;
self.device_count += 1;
Ok(())
}
pub fn remove_device(&mut self, index: usize) -> Result<(), Error> {
if index >= self.device_count as usize {
return Err(Error::IndexOutOfBounds);
}
for i in index..(self.device_count as usize - 1) {
self.devices[i] = self.devices[i + 1];
}
self.device_count -= 1;
Ok(())
}
pub fn device(&self, index: usize) -> Result<&BluetoothDeviceInfo, Error> {
if index >= self.device_count as usize {
return Err(Error::IndexOutOfBounds);
}
Ok(&self.devices[index])
}
#[must_use]
pub fn len(&self) -> usize {
self.device_count as usize
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.device_count == 0
}
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct BluetoothConnectionState {
magic: u32, device_config: BluetoothDeviceInfo, connection_flags: u8, link_quality: u8, connection_phase: u8, _padding: [u8; 1], }
impl Default for BluetoothConnectionState {
fn default() -> Self {
Self {
magic: BLUETOOTH_CONNECTION_STATE_MAGIC,
device_config: BluetoothDeviceInfo::default(),
connection_flags: 0,
link_quality: 0,
connection_phase: BluetoothConnectionPhase::Idle as u8,
_padding: [0; 1],
}
}
}
impl BluetoothConnectionState {
pub fn set_remote_device(&mut self, device_config: BluetoothDeviceInfo) {
self.device_config = device_config;
}
pub fn set_connected(&mut self, connected: bool) {
if connected {
self.connection_flags |= 0x01;
} else {
self.connection_flags &= !0x01;
}
}
pub fn set_link_quality(&mut self, quality: u8) {
self.link_quality = quality;
}
#[must_use]
pub fn remote_device(&self) -> &BluetoothDeviceInfo {
&self.device_config
}
#[must_use]
pub fn is_connected(&self) -> bool {
(self.connection_flags & 0x01) != 0
}
#[must_use]
pub fn link_quality(&self) -> u8 {
self.link_quality
}
pub fn set_authenticated(&mut self, authenticated: bool) {
if authenticated {
self.connection_flags |= 0x02;
} else {
self.connection_flags &= !0x02;
}
}
#[must_use]
pub fn is_authenticated(&self) -> bool {
(self.connection_flags & 0x02) != 0
}
pub fn set_remote_device_address(&mut self, address: [u8; 6]) {
self.device_config.mac_address = address;
}
#[must_use]
pub fn remote_device_address(&self) -> Option<[u8; 6]> {
Some(self.device_config.mac_address)
}
pub fn set_connection_handle(&mut self, handle: Option<ConnHandle>) {
self.device_config.connection_params.connection_handle = handle.unwrap_or_default();
}
#[must_use]
pub fn connection_handle(&self) -> Option<ConnHandle> {
if self.device_config.connection_params.connection_handle.raw() == 0 {
None
} else {
Some(self.device_config.connection_params.connection_handle)
}
}
pub fn set_link_type(&mut self, link_type: u8) {
self.device_config.connection_params.link_type = link_type;
}
#[must_use]
pub fn link_type(&self) -> u8 {
self.device_config.connection_params.link_type
}
pub fn set_connection_phase(&mut self, phase: BluetoothConnectionPhase) {
self.connection_phase = phase as u8;
}
#[must_use]
pub fn connection_phase(&self) -> BluetoothConnectionPhase {
match self.connection_phase {
1 => BluetoothConnectionPhase::Discovery,
2 => BluetoothConnectionPhase::Connecting,
3 => BluetoothConnectionPhase::Connected,
4 => BluetoothConnectionPhase::Authenticating,
5 => BluetoothConnectionPhase::SettingUpEncryption,
6 => BluetoothConnectionPhase::FullyConnected,
7 => BluetoothConnectionPhase::ServiceDiscovery,
8 => BluetoothConnectionPhase::Ready,
9 => BluetoothConnectionPhase::Maintaining,
10 => BluetoothConnectionPhase::Reconnecting,
11 => BluetoothConnectionPhase::Failed,
12 => BluetoothConnectionPhase::Disconnecting,
_ => BluetoothConnectionPhase::Idle, }
}
pub fn advance_to_phase(&mut self, next_phase: BluetoothConnectionPhase) -> bool {
let current = self.connection_phase();
let valid_transition = next_phase == BluetoothConnectionPhase::Idle
|| Self::is_valid_transition(current, next_phase);
if valid_transition {
self.set_connection_phase(next_phase);
}
valid_transition
}
fn is_valid_transition(
current: BluetoothConnectionPhase,
next: BluetoothConnectionPhase,
) -> bool {
use BluetoothConnectionPhase::{
Authenticating, Connected, Connecting, Disconnecting, Discovery, Failed,
FullyConnected, Idle, Maintaining, Ready, Reconnecting, ServiceDiscovery,
SettingUpEncryption,
};
match current {
Idle => matches!(next, Discovery | Connecting),
Discovery => next == Connecting,
Connecting => matches!(next, Connected | Failed),
Connected => matches!(next, Authenticating | ServiceDiscovery | Disconnecting),
Authenticating => matches!(next, SettingUpEncryption | Failed | Disconnecting),
SettingUpEncryption => matches!(next, FullyConnected | Failed | Disconnecting),
FullyConnected => matches!(next, ServiceDiscovery | Ready | Disconnecting),
ServiceDiscovery => matches!(next, Ready | Failed | Disconnecting),
Ready => matches!(next, Maintaining | Disconnecting),
Maintaining => matches!(next, Reconnecting | Disconnecting),
Reconnecting => matches!(next, Connecting | Failed),
Failed => next == Reconnecting,
Disconnecting => false, }
}
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct BluetoothConnectionParams {
connection_handle: ConnHandle,
connection_interval: u16,
connection_latency: u16,
supervision_timeout: u16,
master_clock_accuracy: u8,
link_type: u8,
encryption_enabled: u8,
rssi: i8,
connected_at: u32,
last_activity: u32,
_padding: [u8; 4],
}
impl Default for BluetoothConnectionParams {
fn default() -> Self {
Self {
connection_handle: ConnHandle::default(),
connection_interval: 0,
connection_latency: 0,
supervision_timeout: 0,
master_clock_accuracy: 0,
link_type: 0,
encryption_enabled: 0,
rssi: -127,
connected_at: 0,
last_activity: 0,
_padding: [0; 4],
}
}
}
impl BluetoothConnectionParams {
#[must_use]
pub fn connection_handle(&self) -> ConnHandle {
self.connection_handle
}
#[must_use]
pub fn connection_interval(&self) -> u16 {
self.connection_interval
}
#[must_use]
pub fn connection_latency(&self) -> u16 {
self.connection_latency
}
#[must_use]
pub fn supervision_timeout(&self) -> u16 {
self.supervision_timeout
}
#[must_use]
pub fn master_clock_accuracy(&self) -> u8 {
self.master_clock_accuracy
}
#[must_use]
pub fn link_type(&self) -> u8 {
self.link_type
}
#[must_use]
pub fn encryption_enabled(&self) -> u8 {
self.encryption_enabled
}
#[must_use]
pub fn rssi(&self) -> i8 {
self.rssi
}
#[must_use]
pub fn connected_at(&self) -> u32 {
self.connected_at
}
#[must_use]
pub fn last_activity(&self) -> u32 {
self.last_activity
}
pub fn set_connection_handle(&mut self, handle: ConnHandle) {
self.connection_handle = handle;
}
pub fn set_connection_interval(&mut self, interval: u16) {
self.connection_interval = interval;
}
pub fn set_connection_latency(&mut self, latency: u16) {
self.connection_latency = latency;
}
pub fn set_supervision_timeout(&mut self, timeout: u16) {
self.supervision_timeout = timeout;
}
pub fn set_master_clock_accuracy(&mut self, accuracy: u8) {
self.master_clock_accuracy = accuracy;
}
pub fn set_link_type(&mut self, link_type: u8) {
self.link_type = link_type;
}
pub fn set_encryption_enabled(&mut self, enabled: u8) {
self.encryption_enabled = enabled;
}
pub fn set_rssi(&mut self, rssi: i8) {
self.rssi = rssi;
}
pub fn set_connected_at(&mut self, timestamp: u32) {
self.connected_at = timestamp;
}
pub fn set_last_activity(&mut self, timestamp: u32) {
self.last_activity = timestamp;
}
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct BluetoothSecurityInfo {
link_key: [u8; 16],
link_key_type: u8,
auth_requirements: u8,
io_capabilities: u8,
security_level: u8,
pin_length: u8,
link_key_valid: u8,
authenticated: u8,
encrypted: u8,
ssp_supported: u8,
mitm_required: u8,
_padding: [u8; 6],
}
impl Default for BluetoothSecurityInfo {
fn default() -> Self {
Self {
link_key: [0; 16],
link_key_type: 0,
auth_requirements: 0,
io_capabilities: 0,
security_level: 1,
pin_length: 0,
link_key_valid: 0,
authenticated: 0,
encrypted: 0,
ssp_supported: 0,
mitm_required: 0,
_padding: [0; 6],
}
}
}
impl BluetoothSecurityInfo {
#[must_use]
pub fn link_key(&self) -> &[u8; 16] {
&self.link_key
}
pub fn set_link_key(&mut self, key: [u8; 16]) {
self.link_key = key;
}
#[must_use]
pub fn link_key_type(&self) -> u8 {
self.link_key_type
}
pub fn set_link_key_type(&mut self, t: u8) {
self.link_key_type = t;
}
#[must_use]
pub fn auth_requirements(&self) -> u8 {
self.auth_requirements
}
pub fn set_auth_requirements(&mut self, r: u8) {
self.auth_requirements = r;
}
#[must_use]
pub fn io_capabilities(&self) -> u8 {
self.io_capabilities
}
pub fn set_io_capabilities(&mut self, c: u8) {
self.io_capabilities = c;
}
#[must_use]
pub fn security_level(&self) -> u8 {
self.security_level
}
pub fn set_security_level(&mut self, l: u8) {
self.security_level = l;
}
#[must_use]
pub fn pin_length(&self) -> u8 {
self.pin_length
}
pub fn set_pin_length(&mut self, l: u8) {
self.pin_length = l;
}
#[must_use]
pub fn link_key_valid(&self) -> u8 {
self.link_key_valid
}
pub fn set_link_key_valid(&mut self, v: u8) {
self.link_key_valid = v;
}
#[must_use]
pub fn authenticated(&self) -> u8 {
self.authenticated
}
pub fn set_authenticated(&mut self, v: u8) {
self.authenticated = v;
}
#[must_use]
pub fn encrypted(&self) -> u8 {
self.encrypted
}
pub fn set_encrypted(&mut self, v: u8) {
self.encrypted = v;
}
#[must_use]
pub fn ssp_supported(&self) -> u8 {
self.ssp_supported
}
pub fn set_ssp_supported(&mut self, v: u8) {
self.ssp_supported = v;
}
#[must_use]
pub fn mitm_required(&self) -> u8 {
self.mitm_required
}
pub fn set_mitm_required(&mut self, v: u8) {
self.mitm_required = v;
}
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct BluetoothDeviceInfo {
magic: u32, connection_count: u32, last_seen: u32, last_connected: u32, connection_params: BluetoothConnectionParams, security_info: BluetoothSecurityInfo, vendor_id: u16, product_id: u16, version: u16, mac_address: [u8; 6], class_of_device: [u8; 3], device_type: u8, flags: u8, device_name_len: u8, pairing_key_len: u8, device_name: [u8; 32], pairing_key: [u8; 64], _padding: [u8; 6], }
impl Default for BluetoothDeviceInfo {
fn default() -> Self {
Self {
magic: BLUETOOTH_CONFIG_MAGIC,
connection_count: 0,
last_seen: 0,
last_connected: 0,
connection_params: BluetoothConnectionParams::default(),
security_info: BluetoothSecurityInfo::default(),
vendor_id: 0,
product_id: 0,
version: 0,
mac_address: [0; 6],
class_of_device: [0; 3],
device_type: 0,
flags: 0,
device_name_len: 0,
pairing_key_len: 0,
device_name: [0; 32],
pairing_key: [0; 64],
_padding: [0; 6],
}
}
}
impl BluetoothDeviceInfo {
pub const DEVICE_TYPE_UNKNOWN: u8 = 0;
pub const DEVICE_TYPE_COMPUTER: u8 = 1;
pub const DEVICE_TYPE_PHONE: u8 = 2;
pub const DEVICE_TYPE_NETWORK: u8 = 3;
pub const DEVICE_TYPE_AUDIO: u8 = 4;
pub const DEVICE_TYPE_PERIPHERAL: u8 = 5;
pub const DEVICE_TYPE_IMAGING: u8 = 6;
pub const DEVICE_TYPE_WEARABLE: u8 = 7;
pub const DEVICE_TYPE_TOY: u8 = 8;
}
impl BluetoothDeviceInfo {
pub const FLAG_PAIRED: u8 = 0x01;
pub const FLAG_TRUSTED: u8 = 0x02;
pub const FLAG_AUDIO: u8 = 0x04;
pub const FLAG_INPUT: u8 = 0x08;
pub const FLAG_FILE_TRANSFER: u8 = 0x10;
pub const FLAG_CONNECTED: u8 = 0x20;
pub const FLAG_AUTO_RECONNECT: u8 = 0x40;
pub const FLAG_RECENTLY_DISCOVERED: u8 = 0x80;
}
impl BluetoothDeviceInfo {
pub fn new(mac_address: &[u8; 6], device_name: &[u8]) -> Result<Self, Error> {
if device_name.len() > 32 {
return Err(Error::InvalidBluetoothDeviceInfo);
}
let mut device = Self::default();
device.set_mac_address(mac_address);
device.set_device_name(device_name)?;
Ok(device)
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.magic == BLUETOOTH_CONFIG_MAGIC && !self.mac_address.iter().all(|&b| b == 0)
}
pub fn set_mac_address(&mut self, mac_address: &[u8; 6]) {
self.mac_address.copy_from_slice(mac_address);
}
#[allow(clippy::cast_possible_truncation)]
pub fn set_device_name(&mut self, device_name: &[u8]) -> Result<(), Error> {
if device_name.len() > 32 {
return Err(Error::InvalidBluetoothDeviceInfo);
}
self.device_name_len = device_name.len() as u8;
self.device_name.fill(0);
self.device_name[..device_name.len()].copy_from_slice(device_name);
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
pub fn set_pairing_key(&mut self, pairing_key: &[u8]) -> Result<(), Error> {
if pairing_key.len() > 64 {
return Err(Error::InvalidBluetoothDeviceInfo);
}
self.pairing_key_len = pairing_key.len() as u8;
self.pairing_key.fill(0);
self.pairing_key[..pairing_key.len()].copy_from_slice(pairing_key);
Ok(())
}
#[must_use]
pub fn pairing_key(&self) -> &[u8] {
&self.pairing_key[..self.pairing_key_len as usize]
}
pub fn set_device_info(&mut self, device_name: &[u8], pairing_key: &[u8]) -> Result<(), Error> {
self.set_device_name(device_name)?;
self.set_pairing_key(pairing_key)?;
Ok(())
}
pub fn set_class_of_device(&mut self, class_of_device: &[u8; 3]) {
self.class_of_device.copy_from_slice(class_of_device);
let major_class = (class_of_device[1] >> 2) & 0x1F;
self.device_type = match major_class {
1 => Self::DEVICE_TYPE_COMPUTER,
2 => Self::DEVICE_TYPE_PHONE,
3 => Self::DEVICE_TYPE_NETWORK,
4 => Self::DEVICE_TYPE_AUDIO,
5 => Self::DEVICE_TYPE_PERIPHERAL,
6 => Self::DEVICE_TYPE_IMAGING,
7 => Self::DEVICE_TYPE_WEARABLE,
8 => Self::DEVICE_TYPE_TOY,
_ => Self::DEVICE_TYPE_UNKNOWN,
};
}
pub fn update_connection_params(&mut self, params: &BluetoothConnectionParams) {
self.connection_params = *params;
self.connection_count += 1;
self.add_flag(Self::FLAG_CONNECTED);
}
pub fn update_security_info(&mut self, security: &BluetoothSecurityInfo) {
self.security_info = *security;
if security.authenticated != 0 {
self.add_flag(Self::FLAG_PAIRED);
}
}
pub fn set_flags(&mut self, flags: u8) {
self.flags = flags;
}
pub fn add_flag(&mut self, flag: u8) {
self.flags |= flag;
}
pub fn remove_flag(&mut self, flag: u8) {
self.flags &= !flag;
}
#[must_use]
pub fn has_flag(&self, flag: u8) -> bool {
(self.flags & flag) != 0
}
pub fn update_last_seen(&mut self, timestamp: u32) {
self.last_seen = timestamp;
}
pub fn update_last_connected(&mut self, timestamp: u32) {
self.last_connected = timestamp;
}
pub fn set_connection_count(&mut self, count: u32) {
self.connection_count = count;
}
pub fn increment_connection_count(&mut self) {
self.connection_count = self.connection_count.saturating_add(1);
}
pub fn set_last_connected(&mut self, timestamp: u32) {
self.last_connected = timestamp;
}
pub fn set_last_seen(&mut self, timestamp: u32) {
self.last_seen = timestamp;
}
#[must_use]
pub fn mac_address(&self) -> &[u8; 6] {
&self.mac_address
}
#[must_use]
pub fn device_name(&self) -> &[u8] {
&self.device_name[..self.device_name_len as usize]
}
#[must_use]
pub fn class_of_device(&self) -> &[u8; 3] {
&self.class_of_device
}
#[must_use]
pub fn device_type(&self) -> u8 {
self.device_type
}
#[must_use]
pub fn flags(&self) -> u8 {
self.flags
}
#[must_use]
pub fn connection_params(&self) -> &BluetoothConnectionParams {
&self.connection_params
}
#[must_use]
pub fn security_info(&self) -> &BluetoothSecurityInfo {
&self.security_info
}
#[must_use]
pub fn is_paired(&self) -> bool {
self.has_flag(Self::FLAG_PAIRED)
}
#[must_use]
pub fn is_connected(&self) -> bool {
self.has_flag(Self::FLAG_CONNECTED)
}
#[must_use]
pub fn is_trusted(&self) -> bool {
self.has_flag(Self::FLAG_TRUSTED)
}
#[must_use]
pub fn supports_auto_reconnect(&self) -> bool {
self.has_flag(Self::FLAG_AUTO_RECONNECT)
}
#[must_use]
pub fn vendor_id(&self) -> u16 {
self.vendor_id
}
#[must_use]
pub fn product_id(&self) -> u16 {
self.product_id
}
#[must_use]
pub fn version(&self) -> u16 {
self.version
}
#[must_use]
pub fn connection_count(&self) -> u32 {
self.connection_count
}
#[must_use]
pub fn last_seen(&self) -> u32 {
self.last_seen
}
#[must_use]
pub fn last_connected(&self) -> u32 {
self.last_connected
}
#[must_use]
pub fn device_name_len(&self) -> u8 {
self.device_name_len
}
#[must_use]
pub fn pairing_key_len(&self) -> u8 {
self.pairing_key_len
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
#[derive(Default)]
pub struct ConnHandle(u16);
impl ConnHandle {
#[must_use]
pub fn new(val: u16) -> Self {
assert!(val <= 0x0EFF, "Connection handle must be <= 0x0EFF");
Self(val)
}
#[must_use]
pub fn raw(self) -> u16 {
self.0
}
}
impl From<u16> for ConnHandle {
fn from(val: u16) -> Self {
Self::new(val)
}
}
impl From<ConnHandle> for u16 {
fn from(handle: ConnHandle) -> Self {
handle.raw()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BluetoothConnectionPhase {
Idle = 0,
Discovery = 1,
Connecting = 2,
Connected = 3,
Authenticating = 4,
SettingUpEncryption = 5,
FullyConnected = 6,
ServiceDiscovery = 7,
Ready = 8,
Maintaining = 9,
Reconnecting = 10,
Failed = 11,
Disconnecting = 12,
}
impl Default for BluetoothConnectionPhase {
fn default() -> Self {
Self::Idle
}
}
impl BluetoothConnectionPhase {
#[must_use]
pub fn is_connected(&self) -> bool {
matches!(
self,
Self::Connected
| Self::Authenticating
| Self::SettingUpEncryption
| Self::FullyConnected
| Self::ServiceDiscovery
| Self::Ready
| Self::Maintaining
)
}
#[must_use]
pub fn is_secure(&self) -> bool {
matches!(
self,
Self::FullyConnected | Self::ServiceDiscovery | Self::Ready | Self::Maintaining
)
}
#[must_use]
pub fn is_ready(&self) -> bool {
matches!(self, Self::Ready | Self::Maintaining)
}
}