⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/drivers/bluetooth/
device_node.rs

1//! Bluetooth HCI device node for userland access
2//!
3//! Provides `/dev/bluetooth/hci0` as a device node that allows the BlueZ
4//! userland shim to send HCI command packets and receive HCI event packets
5//! via the kernel Bluetooth HCI driver.
6//!
7//! The device node acts as a bridge between the userland D-Bus daemon
8//! (userland/bluez/bluez-hci-bridge.cpp) and the kernel HCI controller
9//! (kernel/src/drivers/bluetooth/hci.rs).
10//!
11//! Protocol:
12//!   - Write: userland sends H4 HCI command packets (type=0x01 + header +
13//!     params)
14//!   - Read:  userland receives H4 HCI event packets (type=0x04 + header +
15//!     params)
16//!   - Ioctl: adapter info queries and scan mode configuration
17
18#![allow(dead_code)]
19
20use core::result::Result;
21
22use spin::Mutex;
23
24use crate::error::KernelError;
25
26// ---------------------------------------------------------------------------
27// Constants
28// ---------------------------------------------------------------------------
29
30/// Maximum number of pending events in the event queue
31const EVENT_QUEUE_CAPACITY: usize = 32;
32
33/// Maximum size of a single HCI event packet (type + code + len + params)
34const MAX_EVENT_SIZE: usize = 259;
35
36/// Maximum size of a single HCI command packet
37const MAX_COMMAND_SIZE: usize = 259;
38
39/// H4 packet type: HCI Command
40const H4_COMMAND: u8 = 0x01;
41
42/// H4 packet type: HCI Event
43const H4_EVENT: u8 = 0x04;
44
45/// Device node path
46pub const BT_DEVICE_PATH: &str = "/dev/bluetooth/hci0";
47
48// ---------------------------------------------------------------------------
49// Ioctl commands
50// ---------------------------------------------------------------------------
51
52/// Get adapter info (address, name, state)
53pub const BT_IOCTL_GET_ADAPTER_INFO: u32 = 0x4201;
54
55/// Set scan mode (0=off, 1=inquiry, 2=page, 3=both)
56pub const BT_IOCTL_SET_SCAN_MODE: u32 = 0x4202;
57
58/// Reset the HCI controller
59pub const BT_IOCTL_RESET: u32 = 0x4203;
60
61// ---------------------------------------------------------------------------
62// Event queue entry
63// ---------------------------------------------------------------------------
64
65/// A queued HCI event waiting for userland to read
66#[derive(Clone)]
67struct EventEntry {
68    /// Raw event data (H4 type byte + event packet)
69    data: [u8; MAX_EVENT_SIZE],
70    /// Valid data length
71    len: usize,
72}
73
74impl Default for EventEntry {
75    fn default() -> Self {
76        Self {
77            data: [0u8; MAX_EVENT_SIZE],
78            len: 0,
79        }
80    }
81}
82
83// ---------------------------------------------------------------------------
84// Event queue (ring buffer)
85// ---------------------------------------------------------------------------
86
87/// Ring buffer for HCI events pending delivery to userland
88struct EventQueue {
89    entries: [EventEntry; EVENT_QUEUE_CAPACITY],
90    head: usize,
91    tail: usize,
92    count: usize,
93}
94
95impl EventQueue {
96    const fn new() -> Self {
97        // SAFETY: EventEntry is Copy-compatible (all fixed-size arrays of u8 + usize).
98        // We use a const initializer to avoid requiring Default in const context.
99        Self {
100            entries: [const {
101                EventEntry {
102                    data: [0u8; MAX_EVENT_SIZE],
103                    len: 0,
104                }
105            }; EVENT_QUEUE_CAPACITY],
106            head: 0,
107            tail: 0,
108            count: 0,
109        }
110    }
111
112    /// Push an event into the queue. Returns Err if full.
113    fn push(&mut self, data: &[u8]) -> Result<(), KernelError> {
114        if self.count >= EVENT_QUEUE_CAPACITY {
115            return Err(KernelError::ResourceExhausted {
116                resource: "bluetooth event queue",
117            });
118        }
119
120        if data.len() > MAX_EVENT_SIZE {
121            return Err(KernelError::InvalidArgument {
122                name: "event_data",
123                value: "exceeds maximum event size",
124            });
125        }
126
127        let entry = &mut self.entries[self.tail];
128        entry.data[..data.len()].copy_from_slice(data);
129        entry.len = data.len();
130
131        self.tail = (self.tail + 1) % EVENT_QUEUE_CAPACITY;
132        self.count += 1;
133        Ok(())
134    }
135
136    /// Pop the next event from the queue. Returns None if empty.
137    fn pop(&mut self) -> Option<(usize, [u8; MAX_EVENT_SIZE])> {
138        if self.count == 0 {
139            return None;
140        }
141
142        let entry = &self.entries[self.head];
143        let len = entry.len;
144        let data = entry.data;
145
146        self.head = (self.head + 1) % EVENT_QUEUE_CAPACITY;
147        self.count -= 1;
148
149        Some((len, data))
150    }
151
152    /// Check if the queue is empty
153    fn is_empty(&self) -> bool {
154        self.count == 0
155    }
156
157    /// Number of pending events
158    fn len(&self) -> usize {
159        self.count
160    }
161}
162
163// ---------------------------------------------------------------------------
164// Device node handle
165// ---------------------------------------------------------------------------
166
167/// Handle returned by bt_device_open
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub struct BtDeviceHandle {
170    /// Device index (0 for hci0)
171    index: u32,
172    /// Whether the handle is valid/open
173    open: bool,
174}
175
176// ---------------------------------------------------------------------------
177// Device node state
178// ---------------------------------------------------------------------------
179
180/// Global state for the Bluetooth device node
181struct DeviceNodeState {
182    /// Event queue: kernel pushes HCI events, userland reads them
183    event_queue: EventQueue,
184    /// Whether the device node is initialized
185    initialized: bool,
186    /// Number of open handles (reference count)
187    open_count: u32,
188}
189
190impl DeviceNodeState {
191    const fn new() -> Self {
192        Self {
193            event_queue: EventQueue::new(),
194            initialized: false,
195            open_count: 0,
196        }
197    }
198}
199
200/// Global device node state, protected by a spinlock
201static DEVICE_NODE: Mutex<DeviceNodeState> = Mutex::new(DeviceNodeState::new());
202
203// ---------------------------------------------------------------------------
204// Initialization
205// ---------------------------------------------------------------------------
206
207/// Initialize the Bluetooth device node subsystem.
208/// Creates the `/dev/bluetooth/hci0` device node entry.
209/// Must be called after the Bluetooth controller is initialized.
210pub fn init() -> Result<(), KernelError> {
211    let mut state = DEVICE_NODE.lock();
212
213    if state.initialized {
214        return Err(KernelError::InvalidArgument {
215            name: "bt_device_node",
216            value: "already initialized",
217        });
218    }
219
220    state.initialized = true;
221
222    crate::println!("[BT] Device node {} registered", BT_DEVICE_PATH);
223    Ok(())
224}
225
226// ---------------------------------------------------------------------------
227// Device operations
228// ---------------------------------------------------------------------------
229
230/// Open the Bluetooth HCI device node.
231/// Returns a handle for subsequent read/write/ioctl operations.
232pub fn bt_device_open() -> Result<BtDeviceHandle, KernelError> {
233    let mut state = DEVICE_NODE.lock();
234
235    if !state.initialized {
236        return Err(KernelError::InvalidArgument {
237            name: "bt_device_node",
238            value: "not initialized",
239        });
240    }
241
242    state.open_count += 1;
243
244    Ok(BtDeviceHandle {
245        index: 0,
246        open: true,
247    })
248}
249
250/// Close the Bluetooth HCI device node handle.
251pub fn bt_device_close(handle: &mut BtDeviceHandle) -> Result<(), KernelError> {
252    if !handle.open {
253        return Err(KernelError::InvalidArgument {
254            name: "handle",
255            value: "not open",
256        });
257    }
258
259    let mut state = DEVICE_NODE.lock();
260    if state.open_count > 0 {
261        state.open_count -= 1;
262    }
263
264    handle.open = false;
265    Ok(())
266}
267
268/// Write an HCI command packet to the kernel HCI layer.
269///
270/// The data should be an H4-formatted command packet:
271/// `[0x01] [opcode_lo] [opcode_hi] [param_len] [params...]`
272///
273/// The command is forwarded to the kernel's BluetoothController for
274/// processing and transmission to the hardware.
275pub fn bt_device_write(handle: &BtDeviceHandle, data: &[u8]) -> Result<usize, KernelError> {
276    if !handle.open {
277        return Err(KernelError::InvalidArgument {
278            name: "handle",
279            value: "not open",
280        });
281    }
282
283    if data.is_empty() {
284        return Err(KernelError::InvalidArgument {
285            name: "data",
286            value: "empty write buffer",
287        });
288    }
289
290    if data.len() > MAX_COMMAND_SIZE {
291        return Err(KernelError::InvalidArgument {
292            name: "data",
293            value: "exceeds maximum command size",
294        });
295    }
296
297    // Verify H4 command packet type
298    if data[0] != H4_COMMAND {
299        return Err(KernelError::InvalidArgument {
300            name: "packet_type",
301            value: "expected HCI command (0x01)",
302        });
303    }
304
305    // Verify minimum command header length (type + opcode + param_len)
306    if data.len() < 4 {
307        return Err(KernelError::InvalidArgument {
308            name: "command",
309            value: "too short for HCI command header",
310        });
311    }
312
313    let opcode = u16::from_le_bytes([data[1], data[2]]);
314    let param_len = data[3] as usize;
315
316    // Verify packet completeness
317    if data.len() < 4 + param_len {
318        return Err(KernelError::InvalidArgument {
319            name: "command",
320            value: "truncated command parameters",
321        });
322    }
323
324    // Forward to the kernel HCI controller.
325    // Route the command based on its opcode to the appropriate
326    // BluetoothController method.
327    let controller = super::get_controller();
328    let mut ctrl = controller.lock();
329
330    match opcode {
331        super::hci::HCI_RESET => {
332            let _result = ctrl.send_reset();
333        }
334        super::hci::HCI_READ_BD_ADDR => {
335            let _result = ctrl.read_bd_addr();
336        }
337        super::hci::HCI_READ_LOCAL_NAME => {
338            let _result = ctrl.read_local_name();
339        }
340        super::hci::HCI_WRITE_SCAN_ENABLE => {
341            if param_len >= 1 {
342                let mode = match data[4] {
343                    0x00 => super::hci::ScanEnable::NoScans,
344                    0x01 => super::hci::ScanEnable::InquiryScanOnly,
345                    0x02 => super::hci::ScanEnable::PageScanOnly,
346                    _ => super::hci::ScanEnable::InquiryAndPageScan,
347                };
348                let _result = ctrl.write_scan_enable(mode);
349            }
350        }
351        super::hci::HCI_INQUIRY => {
352            if param_len >= 5 {
353                let inquiry_length = data[7]; // params[3]
354                let max_responses = data[8]; // params[4]
355                let _result = ctrl.start_inquiry(inquiry_length, max_responses);
356            }
357        }
358        super::hci::HCI_CREATE_CONNECTION => {
359            if param_len >= 6 {
360                let mut addr = [0u8; 6];
361                addr.copy_from_slice(&data[4..10]);
362                let _result = ctrl.create_connection(&addr);
363            }
364        }
365        super::hci::HCI_DISCONNECT => {
366            if param_len >= 3 {
367                let handle = u16::from_le_bytes([data[4], data[5]]);
368                let reason = data[6];
369                let _result = ctrl.disconnect(handle, reason);
370            }
371        }
372        _ => {
373            // Unknown opcode -- log and ignore
374        }
375    }
376
377    Ok(data.len())
378}
379
380/// Read the next HCI event from the kernel event queue.
381///
382/// Returns the event in H4 format:
383/// `[0x04] [event_code] [param_len] [params...]`
384///
385/// If no event is available, returns Ok(0).
386pub fn bt_device_read(handle: &BtDeviceHandle, buf: &mut [u8]) -> Result<usize, KernelError> {
387    if !handle.open {
388        return Err(KernelError::InvalidArgument {
389            name: "handle",
390            value: "not open",
391        });
392    }
393
394    if buf.is_empty() {
395        return Err(KernelError::InvalidArgument {
396            name: "buf",
397            value: "empty read buffer",
398        });
399    }
400
401    let mut state = DEVICE_NODE.lock();
402
403    match state.event_queue.pop() {
404        Some((len, data)) => {
405            let copy_len = core::cmp::min(len, buf.len());
406            buf[..copy_len].copy_from_slice(&data[..copy_len]);
407            Ok(copy_len)
408        }
409        None => Ok(0), // No event available
410    }
411}
412
413/// Perform an ioctl on the Bluetooth device node.
414///
415/// Supported commands:
416/// - `BT_IOCTL_GET_ADAPTER_INFO`: Read adapter address, name, and state
417/// - `BT_IOCTL_SET_SCAN_MODE`: Set inquiry/page scan mode
418/// - `BT_IOCTL_RESET`: Reset the HCI controller
419pub fn bt_device_ioctl(handle: &BtDeviceHandle, cmd: u32, arg: u64) -> Result<u64, KernelError> {
420    if !handle.open {
421        return Err(KernelError::InvalidArgument {
422            name: "handle",
423            value: "not open",
424        });
425    }
426
427    match cmd {
428        BT_IOCTL_GET_ADAPTER_INFO => {
429            // Return adapter state as a packed u64:
430            // bits [7:0]  = state (0=off, 1=init, 2=ready, 3=scanning, 4=connected)
431            // bits [15:8] = open handle count
432            let controller = super::get_controller();
433            let ctrl = controller.lock();
434
435            let state_val: u8 = match ctrl.state() {
436                super::ControllerState::Off => 0,
437                super::ControllerState::Initializing => 1,
438                super::ControllerState::Ready => 2,
439                super::ControllerState::Scanning => 3,
440                super::ControllerState::Connected => 4,
441            };
442
443            let dev_state = DEVICE_NODE.lock();
444            let result = (state_val as u64) | ((dev_state.open_count as u64) << 8);
445            Ok(result)
446        }
447
448        BT_IOCTL_SET_SCAN_MODE => {
449            let scan_mode = (arg & 0xFF) as u8;
450            let controller = super::get_controller();
451            let mut ctrl = controller.lock();
452
453            let mode = match scan_mode {
454                0x00 => super::hci::ScanEnable::NoScans,
455                0x01 => super::hci::ScanEnable::InquiryScanOnly,
456                0x02 => super::hci::ScanEnable::PageScanOnly,
457                _ => super::hci::ScanEnable::InquiryAndPageScan,
458            };
459            let _result = ctrl.write_scan_enable(mode);
460            Ok(0)
461        }
462
463        BT_IOCTL_RESET => {
464            let controller = super::get_controller();
465            let mut ctrl = controller.lock();
466
467            let _result = ctrl.send_reset();
468            Ok(0)
469        }
470
471        _ => Err(KernelError::InvalidArgument {
472            name: "ioctl_cmd",
473            value: "unknown ioctl command",
474        }),
475    }
476}
477
478// ---------------------------------------------------------------------------
479// Kernel-side event delivery
480// ---------------------------------------------------------------------------
481
482/// Push an HCI event into the device node's event queue.
483///
484/// Called by the kernel HCI driver when an event is received from the
485/// Bluetooth controller. The event is queued for the next userland read().
486///
487/// The data should include the H4 event type byte (0x04) prefix.
488pub fn push_event(data: &[u8]) -> Result<(), KernelError> {
489    let mut state = DEVICE_NODE.lock();
490
491    if !state.initialized {
492        return Err(KernelError::InvalidArgument {
493            name: "bt_device_node",
494            value: "not initialized",
495        });
496    }
497
498    state.event_queue.push(data)
499}
500
501/// Get the number of pending events in the queue.
502pub fn pending_event_count() -> usize {
503    let state = DEVICE_NODE.lock();
504    state.event_queue.len()
505}
506
507// ---------------------------------------------------------------------------
508// Tests
509// ---------------------------------------------------------------------------
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514
515    #[test]
516    fn test_event_queue_push_pop() {
517        let mut queue = EventQueue::new();
518
519        assert!(queue.is_empty());
520        assert_eq!(queue.len(), 0);
521
522        // Push an event
523        let event_data = [H4_EVENT, 0x0E, 0x04, 0x01, 0x00, 0x00, 0x00];
524        assert!(queue.push(&event_data).is_ok());
525        assert!(!queue.is_empty());
526        assert_eq!(queue.len(), 1);
527
528        // Pop it back
529        let result = queue.pop();
530        assert!(result.is_some());
531        let (len, data) = result.unwrap();
532        assert_eq!(len, event_data.len());
533        assert_eq!(&data[..len], &event_data);
534        assert!(queue.is_empty());
535    }
536
537    #[test]
538    fn test_event_queue_overflow() {
539        let mut queue = EventQueue::new();
540
541        let event_data = [H4_EVENT, 0x01, 0x01, 0x00];
542
543        // Fill the queue
544        for _ in 0..EVENT_QUEUE_CAPACITY {
545            assert!(queue.push(&event_data).is_ok());
546        }
547
548        // Next push should fail
549        assert!(queue.push(&event_data).is_err());
550        assert_eq!(queue.len(), EVENT_QUEUE_CAPACITY);
551    }
552
553    #[test]
554    fn test_event_queue_wrap_around() {
555        let mut queue = EventQueue::new();
556
557        let event_data = [H4_EVENT, 0x02, 0x02, 0xAA, 0xBB];
558
559        // Push and pop several times to exercise wrap-around
560        for i in 0..EVENT_QUEUE_CAPACITY * 2 {
561            assert!(queue.push(&event_data).is_ok());
562            let result = queue.pop();
563            assert!(result.is_some());
564            let (len, _data) = result.unwrap();
565            assert_eq!(len, event_data.len());
566            let _ = i;
567        }
568
569        assert!(queue.is_empty());
570    }
571
572    #[test]
573    fn test_event_queue_empty_pop() {
574        let mut queue = EventQueue::new();
575        assert!(queue.pop().is_none());
576    }
577
578    #[test]
579    fn test_event_queue_oversized_event() {
580        let mut queue = EventQueue::new();
581        let big_event = [0u8; MAX_EVENT_SIZE + 1];
582        assert!(queue.push(&big_event).is_err());
583    }
584}