renik/bluetooth.rs
1//! # Bluetooth Device Management
2//!
3//! This module provides comprehensive Bluetooth device management capabilities
4//! for embedded systems, including device information storage, connection state
5//! tracking, and finite state machine (FSM) management.
6//!
7//! ## Features
8//!
9//! - **Device Information**: Store and manage individual Bluetooth device configurations
10//! - **Device Lists**: Manage multiple paired devices (up to 10)
11//! - **Connection State**: Track real-time connection status with FSM support
12//! - **Type-Safe Handles**: Validated connection handle wrapper
13//! - **Security Management**: Store pairing keys and security information
14//! - **Memory Efficient**: Fixed-size structures optimized for embedded use
15//!
16//! ## Core Structures
17//!
18//! - [`BluetoothDeviceInfo`]: Complete device information including pairing data
19//! - [`BluetoothDeviceList`]: Container for multiple device configurations (max 10)
20//! - [`BluetoothConnectionState`]: Real-time connection tracking with FSM
21//! - [`ConnHandle`]: Type-safe connection handle wrapper
22//! - [`BluetoothConnectionPhase`]: FSM phases for connection lifecycle
23//!
24//! ## Finite State Machine
25//!
26//! The Bluetooth connection FSM supports 13 distinct phases:
27//!
28//! ```text
29//! 0 Idle → 1 Discovery
30//! 1 Discovery → 2 Connecting
31//! 2 Connecting → 3 Connected | 11 Failed
32//! 3 Connected → 4 Authenticating | 7 ServiceDiscovery | 12 Disconnecting
33//! 4 Authenticating → 5 SettingUpEncryption | 11 Failed | 12 Disconnecting
34//! 5 SettingUpEncryption → 6 FullyConnected | 11 Failed | 12 Disconnecting
35//! 6 FullyConnected → 7 ServiceDiscovery | 8 Ready | 12 Disconnecting
36//! 7 ServiceDiscovery → 8 Ready | 11 Failed | 12 Disconnecting
37//! 8 Ready → 9 Maintaining | 12 Disconnecting
38//! 9 Maintaining → 10 Reconnecting | 12 Disconnecting
39//! 10 Reconnecting → 2 Connecting | 11 Failed
40//! 11 Failed → 10 Reconnecting
41//! 12 Disconnecting → 0 Idle (only allowed transition)
42//! ```
43//!
44//! See `BluetoothConnectionPhase` and `BluetoothConnectionState::is_valid_transition` for details.
45//!
46//! ## Memory Layout
47//!
48//! All structures use `#[repr(C)]` layout for reliable serialization:
49//! - Predictable field ordering
50//! - No hidden padding (except explicit padding fields)
51//! - Cross-platform compatibility
52//! - Suitable for persistent storage
53//!
54//! ## Examples
55//!
56//! ### Basic Device Management
57//! ```
58//! use renik::{BluetoothDeviceInfo, BluetoothDeviceList};
59//!
60//! // Create a device
61//! let mac_addr = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC];
62//! let mut device = BluetoothDeviceInfo::new(&mac_addr, b"My Speaker")?;
63//! device.set_pairing_key(b"audio_key_123")?;
64//! device.add_flag(BluetoothDeviceInfo::FLAG_AUDIO);
65//!
66//! // Add to device list
67//! let mut device_list = BluetoothDeviceList::default();
68//! device_list.add_device(device)?;
69//! # Ok::<(), renik::Error>(())
70//! ```
71//!
72//! ### Connection State Tracking
73//! ```
74//! use renik::{BluetoothConnectionState, BluetoothConnectionPhase, ConnHandle};
75//!
76//! let mut connection = BluetoothConnectionState::default();
77//!
78//! // FSM transitions
79//! assert!(connection.advance_to_phase(BluetoothConnectionPhase::Discovery));
80//! assert!(connection.advance_to_phase(BluetoothConnectionPhase::Connecting));
81//! assert!(connection.advance_to_phase(BluetoothConnectionPhase::Connected));
82//!
83//! // Set connection details
84//! connection.set_connection_handle(Some(ConnHandle::new(0x0042)));
85//! connection.set_link_quality(85);
86//! # Ok::<(), renik::Error>(())
87//! ```
88
89use crate::Error;
90use bytemuck::{Pod, Zeroable};
91
92/// Magic number used to validate Bluetooth device configuration structures
93/// Value: 0x42544C45 (ASCII "BTLE")
94const BLUETOOTH_CONFIG_MAGIC: u32 = 0x4254_4C45;
95
96/// Magic number for Bluetooth device list
97/// Value: 0x42544C53 (ASCII "BTLS")
98const BLUETOOTH_DEVICE_LIST_MAGIC: u32 = 0x4254_4C53;
99
100/// Magic number for Bluetooth connection state
101/// Value: 0x42544353 (ASCII "BTCS")
102const BLUETOOTH_CONNECTION_STATE_MAGIC: u32 = 0x4254_4353;
103
104/// Bluetooth device list structure
105///
106/// This structure represents a list of up to 10 Bluetooth devices, including their
107/// configuration and connection status. It's used for managing multiple
108/// Bluetooth devices in embedded systems.
109///
110/// # Memory Layout
111/// The structure uses `#[repr(C)]` to ensure predictable memory layout,
112/// making it suitable for serialization and inter-process communication.
113///
114/// # Security Note
115/// This structure may store sensitive pairing data. Ensure proper
116/// memory protection and secure storage mechanisms when persisting this data.
117///
118/// # Fields
119/// - `magic`: Structure validation (0x42544C53)
120/// - `devices`: Array of up to 10 device configurations
121/// - `device_count`: Number of valid devices in the list
122/// - `_padding`: Ensures 4-byte alignment
123///
124/// # Errors
125/// - `Error::DeviceListFull` if adding more than 10 devices
126/// - `Error::IndexOutOfBounds` for invalid indices
127///
128/// # Examples
129/// ```
130/// use renik::{BluetoothDeviceInfo, BluetoothDeviceList};
131///
132/// let mac_addr1 = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC];
133/// let mac_addr2 = [0x98, 0x76, 0x54, 0x32, 0x10, 0xFE];
134/// let device1 = BluetoothDeviceInfo::new(&mac_addr1, b"Device 1").unwrap();
135/// let device2 = BluetoothDeviceInfo::new(&mac_addr2, b"Device 2").unwrap();
136/// let mut device_list = BluetoothDeviceList::default();
137/// device_list.add_device(device1).unwrap();
138/// device_list.add_device(device2).unwrap();
139/// assert_eq!(device_list.len(), 2);
140/// ```
141#[derive(Debug, Clone, Copy, Pod, Zeroable)]
142#[repr(C, packed)]
143pub struct BluetoothDeviceList {
144 /// Magic number for structure validation (0x42544C53)
145 magic: u32, // 4-byte aligned
146 /// Array of Bluetooth device configurations
147 devices: [BluetoothDeviceInfo; 10], // 4-byte aligned
148 /// Number of devices currently in the list
149 device_count: u8, // 1-byte aligned
150 /// Padding to ensure proper alignment
151 _padding: [u8; 3], // Ensures 4-byte alignment
152}
153
154impl Default for BluetoothDeviceList {
155 /// Creates a new Bluetooth device list with default values
156 ///
157 /// The structure is initialized with the correct magic number
158 /// and an empty device list.
159 fn default() -> Self {
160 Self {
161 magic: BLUETOOTH_DEVICE_LIST_MAGIC,
162 devices: Default::default(),
163 device_count: 0,
164 _padding: [0; 3],
165 }
166 }
167}
168
169impl BluetoothDeviceList {
170 /// Adds a Bluetooth device configuration to the list
171 ///
172 /// # Parameters
173 /// - `device_config`: Bluetooth device configuration
174 ///
175 /// # Returns
176 /// - `Ok(())` if the device was added successfully
177 /// - `Err(Error)` if the device list is full
178 ///
179 /// # Errors
180 /// Returns `Error::DeviceListFull` if the device list is already at maximum capacity.
181 pub fn add_device(&mut self, device_config: BluetoothDeviceInfo) -> Result<(), Error> {
182 if self.device_count as usize >= self.devices.len() {
183 return Err(Error::DeviceListFull);
184 }
185
186 self.devices[self.device_count as usize] = device_config;
187 self.device_count += 1;
188
189 Ok(())
190 }
191
192 /// Removes a Bluetooth device configuration from the list
193 ///
194 /// # Parameters
195 /// - `index`: Index of the device to remove (0-based)
196 ///
197 /// # Returns
198 /// - `Ok(())` if the device was removed successfully
199 /// - `Err(Error)` if the index is out of bounds
200 ///
201 /// # Errors
202 /// Returns `Error::IndexOutOfBounds` if the specified index is not valid.
203 pub fn remove_device(&mut self, index: usize) -> Result<(), Error> {
204 if index >= self.device_count as usize {
205 return Err(Error::IndexOutOfBounds);
206 }
207
208 // Shift devices down to fill the gap
209 for i in index..(self.device_count as usize - 1) {
210 self.devices[i] = self.devices[i + 1];
211 }
212
213 self.device_count -= 1;
214
215 Ok(())
216 }
217
218 /// Returns a reference to a Bluetooth device configuration
219 ///
220 /// # Parameters
221 /// - `index`: Index of the device to retrieve (0-based)
222 ///
223 /// # Returns
224 /// - `Ok(&BluetoothDeviceInfo)` if the index is valid
225 /// - `Err(Error)` if the index is out of bounds
226 ///
227 /// # Errors
228 /// Returns `Error::IndexOutOfBounds` if the specified index is not valid.
229 pub fn device(&self, index: usize) -> Result<&BluetoothDeviceInfo, Error> {
230 if index >= self.device_count as usize {
231 return Err(Error::IndexOutOfBounds);
232 }
233
234 Ok(&self.devices[index])
235 }
236
237 /// Returns the number of devices in the list
238 ///
239 /// # Returns
240 /// The current device count
241 #[must_use]
242 pub fn len(&self) -> usize {
243 self.device_count as usize
244 }
245
246 /// Checks if the device list is empty
247 ///
248 /// # Returns
249 /// - `true` if there are no devices in the list
250 /// - `false` otherwise
251 #[must_use]
252 pub fn is_empty(&self) -> bool {
253 self.device_count == 0
254 }
255}
256
257/// Bluetooth connection state structure
258///
259/// This structure represents the connection state of a Bluetooth device,
260/// including information about the remote device, connection status,
261/// and link quality. It's used for managing Bluetooth connections
262/// in embedded systems.
263///
264/// # Memory Layout
265/// The structure uses `#[repr(C)]` to ensure predictable memory layout,
266/// making it suitable for serialization and inter-process communication.
267///
268/// # Security Note
269/// This structure may store sensitive connection data. Ensure proper
270/// memory protection and secure storage mechanisms when persisting this data.
271///
272/// # Fields
273/// - `magic`: Structure validation (0x42544353)
274/// - `device_config`: Bluetooth device configuration
275/// - `connection_flags`: Bitfield for connection/authentication
276/// - `link_quality`: RSSI/LQI/etc.
277/// - `connection_phase`: Current FSM phase (see `BluetoothConnectionPhase`)
278/// - `_padding`: Ensures 4-byte alignment
279///
280/// # Examples
281/// ```
282/// use renik::{BluetoothDeviceInfo, BluetoothConnectionState};
283///
284/// let mac_addr = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC];
285/// let device = BluetoothDeviceInfo::new(&mac_addr, b"My Device").unwrap();
286/// let mut connection_state = BluetoothConnectionState::default();
287/// connection_state.set_remote_device(device);
288/// connection_state.set_connected(true);
289/// assert!(connection_state.is_connected());
290/// ```
291#[derive(Debug, Clone, Copy, Pod, Zeroable)]
292#[repr(C, packed)]
293pub struct BluetoothConnectionState {
294 /// Magic number for structure validation (0x42544353)
295 magic: u32, // 4-byte aligned
296 /// Bluetooth device configuration
297 device_config: BluetoothDeviceInfo, // 4-byte aligned
298 /// Connection status flags
299 connection_flags: u8, // 1-byte aligned
300 /// Link quality (RSSI, LQI, etc.)
301 link_quality: u8, // 1-byte aligned
302 /// Current connection phase
303 connection_phase: u8, // 1-byte aligned (maps to BluetoothConnectionPhase)
304 /// Padding to ensure proper alignment
305 _padding: [u8; 1], // Ensures 4-byte alignment
306}
307
308impl Default for BluetoothConnectionState {
309 /// Creates a new Bluetooth connection state with default values
310 ///
311 /// The structure is initialized with the correct magic number
312 /// and a disconnected state.
313 fn default() -> Self {
314 Self {
315 magic: BLUETOOTH_CONNECTION_STATE_MAGIC,
316 device_config: BluetoothDeviceInfo::default(),
317 connection_flags: 0,
318 link_quality: 0,
319 connection_phase: BluetoothConnectionPhase::Idle as u8,
320 _padding: [0; 1],
321 }
322 }
323}
324
325impl BluetoothConnectionState {
326 /// Sets the remote Bluetooth device configuration
327 ///
328 /// # Parameters
329 /// - `device_config`: Bluetooth device configuration
330 pub fn set_remote_device(&mut self, device_config: BluetoothDeviceInfo) {
331 self.device_config = device_config;
332 }
333
334 /// Sets the connection status
335 ///
336 /// # Parameters
337 /// - `connected`: `true` if connected, `false` if disconnected
338 pub fn set_connected(&mut self, connected: bool) {
339 if connected {
340 self.connection_flags |= 0x01;
341 } else {
342 self.connection_flags &= !0x01;
343 }
344 }
345
346 /// Sets the link quality
347 ///
348 /// # Parameters
349 /// - `quality`: Link quality value (0-255)
350 pub fn set_link_quality(&mut self, quality: u8) {
351 self.link_quality = quality;
352 }
353
354 /// Returns the remote Bluetooth device configuration
355 ///
356 /// # Returns
357 /// A reference to the Bluetooth device configuration
358 #[must_use]
359 pub fn remote_device(&self) -> &BluetoothDeviceInfo {
360 &self.device_config
361 }
362
363 /// Returns the connection status
364 ///
365 /// # Returns
366 /// - `true` if connected
367 /// - `false` if disconnected
368 #[must_use]
369 pub fn is_connected(&self) -> bool {
370 (self.connection_flags & 0x01) != 0
371 }
372
373 /// Returns the link quality
374 ///
375 /// # Returns
376 /// The link quality value (0-255)
377 #[must_use]
378 pub fn link_quality(&self) -> u8 {
379 self.link_quality
380 }
381
382 /// Sets the authentication status
383 ///
384 /// # Parameters
385 /// - `authenticated`: `true` if authenticated, `false` if not
386 pub fn set_authenticated(&mut self, authenticated: bool) {
387 if authenticated {
388 self.connection_flags |= 0x02;
389 } else {
390 self.connection_flags &= !0x02;
391 }
392 }
393
394 /// Returns the authentication status
395 ///
396 /// # Returns
397 /// - `true` if authenticated
398 /// - `false` if not authenticated
399 #[must_use]
400 pub fn is_authenticated(&self) -> bool {
401 (self.connection_flags & 0x02) != 0
402 }
403
404 /// Sets the remote device address
405 ///
406 /// # Parameters
407 /// - `address`: 6-byte Bluetooth address
408 pub fn set_remote_device_address(&mut self, address: [u8; 6]) {
409 self.device_config.mac_address = address;
410 }
411
412 /// Gets the remote device address
413 ///
414 /// # Returns
415 /// Optional 6-byte Bluetooth address
416 #[must_use]
417 pub fn remote_device_address(&self) -> Option<[u8; 6]> {
418 Some(self.device_config.mac_address)
419 }
420
421 /// Sets the connection handle
422 ///
423 /// # Parameters
424 /// - `handle`: Connection handle (`ConnHandle`)
425 pub fn set_connection_handle(&mut self, handle: Option<ConnHandle>) {
426 self.device_config.connection_params.connection_handle = handle.unwrap_or_default();
427 }
428
429 /// Gets the connection handle
430 ///
431 /// # Returns
432 /// Optional connection handle
433 #[must_use]
434 pub fn connection_handle(&self) -> Option<ConnHandle> {
435 if self.device_config.connection_params.connection_handle.raw() == 0 {
436 None
437 } else {
438 Some(self.device_config.connection_params.connection_handle)
439 }
440 }
441
442 /// Sets the link type
443 ///
444 /// # Parameters
445 /// - `link_type`: Link type (0x01 = ACL, 0x02 = SCO)
446 pub fn set_link_type(&mut self, link_type: u8) {
447 self.device_config.connection_params.link_type = link_type;
448 }
449
450 /// Gets the link type
451 ///
452 /// # Returns
453 /// Link type value
454 #[must_use]
455 pub fn link_type(&self) -> u8 {
456 self.device_config.connection_params.link_type
457 }
458
459 /// Sets the connection phase
460 ///
461 /// # Parameters
462 /// - `phase`: Connection phase
463 pub fn set_connection_phase(&mut self, phase: BluetoothConnectionPhase) {
464 self.connection_phase = phase as u8;
465 }
466
467 /// Gets the connection phase
468 ///
469 /// # Returns
470 /// The current connection phase
471 #[must_use]
472 pub fn connection_phase(&self) -> BluetoothConnectionPhase {
473 // Convert u8 back to enum, default to Idle if invalid
474 match self.connection_phase {
475 1 => BluetoothConnectionPhase::Discovery,
476 2 => BluetoothConnectionPhase::Connecting,
477 3 => BluetoothConnectionPhase::Connected,
478 4 => BluetoothConnectionPhase::Authenticating,
479 5 => BluetoothConnectionPhase::SettingUpEncryption,
480 6 => BluetoothConnectionPhase::FullyConnected,
481 7 => BluetoothConnectionPhase::ServiceDiscovery,
482 8 => BluetoothConnectionPhase::Ready,
483 9 => BluetoothConnectionPhase::Maintaining,
484 10 => BluetoothConnectionPhase::Reconnecting,
485 11 => BluetoothConnectionPhase::Failed,
486 12 => BluetoothConnectionPhase::Disconnecting,
487 _ => BluetoothConnectionPhase::Idle, // Default for 0 and invalid values
488 }
489 }
490
491 /// Advances to the next connection phase
492 ///
493 /// # Parameters
494 /// - `next_phase`: The next phase to transition to
495 ///
496 /// # Returns
497 /// - `true` if the transition is valid
498 /// - `false` if the transition is not allowed
499 pub fn advance_to_phase(&mut self, next_phase: BluetoothConnectionPhase) -> bool {
500 let current = self.connection_phase();
501
502 // Simple rule-based validation instead of exhaustive matching
503 let valid_transition = next_phase == BluetoothConnectionPhase::Idle
504 || Self::is_valid_transition(current, next_phase);
505
506 if valid_transition {
507 self.set_connection_phase(next_phase);
508 }
509
510 valid_transition
511 }
512
513 /// Helper function to check if a state transition is valid
514 fn is_valid_transition(
515 current: BluetoothConnectionPhase,
516 next: BluetoothConnectionPhase,
517 ) -> bool {
518 use BluetoothConnectionPhase::{
519 Authenticating, Connected, Connecting, Disconnecting, Discovery, Failed,
520 FullyConnected, Idle, Maintaining, Ready, Reconnecting, ServiceDiscovery,
521 SettingUpEncryption,
522 };
523
524 match current {
525 Idle => matches!(next, Discovery | Connecting),
526 Discovery => next == Connecting,
527 Connecting => matches!(next, Connected | Failed),
528 Connected => matches!(next, Authenticating | ServiceDiscovery | Disconnecting),
529 Authenticating => matches!(next, SettingUpEncryption | Failed | Disconnecting),
530 SettingUpEncryption => matches!(next, FullyConnected | Failed | Disconnecting),
531 FullyConnected => matches!(next, ServiceDiscovery | Ready | Disconnecting),
532 ServiceDiscovery => matches!(next, Ready | Failed | Disconnecting),
533 Ready => matches!(next, Maintaining | Disconnecting),
534 Maintaining => matches!(next, Reconnecting | Disconnecting),
535 Reconnecting => matches!(next, Connecting | Failed),
536 Failed => next == Reconnecting,
537 Disconnecting => false, // Only to Idle, handled above
538 }
539 }
540}
541
542/// Connection parameters for Bluetooth devices
543#[derive(Debug, Clone, Copy, Pod, Zeroable)]
544#[repr(C, packed)]
545pub struct BluetoothConnectionParams {
546 connection_handle: ConnHandle,
547 connection_interval: u16,
548 connection_latency: u16,
549 supervision_timeout: u16,
550 master_clock_accuracy: u8,
551 link_type: u8,
552 encryption_enabled: u8,
553 rssi: i8,
554 connected_at: u32,
555 last_activity: u32,
556 _padding: [u8; 4],
557}
558
559impl Default for BluetoothConnectionParams {
560 fn default() -> Self {
561 Self {
562 connection_handle: ConnHandle::default(),
563 connection_interval: 0,
564 connection_latency: 0,
565 supervision_timeout: 0,
566 master_clock_accuracy: 0,
567 link_type: 0,
568 encryption_enabled: 0,
569 rssi: -127,
570 connected_at: 0,
571 last_activity: 0,
572 _padding: [0; 4],
573 }
574 }
575}
576
577impl BluetoothConnectionParams {
578 /// Returns the connection handle assigned by the controller
579 #[must_use]
580 pub fn connection_handle(&self) -> ConnHandle {
581 self.connection_handle
582 }
583
584 /// Returns the connection interval in 1.25ms units
585 #[must_use]
586 pub fn connection_interval(&self) -> u16 {
587 self.connection_interval
588 }
589
590 /// Returns the connection latency
591 #[must_use]
592 pub fn connection_latency(&self) -> u16 {
593 self.connection_latency
594 }
595
596 /// Returns the supervision timeout in 10ms units
597 #[must_use]
598 pub fn supervision_timeout(&self) -> u16 {
599 self.supervision_timeout
600 }
601
602 /// Returns the master clock accuracy
603 #[must_use]
604 pub fn master_clock_accuracy(&self) -> u8 {
605 self.master_clock_accuracy
606 }
607
608 /// Returns the link type (0x01 = ACL, 0x02 = SCO)
609 #[must_use]
610 pub fn link_type(&self) -> u8 {
611 self.link_type
612 }
613
614 /// Returns whether encryption is enabled (0x00 = disabled, 0x01 = enabled)
615 #[must_use]
616 pub fn encryption_enabled(&self) -> u8 {
617 self.encryption_enabled
618 }
619
620 /// Returns the RSSI value (-127 to 127 dBm)
621 #[must_use]
622 pub fn rssi(&self) -> i8 {
623 self.rssi
624 }
625
626 /// Returns the connection timestamp (seconds since epoch)
627 #[must_use]
628 pub fn connected_at(&self) -> u32 {
629 self.connected_at
630 }
631
632 /// Returns the last activity timestamp (seconds since epoch)
633 #[must_use]
634 pub fn last_activity(&self) -> u32 {
635 self.last_activity
636 }
637
638 /// Sets the connection handle assigned by the controller
639 pub fn set_connection_handle(&mut self, handle: ConnHandle) {
640 self.connection_handle = handle;
641 }
642
643 /// Sets the connection interval in 1.25ms units
644 pub fn set_connection_interval(&mut self, interval: u16) {
645 self.connection_interval = interval;
646 }
647
648 /// Sets the connection latency
649 pub fn set_connection_latency(&mut self, latency: u16) {
650 self.connection_latency = latency;
651 }
652
653 /// Sets the supervision timeout in 10ms units
654 pub fn set_supervision_timeout(&mut self, timeout: u16) {
655 self.supervision_timeout = timeout;
656 }
657
658 /// Sets the master clock accuracy
659 pub fn set_master_clock_accuracy(&mut self, accuracy: u8) {
660 self.master_clock_accuracy = accuracy;
661 }
662
663 /// Sets the link type (0x01 = ACL, 0x02 = SCO)
664 pub fn set_link_type(&mut self, link_type: u8) {
665 self.link_type = link_type;
666 }
667
668 /// Sets whether encryption is enabled (0x00 = disabled, 0x01 = enabled)
669 pub fn set_encryption_enabled(&mut self, enabled: u8) {
670 self.encryption_enabled = enabled;
671 }
672
673 /// Sets the RSSI value (-127 to 127 dBm)
674 pub fn set_rssi(&mut self, rssi: i8) {
675 self.rssi = rssi;
676 }
677
678 /// Sets the connection timestamp (seconds since epoch)
679 pub fn set_connected_at(&mut self, timestamp: u32) {
680 self.connected_at = timestamp;
681 }
682
683 /// Sets the last activity timestamp (seconds since epoch)
684 pub fn set_last_activity(&mut self, timestamp: u32) {
685 self.last_activity = timestamp;
686 }
687}
688
689/// Security information for Bluetooth connections
690#[derive(Debug, Clone, Copy, Pod, Zeroable)]
691#[repr(C, packed)]
692pub struct BluetoothSecurityInfo {
693 link_key: [u8; 16],
694 link_key_type: u8,
695 auth_requirements: u8,
696 io_capabilities: u8,
697 security_level: u8,
698 pin_length: u8,
699 link_key_valid: u8,
700 authenticated: u8,
701 encrypted: u8,
702 ssp_supported: u8,
703 mitm_required: u8,
704 _padding: [u8; 6],
705}
706
707impl Default for BluetoothSecurityInfo {
708 fn default() -> Self {
709 Self {
710 link_key: [0; 16],
711 link_key_type: 0,
712 auth_requirements: 0,
713 io_capabilities: 0,
714 security_level: 1,
715 pin_length: 0,
716 link_key_valid: 0,
717 authenticated: 0,
718 encrypted: 0,
719 ssp_supported: 0,
720 mitm_required: 0,
721 _padding: [0; 6],
722 }
723 }
724}
725
726impl BluetoothSecurityInfo {
727 /// Returns the link key for authentication (16 bytes)
728 #[must_use]
729 pub fn link_key(&self) -> &[u8; 16] {
730 &self.link_key
731 }
732 /// Sets the link key for authentication (16 bytes)
733 pub fn set_link_key(&mut self, key: [u8; 16]) {
734 self.link_key = key;
735 }
736 #[must_use]
737 pub fn link_key_type(&self) -> u8 {
738 self.link_key_type
739 }
740 pub fn set_link_key_type(&mut self, t: u8) {
741 self.link_key_type = t;
742 }
743 #[must_use]
744 pub fn auth_requirements(&self) -> u8 {
745 self.auth_requirements
746 }
747 pub fn set_auth_requirements(&mut self, r: u8) {
748 self.auth_requirements = r;
749 }
750 #[must_use]
751 pub fn io_capabilities(&self) -> u8 {
752 self.io_capabilities
753 }
754 pub fn set_io_capabilities(&mut self, c: u8) {
755 self.io_capabilities = c;
756 }
757 #[must_use]
758 pub fn security_level(&self) -> u8 {
759 self.security_level
760 }
761 pub fn set_security_level(&mut self, l: u8) {
762 self.security_level = l;
763 }
764 #[must_use]
765 pub fn pin_length(&self) -> u8 {
766 self.pin_length
767 }
768 pub fn set_pin_length(&mut self, l: u8) {
769 self.pin_length = l;
770 }
771 #[must_use]
772 pub fn link_key_valid(&self) -> u8 {
773 self.link_key_valid
774 }
775 pub fn set_link_key_valid(&mut self, v: u8) {
776 self.link_key_valid = v;
777 }
778 #[must_use]
779 pub fn authenticated(&self) -> u8 {
780 self.authenticated
781 }
782 pub fn set_authenticated(&mut self, v: u8) {
783 self.authenticated = v;
784 }
785 #[must_use]
786 pub fn encrypted(&self) -> u8 {
787 self.encrypted
788 }
789 pub fn set_encrypted(&mut self, v: u8) {
790 self.encrypted = v;
791 }
792 #[must_use]
793 pub fn ssp_supported(&self) -> u8 {
794 self.ssp_supported
795 }
796 pub fn set_ssp_supported(&mut self, v: u8) {
797 self.ssp_supported = v;
798 }
799 #[must_use]
800 pub fn mitm_required(&self) -> u8 {
801 self.mitm_required
802 }
803 pub fn set_mitm_required(&mut self, v: u8) {
804 self.mitm_required = v;
805 }
806}
807
808/// Complete Bluetooth device information for storage
809#[derive(Debug, Clone, Copy, Pod, Zeroable)]
810#[repr(C, packed)]
811pub struct BluetoothDeviceInfo {
812 magic: u32, // 4
813 connection_count: u32, // 4
814 last_seen: u32, // 4
815 last_connected: u32, // 4
816 connection_params: BluetoothConnectionParams, // 40
817 security_info: BluetoothSecurityInfo, // 32
818 vendor_id: u16, // 2
819 product_id: u16, // 2
820 version: u16, // 2
821 mac_address: [u8; 6], // 6
822 class_of_device: [u8; 3], // 3
823 device_type: u8, // 1
824 flags: u8, // 1
825 device_name_len: u8, // 1
826 pairing_key_len: u8, // 1
827 device_name: [u8; 32], // 32
828 pairing_key: [u8; 64], // 64
829 _padding: [u8; 6], // 6 (to align struct to 4 bytes)
830}
831
832impl Default for BluetoothDeviceInfo {
833 fn default() -> Self {
834 Self {
835 magic: BLUETOOTH_CONFIG_MAGIC,
836 connection_count: 0,
837 last_seen: 0,
838 last_connected: 0,
839 connection_params: BluetoothConnectionParams::default(),
840 security_info: BluetoothSecurityInfo::default(),
841 vendor_id: 0,
842 product_id: 0,
843 version: 0,
844 mac_address: [0; 6],
845 class_of_device: [0; 3],
846 device_type: 0,
847 flags: 0,
848 device_name_len: 0,
849 pairing_key_len: 0,
850 device_name: [0; 32],
851 pairing_key: [0; 64],
852 _padding: [0; 6],
853 }
854 }
855}
856
857/// Device type constants based on Class of Device major class
858impl BluetoothDeviceInfo {
859 pub const DEVICE_TYPE_UNKNOWN: u8 = 0;
860 pub const DEVICE_TYPE_COMPUTER: u8 = 1;
861 pub const DEVICE_TYPE_PHONE: u8 = 2;
862 pub const DEVICE_TYPE_NETWORK: u8 = 3;
863 pub const DEVICE_TYPE_AUDIO: u8 = 4;
864 pub const DEVICE_TYPE_PERIPHERAL: u8 = 5;
865 pub const DEVICE_TYPE_IMAGING: u8 = 6;
866 pub const DEVICE_TYPE_WEARABLE: u8 = 7;
867 pub const DEVICE_TYPE_TOY: u8 = 8;
868}
869
870/// Device flags for `BluetoothDeviceInfo`
871impl BluetoothDeviceInfo {
872 /// Device is paired
873 pub const FLAG_PAIRED: u8 = 0x01;
874 /// Device is trusted
875 pub const FLAG_TRUSTED: u8 = 0x02;
876 /// Device supports audio
877 pub const FLAG_AUDIO: u8 = 0x04;
878 /// Device supports input (keyboard/mouse)
879 pub const FLAG_INPUT: u8 = 0x08;
880 /// Device supports file transfer
881 pub const FLAG_FILE_TRANSFER: u8 = 0x10;
882 /// Device is currently connected
883 pub const FLAG_CONNECTED: u8 = 0x20;
884 /// Device supports automatic reconnection
885 pub const FLAG_AUTO_RECONNECT: u8 = 0x40;
886 /// Device was discovered recently
887 pub const FLAG_RECENTLY_DISCOVERED: u8 = 0x80;
888}
889
890impl BluetoothDeviceInfo {
891 /// Creates a new Bluetooth device info with basic information
892 ///
893 /// # Parameters
894 /// - `mac_address`: Bluetooth MAC address as 6-byte array
895 /// - `device_name`: Device name as byte slice (max 32 bytes)
896 ///
897 /// # Returns
898 /// - `Ok(BluetoothDeviceInfo)` if the device info was created successfully
899 /// - `Err(Error)` if the device name length exceeded the maximum allowed
900 ///
901 /// # Errors
902 /// Returns `Error::InvalidBluetoothDeviceInfo` if the device name exceeds 32 bytes.
903 pub fn new(mac_address: &[u8; 6], device_name: &[u8]) -> Result<Self, Error> {
904 if device_name.len() > 32 {
905 return Err(Error::InvalidBluetoothDeviceInfo);
906 }
907
908 let mut device = Self::default();
909 device.set_mac_address(mac_address);
910 device.set_device_name(device_name)?;
911 Ok(device)
912 }
913
914 /// Validates the device info structure
915 #[must_use]
916 pub fn is_valid(&self) -> bool {
917 self.magic == BLUETOOTH_CONFIG_MAGIC && !self.mac_address.iter().all(|&b| b == 0)
918 }
919
920 /// Sets the MAC address
921 pub fn set_mac_address(&mut self, mac_address: &[u8; 6]) {
922 self.mac_address.copy_from_slice(mac_address);
923 }
924
925 /// Sets the device name
926 ///
927 /// # Parameters
928 /// - `device_name`: Device name as byte slice (max 32 bytes)
929 ///
930 /// # Returns
931 /// - `Ok(())` if the device name was set successfully
932 /// - `Err(Error)` if the device name length exceeded the maximum allowed
933 ///
934 /// # Errors
935 /// Returns `Error::InvalidBluetoothDeviceInfo` if the device name exceeds 32 bytes.
936 #[allow(clippy::cast_possible_truncation)]
937 pub fn set_device_name(&mut self, device_name: &[u8]) -> Result<(), Error> {
938 if device_name.len() > 32 {
939 return Err(Error::InvalidBluetoothDeviceInfo);
940 }
941
942 self.device_name_len = device_name.len() as u8;
943 self.device_name.fill(0);
944 self.device_name[..device_name.len()].copy_from_slice(device_name);
945 Ok(())
946 }
947
948 /// Sets the pairing key/PIN for the device
949 ///
950 /// # Parameters
951 /// - `pairing_key`: Pairing key/PIN as byte slice (max 64 bytes)
952 ///
953 /// # Returns
954 /// - `Ok(())` if the pairing key was set successfully
955 /// - `Err(Error)` if the pairing key length exceeded the maximum allowed
956 ///
957 /// # Errors
958 /// Returns `Error::InvalidBluetoothDeviceInfo` if the pairing key exceeds 64 bytes.
959 #[allow(clippy::cast_possible_truncation)]
960 pub fn set_pairing_key(&mut self, pairing_key: &[u8]) -> Result<(), Error> {
961 if pairing_key.len() > 64 {
962 return Err(Error::InvalidBluetoothDeviceInfo);
963 }
964
965 self.pairing_key_len = pairing_key.len() as u8;
966 self.pairing_key.fill(0);
967 self.pairing_key[..pairing_key.len()].copy_from_slice(pairing_key);
968 Ok(())
969 }
970
971 /// Returns the stored pairing key as a byte slice
972 ///
973 /// # Returns
974 /// A slice containing only the valid pairing key bytes (length determined by `pairing_key_len`)
975 #[must_use]
976 pub fn pairing_key(&self) -> &[u8] {
977 &self.pairing_key[..self.pairing_key_len as usize]
978 }
979
980 /// Sets both device name and pairing key at once
981 ///
982 /// # Parameters
983 /// - `device_name`: Device name as byte slice (max 32 bytes)
984 /// - `pairing_key`: Pairing key/PIN as byte slice (max 64 bytes)
985 ///
986 /// # Returns
987 /// - `Ok(())` if both were set successfully
988 /// - `Err(Error)` if either length exceeded the maximum allowed
989 ///
990 /// # Errors
991 /// Returns `Error::InvalidBluetoothDeviceInfo` if either the device name exceeds 32 bytes
992 /// or the pairing key exceeds 64 bytes.
993 pub fn set_device_info(&mut self, device_name: &[u8], pairing_key: &[u8]) -> Result<(), Error> {
994 self.set_device_name(device_name)?;
995 self.set_pairing_key(pairing_key)?;
996 Ok(())
997 }
998
999 /// Sets the class of device
1000 pub fn set_class_of_device(&mut self, class_of_device: &[u8; 3]) {
1001 self.class_of_device.copy_from_slice(class_of_device);
1002
1003 // Update device type based on major class
1004 // Major class is bits 8-12 (bits 0-4 of the second byte)
1005 let major_class = (class_of_device[1] >> 2) & 0x1F;
1006 self.device_type = match major_class {
1007 1 => Self::DEVICE_TYPE_COMPUTER,
1008 2 => Self::DEVICE_TYPE_PHONE,
1009 3 => Self::DEVICE_TYPE_NETWORK,
1010 4 => Self::DEVICE_TYPE_AUDIO,
1011 5 => Self::DEVICE_TYPE_PERIPHERAL,
1012 6 => Self::DEVICE_TYPE_IMAGING,
1013 7 => Self::DEVICE_TYPE_WEARABLE,
1014 8 => Self::DEVICE_TYPE_TOY,
1015 _ => Self::DEVICE_TYPE_UNKNOWN,
1016 };
1017 }
1018
1019 /// Updates connection parameters
1020 pub fn update_connection_params(&mut self, params: &BluetoothConnectionParams) {
1021 self.connection_params = *params;
1022 self.connection_count += 1;
1023 self.add_flag(Self::FLAG_CONNECTED);
1024 }
1025
1026 /// Updates security information
1027 pub fn update_security_info(&mut self, security: &BluetoothSecurityInfo) {
1028 self.security_info = *security;
1029 if security.authenticated != 0 {
1030 self.add_flag(Self::FLAG_PAIRED);
1031 }
1032 }
1033
1034 /// Sets connection flags
1035 pub fn set_flags(&mut self, flags: u8) {
1036 self.flags = flags;
1037 }
1038
1039 /// Adds a connection flag
1040 pub fn add_flag(&mut self, flag: u8) {
1041 self.flags |= flag;
1042 }
1043
1044 /// Removes a connection flag
1045 pub fn remove_flag(&mut self, flag: u8) {
1046 self.flags &= !flag;
1047 }
1048
1049 /// Checks if a specific flag is set
1050 #[must_use]
1051 pub fn has_flag(&self, flag: u8) -> bool {
1052 (self.flags & flag) != 0
1053 }
1054
1055 /// Updates last seen timestamp
1056 pub fn update_last_seen(&mut self, timestamp: u32) {
1057 self.last_seen = timestamp;
1058 }
1059
1060 /// Updates last connected timestamp
1061 pub fn update_last_connected(&mut self, timestamp: u32) {
1062 self.last_connected = timestamp;
1063 }
1064
1065 /// Sets the connection count
1066 pub fn set_connection_count(&mut self, count: u32) {
1067 self.connection_count = count;
1068 }
1069
1070 /// Increments the connection count
1071 pub fn increment_connection_count(&mut self) {
1072 self.connection_count = self.connection_count.saturating_add(1);
1073 }
1074
1075 /// Sets the last connected timestamp
1076 pub fn set_last_connected(&mut self, timestamp: u32) {
1077 self.last_connected = timestamp;
1078 }
1079
1080 /// Sets the last seen timestamp
1081 pub fn set_last_seen(&mut self, timestamp: u32) {
1082 self.last_seen = timestamp;
1083 }
1084
1085 /// Getters
1086 #[must_use]
1087 pub fn mac_address(&self) -> &[u8; 6] {
1088 &self.mac_address
1089 }
1090
1091 #[must_use]
1092 pub fn device_name(&self) -> &[u8] {
1093 &self.device_name[..self.device_name_len as usize]
1094 }
1095
1096 #[must_use]
1097 pub fn class_of_device(&self) -> &[u8; 3] {
1098 &self.class_of_device
1099 }
1100
1101 #[must_use]
1102 pub fn device_type(&self) -> u8 {
1103 self.device_type
1104 }
1105
1106 #[must_use]
1107 pub fn flags(&self) -> u8 {
1108 self.flags
1109 }
1110
1111 #[must_use]
1112 pub fn connection_params(&self) -> &BluetoothConnectionParams {
1113 &self.connection_params
1114 }
1115
1116 #[must_use]
1117 pub fn security_info(&self) -> &BluetoothSecurityInfo {
1118 &self.security_info
1119 }
1120
1121 #[must_use]
1122 pub fn is_paired(&self) -> bool {
1123 self.has_flag(Self::FLAG_PAIRED)
1124 }
1125
1126 #[must_use]
1127 pub fn is_connected(&self) -> bool {
1128 self.has_flag(Self::FLAG_CONNECTED)
1129 }
1130
1131 #[must_use]
1132 pub fn is_trusted(&self) -> bool {
1133 self.has_flag(Self::FLAG_TRUSTED)
1134 }
1135
1136 #[must_use]
1137 pub fn supports_auto_reconnect(&self) -> bool {
1138 self.has_flag(Self::FLAG_AUTO_RECONNECT)
1139 }
1140
1141 #[must_use]
1142 pub fn vendor_id(&self) -> u16 {
1143 self.vendor_id
1144 }
1145 #[must_use]
1146 pub fn product_id(&self) -> u16 {
1147 self.product_id
1148 }
1149 #[must_use]
1150 pub fn version(&self) -> u16 {
1151 self.version
1152 }
1153 #[must_use]
1154 pub fn connection_count(&self) -> u32 {
1155 self.connection_count
1156 }
1157 #[must_use]
1158 pub fn last_seen(&self) -> u32 {
1159 self.last_seen
1160 }
1161 #[must_use]
1162 pub fn last_connected(&self) -> u32 {
1163 self.last_connected
1164 }
1165 #[must_use]
1166 pub fn device_name_len(&self) -> u8 {
1167 self.device_name_len
1168 }
1169 #[must_use]
1170 pub fn pairing_key_len(&self) -> u8 {
1171 self.pairing_key_len
1172 }
1173}
1174
1175/// Bluetooth connection handle wrapper
1176///
1177/// Provides a type-safe wrapper around the raw connection handle value
1178/// with validation to ensure the handle is within the valid range (0x0000-0x0EFF).
1179///
1180/// According to the Bluetooth specification, connection handles are assigned by
1181/// the Bluetooth controller and must be unique for each active connection.
1182/// The valid range is 0x0000-0x0EFF (0-3839 decimal), with 0x0F00-0x0FFF
1183/// reserved for future use.
1184///
1185/// # Use Cases
1186/// - Tracking active Bluetooth connections in embedded systems
1187/// - Providing type safety for connection handle operations
1188/// - Ensuring compliance with Bluetooth specification limits
1189///
1190/// # Performance
1191/// This is a zero-cost abstraction - the wrapper has the same memory
1192/// layout and performance characteristics as a raw `u16`.
1193///
1194/// # Examples
1195/// ```
1196/// use renik::ConnHandle;
1197///
1198/// // Create a valid connection handle
1199/// let handle = ConnHandle::new(0x0001);
1200/// assert_eq!(handle.raw(), 0x0001);
1201///
1202/// // Handles can be converted to/from u16
1203/// let raw_value: u16 = handle.into();
1204/// let back_to_handle = ConnHandle::from(raw_value);
1205/// assert_eq!(handle, back_to_handle);
1206///
1207/// // Maximum valid handle
1208/// let max_handle = ConnHandle::new(0x0EFF);
1209/// assert_eq!(max_handle.raw(), 0x0EFF);
1210/// ```
1211///
1212/// # Panics
1213/// The `new` method panics if the provided value exceeds 0x0EFF:
1214/// ```should_panic
1215/// use renik::ConnHandle;
1216/// let invalid = ConnHandle::new(0x0F00); // Panics!
1217/// ```
1218#[derive(Debug, Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
1219#[repr(transparent)]
1220#[derive(Default)]
1221pub struct ConnHandle(u16);
1222
1223impl ConnHandle {
1224 /// Create a new connection handle instance.
1225 ///
1226 /// # Parameters
1227 /// - `val`: Raw connection handle value (must be <= 0x0EFF)
1228 ///
1229 /// # Panics
1230 /// Panics if the value exceeds 0x0EFF (the maximum valid connection handle).
1231 #[must_use]
1232 pub fn new(val: u16) -> Self {
1233 assert!(val <= 0x0EFF, "Connection handle must be <= 0x0EFF");
1234 Self(val)
1235 }
1236
1237 /// Get the underlying representation.
1238 ///
1239 /// # Returns
1240 /// The raw u16 connection handle value.
1241 #[must_use]
1242 pub fn raw(self) -> u16 {
1243 self.0
1244 }
1245}
1246
1247impl From<u16> for ConnHandle {
1248 fn from(val: u16) -> Self {
1249 Self::new(val)
1250 }
1251}
1252
1253impl From<ConnHandle> for u16 {
1254 fn from(handle: ConnHandle) -> Self {
1255 handle.raw()
1256 }
1257}
1258
1259/// Connection phases for multi-phase Bluetooth connection flow
1260///
1261/// Provides 13 distinct phases for the Bluetooth connection lifecycle:
1262/// - `Idle` (0): No connection attempt
1263/// - `Discovery` (1): Discovering devices
1264/// - `Connecting` (2): Connecting to a device
1265/// - `Connected` (3): Connected, not authenticated
1266/// - `Authenticating` (4): Authentication in progress
1267/// - `SettingUpEncryption` (5): Setting up encryption
1268/// - `FullyConnected` (6): Connected, authenticated, encrypted
1269/// - `ServiceDiscovery` (7): Service discovery in progress
1270/// - `Ready` (8): Connection established with services
1271/// - `Maintaining` (9): Maintenance mode
1272/// - `Reconnecting` (10): Attempting reconnection
1273/// - `Failed` (11): Connection failed
1274/// - `Disconnecting` (12): Disconnecting
1275///
1276/// See `BluetoothConnectionState::is_valid_transition` for allowed transitions.
1277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1278#[repr(u8)]
1279pub enum BluetoothConnectionPhase {
1280 /// Initial state - no connection attempt
1281 Idle = 0,
1282 /// Discovering devices
1283 Discovery = 1,
1284 /// Connecting to a specific device
1285 Connecting = 2,
1286 /// Connected but not authenticated
1287 Connected = 3,
1288 /// Authentication in progress
1289 Authenticating = 4,
1290 /// Authenticated, setting up encryption
1291 SettingUpEncryption = 5,
1292 /// Connected, authenticated, and encrypted
1293 FullyConnected = 6,
1294 /// Service discovery in progress
1295 ServiceDiscovery = 7,
1296 /// Connection established with services
1297 Ready = 8,
1298 /// Connection maintenance mode
1299 Maintaining = 9,
1300 /// Connection lost, attempting reconnection
1301 Reconnecting = 10,
1302 /// Connection failed
1303 Failed = 11,
1304 /// Disconnecting
1305 Disconnecting = 12,
1306}
1307
1308impl Default for BluetoothConnectionPhase {
1309 fn default() -> Self {
1310 Self::Idle
1311 }
1312}
1313
1314impl BluetoothConnectionPhase {
1315 /// Returns true if the phase indicates an active connection
1316 #[must_use]
1317 pub fn is_connected(&self) -> bool {
1318 matches!(
1319 self,
1320 Self::Connected
1321 | Self::Authenticating
1322 | Self::SettingUpEncryption
1323 | Self::FullyConnected
1324 | Self::ServiceDiscovery
1325 | Self::Ready
1326 | Self::Maintaining
1327 )
1328 }
1329
1330 /// Returns true if the phase indicates the connection is secure
1331 #[must_use]
1332 pub fn is_secure(&self) -> bool {
1333 matches!(
1334 self,
1335 Self::FullyConnected | Self::ServiceDiscovery | Self::Ready | Self::Maintaining
1336 )
1337 }
1338
1339 /// Returns true if the phase indicates the connection is ready for use
1340 #[must_use]
1341 pub fn is_ready(&self) -> bool {
1342 matches!(self, Self::Ready | Self::Maintaining)
1343 }
1344}