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

veridian_kernel/drivers/bluetooth/
profiles.rs

1//! Bluetooth Profiles
2//!
3//! Implements Bluetooth profile support including SDP (Service Discovery
4//! Protocol) database, A2DP (Advanced Audio Distribution Profile) with SBC
5//! codec configuration, HID (Human Interface Device) report handling, and
6//! SPP (Serial Port Profile) over RFCOMM.
7//!
8//! Reference: Bluetooth Core Specification v5.4, Bluetooth Profile
9//! Specifications
10
11#![allow(dead_code)]
12
13#[cfg(feature = "alloc")]
14use alloc::collections::BTreeMap;
15#[cfg(feature = "alloc")]
16use alloc::string::String;
17#[cfg(feature = "alloc")]
18use alloc::vec::Vec;
19
20use crate::error::KernelError;
21
22// ---------------------------------------------------------------------------
23// SDP Attribute Types
24// ---------------------------------------------------------------------------
25
26/// SDP Data Element types
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum SdpAttribute {
29    /// Nil / null value
30    Nil,
31    /// Unsigned 8-bit integer
32    Uint8(u8),
33    /// Unsigned 16-bit integer
34    Uint16(u16),
35    /// Unsigned 32-bit integer
36    Uint32(u32),
37    /// Signed 8-bit integer
38    Int8(i8),
39    /// Signed 16-bit integer
40    Int16(i16),
41    /// Signed 32-bit integer
42    Int32(i32),
43    /// 16-bit UUID
44    Uuid16(u16),
45    /// 128-bit UUID (stored as [u8; 16])
46    Uuid128([u8; 16]),
47    /// Text string
48    #[cfg(feature = "alloc")]
49    Text(String),
50    /// Boolean value
51    Bool(bool),
52    /// Sequence of attributes
53    #[cfg(feature = "alloc")]
54    Sequence(Vec<SdpAttribute>),
55}
56
57impl SdpAttribute {
58    /// Check if this attribute is nil
59    pub(crate) fn is_nil(&self) -> bool {
60        matches!(self, Self::Nil)
61    }
62
63    /// Try to extract a u16 value
64    pub(crate) fn as_u16(&self) -> Option<u16> {
65        match self {
66            Self::Uint16(v) => Some(*v),
67            Self::Uint8(v) => Some(*v as u16),
68            _ => None,
69        }
70    }
71
72    /// Try to extract a UUID16 value
73    pub(crate) fn as_uuid16(&self) -> Option<u16> {
74        match self {
75            Self::Uuid16(v) => Some(*v),
76            _ => None,
77        }
78    }
79}
80
81// ---------------------------------------------------------------------------
82// Well-known SDP Attribute IDs
83// ---------------------------------------------------------------------------
84
85/// ServiceRecordHandle attribute ID
86pub const ATTR_SERVICE_RECORD_HANDLE: u16 = 0x0000;
87
88/// ServiceClassIDList attribute ID
89pub const ATTR_SERVICE_CLASS_ID_LIST: u16 = 0x0001;
90
91/// ProtocolDescriptorList attribute ID
92pub const ATTR_PROTOCOL_DESCRIPTOR_LIST: u16 = 0x0004;
93
94/// BrowseGroupList attribute ID
95pub const ATTR_BROWSE_GROUP_LIST: u16 = 0x0005;
96
97/// BluetoothProfileDescriptorList attribute ID
98pub const ATTR_PROFILE_DESCRIPTOR_LIST: u16 = 0x0009;
99
100/// ServiceName attribute ID
101pub const ATTR_SERVICE_NAME: u16 = 0x0100;
102
103/// ServiceDescription attribute ID
104pub const ATTR_SERVICE_DESCRIPTION: u16 = 0x0101;
105
106// ---------------------------------------------------------------------------
107// Well-known UUIDs (duplicated from HCI for self-containedness)
108// ---------------------------------------------------------------------------
109
110/// L2CAP protocol UUID
111pub const UUID_L2CAP: u16 = 0x0100;
112
113/// RFCOMM protocol UUID
114pub const UUID_RFCOMM: u16 = 0x0003;
115
116/// SDP UUID
117pub const UUID_SDP: u16 = 0x0001;
118
119/// OBEX UUID
120pub const UUID_OBEX: u16 = 0x0008;
121
122/// Serial Port Profile UUID
123pub const UUID_SERIAL_PORT: u16 = 0x1101;
124
125/// OBEX Object Push UUID
126pub const UUID_OBEX_PUSH: u16 = 0x1105;
127
128/// A2DP Source UUID
129pub const UUID_A2DP_SOURCE: u16 = 0x110A;
130
131/// A2DP Sink UUID
132pub const UUID_A2DP_SINK: u16 = 0x110B;
133
134/// AVRCP Target UUID
135pub const UUID_AVRCP_TARGET: u16 = 0x110C;
136
137/// AVRCP Controller UUID
138pub const UUID_AVRCP_CONTROLLER: u16 = 0x110E;
139
140/// HFP AG UUID
141pub const UUID_HFP_AG: u16 = 0x111F;
142
143/// HFP HF UUID
144pub const UUID_HFP_HF: u16 = 0x111E;
145
146/// HID UUID
147pub const UUID_HID: u16 = 0x1124;
148
149/// PnP Information UUID
150pub const UUID_PNP_INFO: u16 = 0x1200;
151
152/// Public Browse Group UUID
153pub const UUID_PUBLIC_BROWSE_GROUP: u16 = 0x1002;
154
155// ---------------------------------------------------------------------------
156// SDP Service Record
157// ---------------------------------------------------------------------------
158
159/// An SDP service record containing service attributes
160#[cfg(feature = "alloc")]
161#[derive(Debug, Clone)]
162pub struct SdpRecord {
163    /// Service record handle (unique within the database)
164    pub handle: u32,
165    /// Primary service class UUID (16-bit short form)
166    pub service_class_uuid: u16,
167    /// Protocol descriptor list (list of protocol UUIDs + parameters)
168    pub protocol_list: Vec<u16>,
169    /// Profile descriptor list (profile UUID + version pairs)
170    pub profile_list: Vec<(u16, u16)>,
171    /// Human-readable service name
172    pub service_name: String,
173    /// Additional attributes indexed by attribute ID
174    pub attributes: BTreeMap<u16, SdpAttribute>,
175}
176
177#[cfg(feature = "alloc")]
178impl SdpRecord {
179    /// Create a new SDP record with the given handle and service class
180    pub fn new(handle: u32, service_class_uuid: u16, name: &str) -> Self {
181        Self {
182            handle,
183            service_class_uuid,
184            protocol_list: Vec::new(),
185            profile_list: Vec::new(),
186            service_name: String::from(name),
187            attributes: BTreeMap::new(),
188        }
189    }
190
191    /// Add a protocol to the protocol descriptor list
192    pub(crate) fn add_protocol(&mut self, uuid: u16) {
193        self.protocol_list.push(uuid);
194    }
195
196    /// Add a profile descriptor (UUID + version)
197    pub(crate) fn add_profile(&mut self, uuid: u16, version: u16) {
198        self.profile_list.push((uuid, version));
199    }
200
201    /// Set an attribute on this record
202    pub(crate) fn set_attribute(&mut self, id: u16, value: SdpAttribute) {
203        self.attributes.insert(id, value);
204    }
205
206    /// Get an attribute by ID
207    pub(crate) fn get_attribute(&self, id: u16) -> Option<&SdpAttribute> {
208        self.attributes.get(&id)
209    }
210
211    /// Check if this record matches a given service class UUID
212    pub(crate) fn matches_uuid(&self, uuid: u16) -> bool {
213        self.service_class_uuid == uuid
214    }
215}
216
217// ---------------------------------------------------------------------------
218// SDP Database
219// ---------------------------------------------------------------------------
220
221/// SDP service database
222#[cfg(feature = "alloc")]
223pub struct SdpDatabase {
224    /// Registered service records
225    records: Vec<SdpRecord>,
226    /// Next available service record handle
227    next_handle: u32,
228}
229
230#[cfg(feature = "alloc")]
231impl Default for SdpDatabase {
232    fn default() -> Self {
233        Self::new()
234    }
235}
236
237#[cfg(feature = "alloc")]
238impl SdpDatabase {
239    /// Create a new empty SDP database
240    pub fn new() -> Self {
241        Self {
242            records: Vec::new(),
243            next_handle: 0x00010001, // First user handle
244        }
245    }
246
247    /// Get number of registered services
248    pub(crate) fn record_count(&self) -> usize {
249        self.records.len()
250    }
251
252    /// Register a new service record
253    ///
254    /// Returns the assigned service record handle
255    pub(crate) fn register_service(&mut self, service_class_uuid: u16, name: &str) -> u32 {
256        let handle = self.next_handle;
257        self.next_handle += 1;
258        let record = SdpRecord::new(handle, service_class_uuid, name);
259        self.records.push(record);
260        handle
261    }
262
263    /// Register a fully constructed SDP record
264    pub(crate) fn register_record(&mut self, mut record: SdpRecord) -> u32 {
265        let handle = self.next_handle;
266        self.next_handle += 1;
267        record.handle = handle;
268        self.records.push(record);
269        handle
270    }
271
272    /// Remove a service record by handle
273    pub(crate) fn remove_service(&mut self, handle: u32) -> bool {
274        let initial_len = self.records.len();
275        self.records.retain(|r| r.handle != handle);
276        self.records.len() < initial_len
277    }
278
279    /// Find a service record by service class UUID
280    pub(crate) fn find_by_uuid(&self, uuid: u16) -> Option<&SdpRecord> {
281        self.records.iter().find(|r| r.matches_uuid(uuid))
282    }
283
284    /// Search for all records matching a service class UUID
285    pub(crate) fn search(&self, uuid: u16) -> Vec<&SdpRecord> {
286        self.records
287            .iter()
288            .filter(|r| r.matches_uuid(uuid))
289            .collect()
290    }
291
292    /// Get a record by handle
293    pub(crate) fn get_by_handle(&self, handle: u32) -> Option<&SdpRecord> {
294        self.records.iter().find(|r| r.handle == handle)
295    }
296
297    /// Get a mutable record by handle
298    pub(crate) fn get_by_handle_mut(&mut self, handle: u32) -> Option<&mut SdpRecord> {
299        self.records.iter_mut().find(|r| r.handle == handle)
300    }
301}
302
303// ---------------------------------------------------------------------------
304// A2DP (Advanced Audio Distribution Profile)
305// ---------------------------------------------------------------------------
306
307/// A2DP codec types
308#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309#[repr(u8)]
310pub enum A2dpCodec {
311    /// SBC (Sub-Band Coding) - mandatory codec
312    Sbc = 0x00,
313    /// MPEG-1,2 Audio
314    Mpeg12 = 0x01,
315    /// AAC (MPEG-2,4 AAC)
316    Aac = 0x02,
317    /// ATRAC
318    Atrac = 0x04,
319    /// Vendor-specific (e.g., aptX)
320    VendorSpecific = 0xFF,
321}
322
323impl A2dpCodec {
324    /// Parse codec type from byte
325    pub(crate) fn from_u8(val: u8) -> Option<Self> {
326        match val {
327            0x00 => Some(Self::Sbc),
328            0x01 => Some(Self::Mpeg12),
329            0x02 => Some(Self::Aac),
330            0x04 => Some(Self::Atrac),
331            0xFF => Some(Self::VendorSpecific),
332            _ => None,
333        }
334    }
335}
336
337/// SBC channel modes
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
339#[repr(u8)]
340pub enum SbcChannelMode {
341    /// Mono
342    Mono = 0x08,
343    /// Dual Channel
344    DualChannel = 0x04,
345    /// Stereo
346    Stereo = 0x02,
347    /// Joint Stereo
348    JointStereo = 0x01,
349}
350
351/// SBC allocation method
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
353#[repr(u8)]
354pub enum SbcAllocationMethod {
355    /// SNR (Signal-to-Noise Ratio)
356    Snr = 0x02,
357    /// Loudness
358    Loudness = 0x01,
359}
360
361/// SBC sample frequencies
362#[derive(Debug, Clone, Copy, PartialEq, Eq)]
363#[repr(u8)]
364pub enum SbcSampleFrequency {
365    /// 16 kHz
366    Freq16000 = 0x80,
367    /// 32 kHz
368    Freq32000 = 0x40,
369    /// 44.1 kHz
370    Freq44100 = 0x20,
371    /// 48 kHz
372    Freq48000 = 0x10,
373}
374
375/// SBC codec configuration
376#[derive(Debug, Clone, Copy, PartialEq, Eq)]
377pub struct SbcConfig {
378    /// Number of subbands (4 or 8)
379    pub subbands: u8,
380    /// Block length (4, 8, 12, or 16)
381    pub block_length: u8,
382    /// Allocation method
383    pub allocation_method: SbcAllocationMethod,
384    /// Channel mode
385    pub channel_mode: SbcChannelMode,
386    /// Sample frequency
387    pub sample_frequency: SbcSampleFrequency,
388    /// Minimum bitpool value (2-250)
389    pub bitpool_min: u8,
390    /// Maximum bitpool value (2-250)
391    pub bitpool_max: u8,
392}
393
394impl Default for SbcConfig {
395    fn default() -> Self {
396        Self {
397            subbands: 8,
398            block_length: 16,
399            allocation_method: SbcAllocationMethod::Loudness,
400            channel_mode: SbcChannelMode::JointStereo,
401            sample_frequency: SbcSampleFrequency::Freq44100,
402            bitpool_min: 2,
403            bitpool_max: 53,
404        }
405    }
406}
407
408impl SbcConfig {
409    /// Validate the SBC configuration
410    pub(crate) fn validate(&self) -> Result<(), KernelError> {
411        if self.subbands != 4 && self.subbands != 8 {
412            return Err(KernelError::InvalidArgument {
413                name: "subbands",
414                value: "must be 4 or 8",
415            });
416        }
417        if !matches!(self.block_length, 4 | 8 | 12 | 16) {
418            return Err(KernelError::InvalidArgument {
419                name: "block_length",
420                value: "must be 4, 8, 12, or 16",
421            });
422        }
423        if self.bitpool_min < 2 || self.bitpool_max > 250 {
424            return Err(KernelError::InvalidArgument {
425                name: "bitpool",
426                value: "must be in range 2-250",
427            });
428        }
429        if self.bitpool_min > self.bitpool_max {
430            return Err(KernelError::InvalidArgument {
431                name: "bitpool_min",
432                value: "must not exceed bitpool_max",
433            });
434        }
435        Ok(())
436    }
437
438    /// Encode SBC configuration into the 4-byte AVDTP codec information element
439    #[allow(clippy::wrong_self_convention)]
440    pub(crate) fn to_bytes(&self) -> [u8; 4] {
441        let mut bytes = [0u8; 4];
442        // Byte 0: sample frequency (4 bits) | channel mode (4 bits)
443        bytes[0] = (self.sample_frequency as u8) | (self.channel_mode as u8);
444        // Byte 1: block length (4 bits) | subbands (2 bits) | allocation (2 bits)
445        let bl = match self.block_length {
446            4 => 0x80u8,
447            8 => 0x40,
448            12 => 0x20,
449            16 => 0x10,
450            _ => 0x10,
451        };
452        let sb = if self.subbands == 4 { 0x08u8 } else { 0x04 };
453        bytes[1] = bl | sb | (self.allocation_method as u8);
454        // Byte 2: minimum bitpool
455        bytes[2] = self.bitpool_min;
456        // Byte 3: maximum bitpool
457        bytes[3] = self.bitpool_max;
458        bytes
459    }
460}
461
462/// A2DP stream endpoint
463#[derive(Debug, Clone, Copy)]
464pub struct A2dpEndpoint {
465    /// Codec type
466    pub codec: A2dpCodec,
467    /// Sample rate in Hz
468    pub sample_rate: u32,
469    /// Number of channels (1 = mono, 2 = stereo)
470    pub channels: u8,
471    /// Minimum bitpool value
472    pub bitpool_min: u8,
473    /// Maximum bitpool value
474    pub bitpool_max: u8,
475}
476
477impl Default for A2dpEndpoint {
478    fn default() -> Self {
479        Self {
480            codec: A2dpCodec::Sbc,
481            sample_rate: 44100,
482            channels: 2,
483            bitpool_min: 2,
484            bitpool_max: 53,
485        }
486    }
487}
488
489/// A2DP stream state
490#[derive(Debug, Clone, Copy, PartialEq, Eq)]
491pub enum A2dpState {
492    /// Endpoint is idle (not configured)
493    Idle,
494    /// Endpoint is configured (codec negotiated)
495    Configured,
496    /// Stream is actively sending/receiving audio
497    Streaming,
498    /// Stream is temporarily suspended
499    Suspended,
500}
501
502/// A2DP Sink: receives audio data from a remote source
503pub struct A2dpSink {
504    /// Endpoint configuration
505    pub endpoint: A2dpEndpoint,
506    /// SBC codec configuration (when SBC is selected)
507    pub sbc_config: SbcConfig,
508    /// Current stream state
509    pub state: A2dpState,
510    /// AVDTP stream handle (SEID)
511    pub seid: u8,
512    /// L2CAP channel ID for media transport
513    pub transport_cid: u16,
514}
515
516impl Default for A2dpSink {
517    fn default() -> Self {
518        Self::new()
519    }
520}
521
522impl A2dpSink {
523    /// Create a new A2DP sink
524    pub fn new() -> Self {
525        Self {
526            endpoint: A2dpEndpoint::default(),
527            sbc_config: SbcConfig::default(),
528            state: A2dpState::Idle,
529            seid: 1,
530            transport_cid: 0,
531        }
532    }
533
534    /// Configure the sink with a specific codec and parameters
535    pub(crate) fn configure(
536        &mut self,
537        codec: A2dpCodec,
538        sample_rate: u32,
539        channels: u8,
540    ) -> Result<(), KernelError> {
541        if self.state != A2dpState::Idle && self.state != A2dpState::Configured {
542            return Err(KernelError::InvalidState {
543                expected: "Idle or Configured",
544                actual: "Streaming or Suspended",
545            });
546        }
547        if channels == 0 || channels > 2 {
548            return Err(KernelError::InvalidArgument {
549                name: "channels",
550                value: "must be 1 or 2",
551            });
552        }
553
554        self.endpoint.codec = codec;
555        self.endpoint.sample_rate = sample_rate;
556        self.endpoint.channels = channels;
557
558        // Update SBC config channel mode based on channel count
559        if codec == A2dpCodec::Sbc {
560            self.sbc_config.channel_mode = if channels == 1 {
561                SbcChannelMode::Mono
562            } else {
563                SbcChannelMode::JointStereo
564            };
565            // Map sample rate to SBC frequency
566            self.sbc_config.sample_frequency = match sample_rate {
567                16000 => SbcSampleFrequency::Freq16000,
568                32000 => SbcSampleFrequency::Freq32000,
569                44100 => SbcSampleFrequency::Freq44100,
570                48000 => SbcSampleFrequency::Freq48000,
571                _ => SbcSampleFrequency::Freq44100,
572            };
573        }
574
575        self.state = A2dpState::Configured;
576        Ok(())
577    }
578
579    /// Start streaming audio
580    pub(crate) fn start_stream(&mut self) -> Result<(), KernelError> {
581        if self.state != A2dpState::Configured && self.state != A2dpState::Suspended {
582            return Err(KernelError::InvalidState {
583                expected: "Configured or Suspended",
584                actual: "Idle or Streaming",
585            });
586        }
587        self.state = A2dpState::Streaming;
588        Ok(())
589    }
590
591    /// Suspend the audio stream
592    pub(crate) fn suspend(&mut self) -> Result<(), KernelError> {
593        if self.state != A2dpState::Streaming {
594            return Err(KernelError::InvalidState {
595                expected: "Streaming",
596                actual: "not Streaming",
597            });
598        }
599        self.state = A2dpState::Suspended;
600        Ok(())
601    }
602
603    /// Close the stream and return to idle
604    pub(crate) fn close(&mut self) {
605        self.state = A2dpState::Idle;
606        self.transport_cid = 0;
607    }
608}
609
610// ---------------------------------------------------------------------------
611// HID (Human Interface Device Profile)
612// ---------------------------------------------------------------------------
613
614/// HID report types
615#[derive(Debug, Clone, Copy, PartialEq, Eq)]
616#[repr(u8)]
617pub enum HidReportType {
618    /// Input report (device -> host, e.g., key presses, mouse movements)
619    Input = 0x01,
620    /// Output report (host -> device, e.g., keyboard LEDs)
621    Output = 0x02,
622    /// Feature report (bidirectional, device configuration)
623    Feature = 0x03,
624}
625
626impl HidReportType {
627    /// Parse report type from byte
628    pub(crate) fn from_u8(val: u8) -> Option<Self> {
629        match val {
630            0x01 => Some(Self::Input),
631            0x02 => Some(Self::Output),
632            0x03 => Some(Self::Feature),
633            _ => None,
634        }
635    }
636}
637
638/// Maximum HID report data size
639pub const HID_MAX_REPORT_SIZE: usize = 64;
640
641/// A HID report
642#[derive(Debug, Clone)]
643pub struct HidReport {
644    /// Report type
645    pub report_type: HidReportType,
646    /// Report ID (0 if not used)
647    pub report_id: u8,
648    /// Report data
649    pub data: [u8; HID_MAX_REPORT_SIZE],
650    /// Valid data length
651    pub data_len: usize,
652}
653
654impl HidReport {
655    /// Create a new empty HID report
656    pub fn new(report_type: HidReportType, report_id: u8) -> Self {
657        Self {
658            report_type,
659            report_id,
660            data: [0u8; HID_MAX_REPORT_SIZE],
661            data_len: 0,
662        }
663    }
664
665    /// Create a report with data
666    pub(crate) fn with_data(report_type: HidReportType, report_id: u8, data: &[u8]) -> Self {
667        let copy_len = data.len().min(HID_MAX_REPORT_SIZE);
668        let mut report = Self::new(report_type, report_id);
669        report.data[..copy_len].copy_from_slice(&data[..copy_len]);
670        report.data_len = copy_len;
671        report
672    }
673
674    /// Get the report data as a slice
675    pub(crate) fn as_bytes(&self) -> &[u8] {
676        &self.data[..self.data_len]
677    }
678}
679
680/// HID Report Descriptor item (simplified)
681#[derive(Debug, Clone, Copy, PartialEq, Eq)]
682pub struct HidDescriptor {
683    /// Usage Page (e.g., 0x01 = Generic Desktop, 0x07 = Keyboard)
684    pub usage_page: u16,
685    /// Usage (e.g., 0x06 = Keyboard, 0x02 = Mouse)
686    pub usage: u16,
687    /// Size of each report field in bits
688    pub report_size: u8,
689    /// Number of fields in the report
690    pub report_count: u8,
691}
692
693impl HidDescriptor {
694    /// Create a keyboard HID descriptor
695    pub(crate) fn keyboard() -> Self {
696        Self {
697            usage_page: 0x01, // Generic Desktop
698            usage: 0x06,      // Keyboard
699            report_size: 8,
700            report_count: 6, // 6-key rollover
701        }
702    }
703
704    /// Create a mouse HID descriptor
705    pub(crate) fn mouse() -> Self {
706        Self {
707            usage_page: 0x01, // Generic Desktop
708            usage: 0x02,      // Mouse
709            report_size: 8,
710            report_count: 3, // buttons + X + Y
711        }
712    }
713
714    /// Create a gamepad HID descriptor
715    pub(crate) fn gamepad() -> Self {
716        Self {
717            usage_page: 0x01, // Generic Desktop
718            usage: 0x05,      // Game Pad
719            report_size: 8,
720            report_count: 8, // buttons + axes
721        }
722    }
723
724    /// Total report size in bytes
725    pub(crate) fn report_byte_size(&self) -> usize {
726        let total_bits = (self.report_size as usize) * (self.report_count as usize);
727        total_bits.div_ceil(8)
728    }
729}
730
731/// HID device state
732#[derive(Debug, Clone, Copy, PartialEq, Eq)]
733pub enum HidState {
734    /// Device not connected
735    Disconnected,
736    /// Device connected, not yet configured
737    Connected,
738    /// Device ready for reports
739    Ready,
740    /// Device is suspended
741    Suspended,
742}
743
744/// A Bluetooth HID device
745pub struct HidDevice {
746    /// Device descriptor
747    pub descriptor: HidDescriptor,
748    /// Current device state
749    pub state: HidState,
750    /// L2CAP control channel CID
751    pub control_cid: u16,
752    /// L2CAP interrupt channel CID
753    pub interrupt_cid: u16,
754    /// Last received input report
755    pub last_input_report: HidReport,
756}
757
758impl Default for HidDevice {
759    fn default() -> Self {
760        Self::new(HidDescriptor::keyboard())
761    }
762}
763
764impl HidDevice {
765    /// Create a new HID device with the given descriptor
766    pub fn new(descriptor: HidDescriptor) -> Self {
767        Self {
768            descriptor,
769            state: HidState::Disconnected,
770            control_cid: 0,
771            interrupt_cid: 0,
772            last_input_report: HidReport::new(HidReportType::Input, 0),
773        }
774    }
775
776    /// Send an output report to the device (e.g., keyboard LEDs)
777    pub(crate) fn send_report(&self, report: &HidReport) -> Result<(), KernelError> {
778        if self.state != HidState::Ready {
779            return Err(KernelError::InvalidState {
780                expected: "Ready",
781                actual: "not Ready",
782            });
783        }
784        if report.report_type != HidReportType::Output
785            && report.report_type != HidReportType::Feature
786        {
787            return Err(KernelError::InvalidArgument {
788                name: "report_type",
789                value: "send_report expects Output or Feature",
790            });
791        }
792        // In a real implementation, this would send via L2CAP interrupt channel
793        let _ = report;
794        Ok(())
795    }
796
797    /// Receive an input report from the device
798    pub(crate) fn receive_report(&mut self, data: &[u8]) -> Result<HidReport, KernelError> {
799        if self.state != HidState::Ready {
800            return Err(KernelError::InvalidState {
801                expected: "Ready",
802                actual: "not Ready",
803            });
804        }
805        if data.is_empty() {
806            return Err(KernelError::InvalidArgument {
807                name: "data",
808                value: "empty report data",
809            });
810        }
811
812        let report = HidReport::with_data(HidReportType::Input, 0, data);
813        self.last_input_report = report.clone();
814        Ok(report)
815    }
816
817    /// Parse a raw HID report into individual field values
818    ///
819    /// Returns a list of field values extracted based on
820    /// report_size/report_count
821    pub(crate) fn parse_report(&self, report: &HidReport) -> Result<ParsedHidReport, KernelError> {
822        if report.data_len == 0 {
823            return Err(KernelError::InvalidArgument {
824                name: "report",
825                value: "empty report data",
826            });
827        }
828
829        let report_size = self.descriptor.report_size as usize;
830        let report_count = self.descriptor.report_count as usize;
831        let mut fields = [0u32; 16]; // Up to 16 fields
832        let mut field_count = 0;
833
834        for (i, field) in fields.iter_mut().enumerate().take(report_count.min(16)) {
835            let bit_offset = i * report_size;
836            let byte_offset = bit_offset / 8;
837            let bit_shift = bit_offset % 8;
838
839            if byte_offset >= report.data_len {
840                break;
841            }
842
843            let mut value = report.data[byte_offset] as u32 >> bit_shift;
844            // Handle fields spanning byte boundaries
845            if bit_shift + report_size > 8 && byte_offset + 1 < report.data_len {
846                value |= (report.data[byte_offset + 1] as u32) << (8 - bit_shift);
847            }
848            value &= (1u32 << report_size) - 1;
849            *field = value;
850            field_count += 1;
851        }
852
853        Ok(ParsedHidReport {
854            fields,
855            field_count,
856        })
857    }
858
859    /// Connect the HID device
860    pub(crate) fn connect(&mut self, control_cid: u16, interrupt_cid: u16) {
861        self.control_cid = control_cid;
862        self.interrupt_cid = interrupt_cid;
863        self.state = HidState::Connected;
864    }
865
866    /// Set the device to ready state
867    pub(crate) fn set_ready(&mut self) {
868        if self.state == HidState::Connected {
869            self.state = HidState::Ready;
870        }
871    }
872
873    /// Disconnect the HID device
874    pub(crate) fn disconnect(&mut self) {
875        self.state = HidState::Disconnected;
876        self.control_cid = 0;
877        self.interrupt_cid = 0;
878    }
879}
880
881/// Parsed HID report with extracted field values
882#[derive(Debug, Clone)]
883pub struct ParsedHidReport {
884    /// Extracted field values
885    pub fields: [u32; 16],
886    /// Number of valid fields
887    pub field_count: usize,
888}
889
890impl ParsedHidReport {
891    /// Get field value at index
892    pub(crate) fn field(&self, index: usize) -> Option<u32> {
893        if index < self.field_count {
894            Some(self.fields[index])
895        } else {
896            None
897        }
898    }
899}
900
901// ---------------------------------------------------------------------------
902// SPP (Serial Port Profile)
903// ---------------------------------------------------------------------------
904
905/// Serial Port Profile: wraps an RFCOMM channel for serial communication
906pub struct SerialPortProfile {
907    /// RFCOMM DLCI for this serial port
908    pub dlci: u8,
909    /// Whether the serial port is connected
910    pub connected: bool,
911    /// Baud rate (informational, not enforced over Bluetooth)
912    pub baud_rate: u32,
913    /// Data bits (5-8)
914    pub data_bits: u8,
915    /// Stop bits (1 or 2)
916    pub stop_bits: u8,
917    /// Parity: 0=none, 1=odd, 2=even
918    pub parity: u8,
919    /// Read buffer
920    pub read_buf: [u8; 256],
921    /// Number of valid bytes in read buffer
922    pub read_len: usize,
923}
924
925impl Default for SerialPortProfile {
926    fn default() -> Self {
927        Self::new(1)
928    }
929}
930
931impl SerialPortProfile {
932    /// Create a new SPP instance for the given RFCOMM DLCI
933    pub fn new(dlci: u8) -> Self {
934        Self {
935            dlci,
936            connected: false,
937            baud_rate: 115200,
938            data_bits: 8,
939            stop_bits: 1,
940            parity: 0,
941            read_buf: [0u8; 256],
942            read_len: 0,
943        }
944    }
945
946    /// Connect the serial port
947    pub(crate) fn connect(&mut self) -> Result<(), KernelError> {
948        if self.connected {
949            return Err(KernelError::InvalidState {
950                expected: "disconnected",
951                actual: "connected",
952            });
953        }
954        self.connected = true;
955        Ok(())
956    }
957
958    /// Disconnect the serial port
959    pub(crate) fn disconnect(&mut self) -> Result<(), KernelError> {
960        if !self.connected {
961            return Err(KernelError::InvalidState {
962                expected: "connected",
963                actual: "disconnected",
964            });
965        }
966        self.connected = false;
967        self.read_len = 0;
968        Ok(())
969    }
970
971    /// Write data to the serial port (to be sent over RFCOMM)
972    ///
973    /// Returns the data slice that should be sent via RFCOMM
974    pub(crate) fn write<'a>(&self, data: &'a [u8]) -> Result<&'a [u8], KernelError> {
975        if !self.connected {
976            return Err(KernelError::InvalidState {
977                expected: "connected",
978                actual: "disconnected",
979            });
980        }
981        if data.is_empty() {
982            return Err(KernelError::InvalidArgument {
983                name: "data",
984                value: "empty write data",
985            });
986        }
987        // Data passes through to RFCOMM layer
988        Ok(data)
989    }
990
991    /// Buffer received data from RFCOMM
992    pub(crate) fn receive(&mut self, data: &[u8]) -> Result<usize, KernelError> {
993        if !self.connected {
994            return Err(KernelError::InvalidState {
995                expected: "connected",
996                actual: "disconnected",
997            });
998        }
999        let available = self.read_buf.len() - self.read_len;
1000        let copy_len = data.len().min(available);
1001        self.read_buf[self.read_len..self.read_len + copy_len].copy_from_slice(&data[..copy_len]);
1002        self.read_len += copy_len;
1003        Ok(copy_len)
1004    }
1005
1006    /// Read buffered data
1007    ///
1008    /// Returns number of bytes read
1009    pub(crate) fn read(&mut self, buf: &mut [u8]) -> usize {
1010        let copy_len = buf.len().min(self.read_len);
1011        buf[..copy_len].copy_from_slice(&self.read_buf[..copy_len]);
1012        // Shift remaining data forward
1013        if copy_len < self.read_len {
1014            let remaining = self.read_len - copy_len;
1015            // Use a temporary buffer to avoid overlapping copy
1016            let mut temp = [0u8; 256];
1017            temp[..remaining].copy_from_slice(&self.read_buf[copy_len..copy_len + remaining]);
1018            self.read_buf[..remaining].copy_from_slice(&temp[..remaining]);
1019        }
1020        self.read_len -= copy_len;
1021        copy_len
1022    }
1023}
1024
1025// ---------------------------------------------------------------------------
1026// Tests
1027// ---------------------------------------------------------------------------
1028
1029#[cfg(test)]
1030mod tests {
1031    #[cfg(feature = "alloc")]
1032    #[allow(unused_imports)]
1033    use alloc::vec;
1034
1035    use super::*;
1036
1037    #[test]
1038    fn test_sdp_attribute_types() {
1039        assert!(SdpAttribute::Nil.is_nil());
1040        assert!(!SdpAttribute::Uint8(0).is_nil());
1041        assert_eq!(SdpAttribute::Uint16(0x1101).as_u16(), Some(0x1101));
1042        assert_eq!(SdpAttribute::Uuid16(0x0003).as_uuid16(), Some(0x0003));
1043        assert_eq!(SdpAttribute::Uint32(42).as_u16(), None);
1044    }
1045
1046    #[cfg(feature = "alloc")]
1047    #[test]
1048    fn test_sdp_database_register_find() {
1049        let mut db = SdpDatabase::new();
1050        let h1 = db.register_service(UUID_SERIAL_PORT, "Serial Port");
1051        let h2 = db.register_service(UUID_A2DP_SINK, "A2DP Sink");
1052        assert_eq!(db.record_count(), 2);
1053
1054        let rec = db.find_by_uuid(UUID_SERIAL_PORT).unwrap();
1055        assert_eq!(rec.handle, h1);
1056        assert_eq!(rec.service_name, "Serial Port");
1057
1058        let rec2 = db.find_by_uuid(UUID_A2DP_SINK).unwrap();
1059        assert_eq!(rec2.handle, h2);
1060    }
1061
1062    #[cfg(feature = "alloc")]
1063    #[test]
1064    fn test_sdp_database_remove() {
1065        let mut db = SdpDatabase::new();
1066        let h = db.register_service(UUID_HID, "HID");
1067        assert_eq!(db.record_count(), 1);
1068        assert!(db.remove_service(h));
1069        assert_eq!(db.record_count(), 0);
1070        assert!(!db.remove_service(h)); // Already removed
1071    }
1072
1073    #[cfg(feature = "alloc")]
1074    #[test]
1075    fn test_sdp_search() {
1076        let mut db = SdpDatabase::new();
1077        db.register_service(UUID_SERIAL_PORT, "SPP 1");
1078        db.register_service(UUID_SERIAL_PORT, "SPP 2");
1079        db.register_service(UUID_A2DP_SINK, "A2DP");
1080        let results = db.search(UUID_SERIAL_PORT);
1081        assert_eq!(results.len(), 2);
1082    }
1083
1084    #[test]
1085    fn test_sbc_config_default_valid() {
1086        let config = SbcConfig::default();
1087        assert!(config.validate().is_ok());
1088    }
1089
1090    #[test]
1091    fn test_sbc_config_invalid_subbands() {
1092        let mut config = SbcConfig::default();
1093        config.subbands = 3;
1094        assert!(config.validate().is_err());
1095    }
1096
1097    #[test]
1098    fn test_sbc_config_to_bytes() {
1099        let config = SbcConfig::default();
1100        let bytes = config.to_bytes();
1101        // Sample freq 44100 (0x20) | Joint Stereo (0x01)
1102        assert_eq!(bytes[0], 0x21);
1103        // Block 16 (0x10) | Subbands 8 (0x04) | Loudness (0x01)
1104        assert_eq!(bytes[1], 0x15);
1105        assert_eq!(bytes[2], 2); // bitpool_min
1106        assert_eq!(bytes[3], 53); // bitpool_max
1107    }
1108
1109    #[test]
1110    fn test_a2dp_sink_lifecycle() {
1111        let mut sink = A2dpSink::new();
1112        assert_eq!(sink.state, A2dpState::Idle);
1113
1114        sink.configure(A2dpCodec::Sbc, 44100, 2).unwrap();
1115        assert_eq!(sink.state, A2dpState::Configured);
1116
1117        sink.start_stream().unwrap();
1118        assert_eq!(sink.state, A2dpState::Streaming);
1119
1120        sink.suspend().unwrap();
1121        assert_eq!(sink.state, A2dpState::Suspended);
1122
1123        sink.start_stream().unwrap();
1124        assert_eq!(sink.state, A2dpState::Streaming);
1125
1126        sink.close();
1127        assert_eq!(sink.state, A2dpState::Idle);
1128    }
1129
1130    #[test]
1131    fn test_hid_report_creation() {
1132        let data = [0x01, 0x02, 0x03];
1133        let report = HidReport::with_data(HidReportType::Input, 1, &data);
1134        assert_eq!(report.report_type, HidReportType::Input);
1135        assert_eq!(report.report_id, 1);
1136        assert_eq!(report.as_bytes(), &data);
1137    }
1138
1139    #[test]
1140    fn test_hid_descriptor_keyboard() {
1141        let desc = HidDescriptor::keyboard();
1142        assert_eq!(desc.usage_page, 0x01);
1143        assert_eq!(desc.usage, 0x06);
1144        assert_eq!(desc.report_byte_size(), 6);
1145    }
1146
1147    #[test]
1148    fn test_hid_device_lifecycle() {
1149        let mut dev = HidDevice::new(HidDescriptor::keyboard());
1150        assert_eq!(dev.state, HidState::Disconnected);
1151
1152        dev.connect(0x0011, 0x0013);
1153        assert_eq!(dev.state, HidState::Connected);
1154
1155        dev.set_ready();
1156        assert_eq!(dev.state, HidState::Ready);
1157
1158        let data = [0x00, 0x00, 0x04, 0x00, 0x00, 0x00]; // 'a' key
1159        let report = dev.receive_report(&data).unwrap();
1160        assert_eq!(report.data_len, 6);
1161
1162        dev.disconnect();
1163        assert_eq!(dev.state, HidState::Disconnected);
1164    }
1165
1166    #[test]
1167    fn test_hid_parse_report() {
1168        let dev = HidDevice::new(HidDescriptor {
1169            usage_page: 0x01,
1170            usage: 0x02,
1171            report_size: 8,
1172            report_count: 3,
1173        });
1174        let report = HidReport::with_data(HidReportType::Input, 0, &[0x01, 0x0A, 0xF0]);
1175        // Device must be ready to use parse_report (no state check in parse)
1176        let parsed = dev.parse_report(&report).unwrap();
1177        assert_eq!(parsed.field_count, 3);
1178        assert_eq!(parsed.field(0), Some(0x01));
1179        assert_eq!(parsed.field(1), Some(0x0A));
1180        assert_eq!(parsed.field(2), Some(0xF0));
1181        assert_eq!(parsed.field(3), None);
1182    }
1183
1184    #[test]
1185    fn test_spp_read_write() {
1186        let mut spp = SerialPortProfile::new(5);
1187        spp.connect().unwrap();
1188
1189        // Write passes through
1190        let data = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello"
1191        let result = spp.write(&data).unwrap();
1192        assert_eq!(result, &data);
1193
1194        // Receive some data
1195        let incoming = [0x41, 0x42, 0x43]; // "ABC"
1196        let received = spp.receive(&incoming).unwrap();
1197        assert_eq!(received, 3);
1198
1199        // Read it back
1200        let mut buf = [0u8; 10];
1201        let read = spp.read(&mut buf);
1202        assert_eq!(read, 3);
1203        assert_eq!(&buf[..3], &incoming);
1204
1205        spp.disconnect().unwrap();
1206    }
1207
1208    #[test]
1209    fn test_spp_errors() {
1210        let mut spp = SerialPortProfile::new(1);
1211        // Cannot write when disconnected
1212        assert!(spp.write(&[0x01]).is_err());
1213        // Cannot disconnect when not connected
1214        assert!(spp.disconnect().is_err());
1215        // Cannot connect twice
1216        spp.connect().unwrap();
1217        assert!(spp.connect().is_err());
1218    }
1219}