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

veridian_kernel/drivers/bluetooth/
rfcomm.rs

1//! Bluetooth RFCOMM (Serial Port Emulation Protocol)
2//!
3//! Implements the RFCOMM multiplexer protocol over L2CAP (PSM 0x0003),
4//! providing RS-232 serial port emulation. Supports credit-based flow
5//! control, FCS computation, modem status commands, and DLCI-based
6//! multiplexing for multiple simultaneous serial connections.
7//!
8//! Reference: Bluetooth Core Specification v5.4, RFCOMM with TS 07.10
9
10#![allow(dead_code)]
11
12#[cfg(feature = "alloc")]
13use alloc::collections::BTreeMap;
14#[cfg(feature = "alloc")]
15#[allow(unused_imports)]
16use alloc::vec::Vec;
17
18use crate::error::KernelError;
19
20// ---------------------------------------------------------------------------
21// RFCOMM Frame Types
22// ---------------------------------------------------------------------------
23
24/// SABM (Set Asynchronous Balanced Mode) - connection establishment
25pub const FRAME_SABM: u8 = 0x2F;
26
27/// UA (Unnumbered Acknowledgement) - acknowledgement
28pub const FRAME_UA: u8 = 0x63;
29
30/// DM (Disconnected Mode) - rejection / channel not available
31pub const FRAME_DM: u8 = 0x0F;
32
33/// DISC (Disconnect) - disconnect request
34pub const FRAME_DISC: u8 = 0x43;
35
36/// UIH (Unnumbered Information with Header check) - data transfer
37pub const FRAME_UIH: u8 = 0xEF;
38
39// ---------------------------------------------------------------------------
40// RFCOMM Address Field
41// ---------------------------------------------------------------------------
42
43/// Maximum DLCI value (5 bits, 0-30 usable; 31 reserved)
44pub const MAX_DLCI: u8 = 30;
45
46/// DLCI 0 is the multiplexer control channel
47pub const DLCI_CONTROL: u8 = 0;
48
49/// L2CAP PSM for RFCOMM
50pub const RFCOMM_PSM: u16 = 0x0003;
51
52/// Default RFCOMM MTU (maximum frame size, N1)
53pub const RFCOMM_DEFAULT_MTU: u16 = 127;
54
55/// Maximum RFCOMM MTU
56pub const RFCOMM_MAX_MTU: u16 = 32767;
57
58/// Default initial credits for credit-based flow control
59pub const DEFAULT_INITIAL_CREDITS: u8 = 7;
60
61/// Encode the RFCOMM address byte from DLCI, C/R bit, and EA bit
62///
63/// Format: D5 D4 D3 D2 D1 C/R EA
64/// - DLCI in bits [7:2]
65/// - C/R (Command/Response) in bit 1
66/// - EA (Extended Address) in bit 0 (always 1 for single-byte)
67pub const fn encode_address(dlci: u8, cr: bool, ea: bool) -> u8 {
68    ((dlci & 0x3F) << 2) | ((cr as u8) << 1) | (ea as u8)
69}
70
71/// Decode the DLCI from an RFCOMM address byte
72pub const fn decode_dlci(address: u8) -> u8 {
73    (address >> 2) & 0x3F
74}
75
76/// Decode the C/R bit from an RFCOMM address byte
77pub const fn decode_cr(address: u8) -> bool {
78    (address & 0x02) != 0
79}
80
81// ---------------------------------------------------------------------------
82// FCS (Frame Check Sequence) - CRC-8
83// ---------------------------------------------------------------------------
84
85/// CRC-8 lookup table (polynomial 0xE0, reversed)
86const FCS_TABLE: [u8; 256] = generate_fcs_table();
87
88/// Generate the FCS lookup table at compile time
89const fn generate_fcs_table() -> [u8; 256] {
90    let mut table = [0u8; 256];
91    let mut i = 0u16;
92    while i < 256 {
93        let mut crc = i as u8;
94        let mut bit = 0;
95        while bit < 8 {
96            if crc & 1 != 0 {
97                crc = (crc >> 1) ^ 0xE0;
98            } else {
99                crc >>= 1;
100            }
101            bit += 1;
102        }
103        table[i as usize] = crc;
104        i += 1;
105    }
106    table
107}
108
109/// Calculate FCS over a byte slice
110pub fn calculate_fcs(data: &[u8]) -> u8 {
111    let mut fcs = 0xFFu8;
112    for &byte in data {
113        fcs = FCS_TABLE[(fcs ^ byte) as usize];
114    }
115    // Complement
116    0xFF - fcs
117}
118
119/// Verify FCS: compute over data + fcs byte, result should be 0xCF
120pub fn verify_fcs(data: &[u8], fcs: u8) -> bool {
121    let mut crc = 0xFFu8;
122    for &byte in data {
123        crc = FCS_TABLE[(crc ^ byte) as usize];
124    }
125    crc = FCS_TABLE[(crc ^ fcs) as usize];
126    crc == 0xCF
127}
128
129// ---------------------------------------------------------------------------
130// RFCOMM Frame
131// ---------------------------------------------------------------------------
132
133/// Maximum RFCOMM frame data size for fixed-size buffers
134pub const RFCOMM_MAX_FRAME_DATA: usize = 512;
135
136/// An RFCOMM frame
137#[derive(Debug, Clone)]
138pub struct RfcommFrame {
139    /// Address byte (DLCI + C/R + EA)
140    pub address: u8,
141    /// Control byte (frame type)
142    pub control: u8,
143    /// Length of the information field
144    pub length: u16,
145    /// Information field (data payload)
146    #[cfg(feature = "alloc")]
147    pub data: Vec<u8>,
148    #[cfg(not(feature = "alloc"))]
149    pub data: [u8; RFCOMM_MAX_FRAME_DATA],
150    #[cfg(not(feature = "alloc"))]
151    pub data_len: usize,
152    /// Frame Check Sequence
153    pub fcs: u8,
154    /// Credit byte (present in UIH frames when P/F bit set in credit-based
155    /// flow)
156    pub credits: Option<u8>,
157}
158
159impl RfcommFrame {
160    /// Create a new RFCOMM frame
161    #[cfg(feature = "alloc")]
162    pub fn new(dlci: u8, control: u8, data: &[u8]) -> Self {
163        let address = encode_address(dlci, true, true);
164        // Compute FCS over address + control (for UIH frames)
165        // For SABM/UA/DM/DISC, FCS covers address + control + length
166        let fcs = if control == FRAME_UIH {
167            calculate_fcs(&[address, control])
168        } else {
169            // For non-UIH, FCS covers address + control
170            // (length included for SABM/UA/DM/DISC but spec says addr+ctrl for those too)
171            calculate_fcs(&[address, control])
172        };
173        Self {
174            address,
175            control,
176            length: data.len() as u16,
177            data: Vec::from(data),
178            fcs,
179            credits: None,
180        }
181    }
182
183    /// Create a SABM frame for connection establishment
184    #[cfg(feature = "alloc")]
185    pub fn sabm(dlci: u8) -> Self {
186        Self::new(dlci, FRAME_SABM, &[])
187    }
188
189    /// Create a UA (acknowledgement) frame
190    #[cfg(feature = "alloc")]
191    pub fn ua(dlci: u8) -> Self {
192        Self::new(dlci, FRAME_UA, &[])
193    }
194
195    /// Create a DM (disconnected mode) frame
196    #[cfg(feature = "alloc")]
197    pub fn dm(dlci: u8) -> Self {
198        Self::new(dlci, FRAME_DM, &[])
199    }
200
201    /// Create a DISC (disconnect) frame
202    #[cfg(feature = "alloc")]
203    pub fn disc(dlci: u8) -> Self {
204        Self::new(dlci, FRAME_DISC, &[])
205    }
206
207    /// Create a UIH (data) frame with optional credits
208    #[cfg(feature = "alloc")]
209    pub fn uih(dlci: u8, data: &[u8], credits: Option<u8>) -> Self {
210        let mut frame = Self::new(dlci, FRAME_UIH, data);
211        frame.credits = credits;
212        frame
213    }
214
215    /// Get the DLCI from this frame's address byte
216    pub fn dlci(&self) -> u8 {
217        decode_dlci(self.address)
218    }
219
220    /// Check if this is a UIH (data) frame
221    pub fn is_uih(&self) -> bool {
222        self.control == FRAME_UIH
223    }
224
225    /// Verify the FCS of this frame
226    pub fn verify_fcs(&self) -> bool {
227        verify_fcs(&[self.address, self.control], self.fcs)
228    }
229
230    /// Serialize frame to buffer, returns bytes written
231    #[cfg(feature = "alloc")]
232    pub fn serialize(&self, buf: &mut [u8]) -> Result<usize, KernelError> {
233        // Address(1) + Control(1) + Length(1 or 2) + Data + Credits?(1) + FCS(1)
234        let length_bytes = if self.length > 127 { 2 } else { 1 };
235        let credit_bytes = if self.credits.is_some() { 1 } else { 0 };
236        let total = 1 + 1 + length_bytes + self.data.len() + credit_bytes + 1;
237
238        if buf.len() < total {
239            return Err(KernelError::InvalidArgument {
240                name: "buffer",
241                value: "too small for RFCOMM frame",
242            });
243        }
244
245        let mut pos = 0;
246        buf[pos] = self.address;
247        pos += 1;
248        buf[pos] = self.control;
249        pos += 1;
250
251        // Length field: EA bit in bit 0 (1 = last byte, 0 = continued)
252        if self.length <= 127 {
253            buf[pos] = ((self.length as u8) << 1) | 0x01; // EA=1
254            pos += 1;
255        } else {
256            buf[pos] = (self.length as u8) << 1; // EA=0
257            pos += 1;
258            buf[pos] = (self.length >> 7) as u8;
259            pos += 1;
260        }
261
262        // Credits (for UIH with credit-based flow)
263        if let Some(credits) = self.credits {
264            buf[pos] = credits;
265            pos += 1;
266        }
267
268        // Data
269        buf[pos..pos + self.data.len()].copy_from_slice(&self.data);
270        pos += self.data.len();
271
272        // FCS
273        buf[pos] = self.fcs;
274        pos += 1;
275
276        Ok(pos)
277    }
278}
279
280// ---------------------------------------------------------------------------
281// Modem Status Command (MSC)
282// ---------------------------------------------------------------------------
283
284/// Modem signal bits for MSC (Modem Status Command)
285#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
286pub struct ModemSignals {
287    /// Data Terminal Ready
288    pub dtr: bool,
289    /// Request To Send
290    pub rts: bool,
291    /// Ring Indicator
292    pub ri: bool,
293    /// Data Carrier Detect
294    pub dcd: bool,
295}
296
297impl ModemSignals {
298    /// Encode modem signals into a V.24 signal byte
299    ///
300    /// Bit mapping: FC=0, RTC(DTR)=bit2, RTR(RTS)=bit3, IC(RI)=bit6,
301    /// DV(DCD)=bit7
302    pub fn to_byte(&self) -> u8 {
303        let mut val = 0x01u8; // EA bit always set
304        if self.dtr {
305            val |= 0x04;
306        }
307        if self.rts {
308            val |= 0x08;
309        }
310        if self.ri {
311            val |= 0x40;
312        }
313        if self.dcd {
314            val |= 0x80;
315        }
316        val
317    }
318
319    /// Decode modem signals from a V.24 signal byte
320    pub fn from_byte(val: u8) -> Self {
321        Self {
322            dtr: val & 0x04 != 0,
323            rts: val & 0x08 != 0,
324            ri: val & 0x40 != 0,
325            dcd: val & 0x80 != 0,
326        }
327    }
328}
329
330// ---------------------------------------------------------------------------
331// Multiplexer Control Commands
332// ---------------------------------------------------------------------------
333
334/// RFCOMM multiplexer control command types
335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
336#[repr(u8)]
337pub enum MuxCommandType {
338    /// Parameter Negotiation (PN) - MTU, priority, flow control
339    ParameterNegotiation = 0x20,
340    /// Modem Status Command (MSC) - DTR/RTS/RI/DCD signals
341    ModemStatusCommand = 0x38,
342    /// Remote Port Negotiation (RPN) - baud rate, data bits, etc.
343    RemotePortNegotiation = 0x24,
344    /// Test Command
345    Test = 0x08,
346    /// Flow Control On (aggregate)
347    FlowControlOn = 0x28,
348    /// Flow Control Off (aggregate)
349    FlowControlOff = 0x18,
350    /// Non-Supported Command Response
351    NonSupported = 0x04,
352}
353
354impl MuxCommandType {
355    /// Parse from raw byte (upper 6 bits, bit 1 = C/R, bit 0 = EA)
356    pub fn from_u8(val: u8) -> Option<Self> {
357        match val & 0xFC {
358            0x20 => Some(Self::ParameterNegotiation),
359            0x38 => Some(Self::ModemStatusCommand),
360            0x24 => Some(Self::RemotePortNegotiation),
361            0x08 => Some(Self::Test),
362            0x28 => Some(Self::FlowControlOn),
363            0x18 => Some(Self::FlowControlOff),
364            0x04 => Some(Self::NonSupported),
365            _ => None,
366        }
367    }
368}
369
370// ---------------------------------------------------------------------------
371// Credit-Based Flow Control
372// ---------------------------------------------------------------------------
373
374/// Credit-based flow control state for an RFCOMM channel
375#[derive(Debug, Clone, Copy)]
376pub struct CreditFlowControl {
377    /// Credits granted by remote peer (how many frames we can send)
378    pub tx_credits: u8,
379    /// Credits granted to remote peer (how many frames they can send)
380    pub rx_credits: u8,
381    /// Initial credits to grant at connection setup
382    pub initial_credits: u8,
383}
384
385impl Default for CreditFlowControl {
386    fn default() -> Self {
387        Self {
388            tx_credits: DEFAULT_INITIAL_CREDITS,
389            rx_credits: DEFAULT_INITIAL_CREDITS,
390            initial_credits: DEFAULT_INITIAL_CREDITS,
391        }
392    }
393}
394
395impl CreditFlowControl {
396    /// Create with specific initial credits
397    pub fn with_initial(initial: u8) -> Self {
398        Self {
399            tx_credits: initial,
400            rx_credits: initial,
401            initial_credits: initial,
402        }
403    }
404
405    /// Grant additional credits to the remote peer
406    pub fn grant_credits(&mut self, count: u8) {
407        self.rx_credits = self.rx_credits.saturating_add(count);
408    }
409
410    /// Consume one TX credit (returns false if no credits available)
411    pub fn consume_credit(&mut self) -> bool {
412        if self.tx_credits > 0 {
413            self.tx_credits -= 1;
414            true
415        } else {
416            false
417        }
418    }
419
420    /// Add TX credits (received from remote peer)
421    pub fn add_tx_credits(&mut self, count: u8) {
422        self.tx_credits = self.tx_credits.saturating_add(count);
423    }
424
425    /// Check if we can send (have TX credits)
426    pub fn can_send(&self) -> bool {
427        self.tx_credits > 0
428    }
429
430    /// Check if remote peer's credit is low and needs replenishment
431    pub fn needs_replenish(&self) -> bool {
432        self.rx_credits < 2
433    }
434}
435
436// ---------------------------------------------------------------------------
437// RFCOMM Channel State
438// ---------------------------------------------------------------------------
439
440/// RFCOMM channel state
441#[derive(Debug, Clone, Copy, PartialEq, Eq)]
442pub enum ChannelState {
443    /// Channel is closed
444    Closed,
445    /// SABM sent, waiting for UA
446    Opening,
447    /// Channel is open and ready for data
448    Open,
449    /// DISC sent, waiting for UA/DM
450    Closing,
451}
452
453// ---------------------------------------------------------------------------
454// RFCOMM Channel
455// ---------------------------------------------------------------------------
456
457/// An RFCOMM data channel (one per DLCI)
458#[derive(Debug, Clone)]
459pub struct RfcommChannel {
460    /// Data Link Connection Identifier (1-30)
461    pub dlci: u8,
462    /// Current channel state
463    pub state: ChannelState,
464    /// Credit-based flow control
465    pub flow_control: CreditFlowControl,
466    /// Negotiated MTU (N1 parameter)
467    pub mtu: u16,
468    /// Underlying L2CAP channel ID
469    pub l2cap_cid: u16,
470    /// Modem signals
471    pub modem_signals: ModemSignals,
472    /// Channel priority (0-63)
473    pub priority: u8,
474}
475
476impl RfcommChannel {
477    /// Create a new RFCOMM channel
478    pub fn new(dlci: u8, l2cap_cid: u16) -> Self {
479        Self {
480            dlci,
481            state: ChannelState::Closed,
482            flow_control: CreditFlowControl::default(),
483            mtu: RFCOMM_DEFAULT_MTU,
484            l2cap_cid,
485            modem_signals: ModemSignals::default(),
486            priority: 0,
487        }
488    }
489
490    /// Check if channel is open
491    pub fn is_open(&self) -> bool {
492        self.state == ChannelState::Open
493    }
494}
495
496// ---------------------------------------------------------------------------
497// RFCOMM Multiplexer
498// ---------------------------------------------------------------------------
499
500/// RFCOMM multiplexer: manages all channels over a single L2CAP connection
501#[cfg(feature = "alloc")]
502pub struct RfcommMultiplexer {
503    /// Active channels indexed by DLCI
504    channels: BTreeMap<u8, RfcommChannel>,
505    /// L2CAP channel ID for the underlying L2CAP connection
506    l2cap_cid: u16,
507    /// Whether the multiplexer session is established (DLCI 0 SABM/UA done)
508    session_open: bool,
509    /// Whether we are the initiator of the multiplexer session
510    is_initiator: bool,
511}
512
513#[cfg(feature = "alloc")]
514impl Default for RfcommMultiplexer {
515    fn default() -> Self {
516        Self::new(0)
517    }
518}
519
520#[cfg(feature = "alloc")]
521impl RfcommMultiplexer {
522    /// Create a new RFCOMM multiplexer
523    pub fn new(l2cap_cid: u16) -> Self {
524        Self {
525            channels: BTreeMap::new(),
526            l2cap_cid,
527            session_open: false,
528            is_initiator: false,
529        }
530    }
531
532    /// Get the underlying L2CAP CID
533    pub fn l2cap_cid(&self) -> u16 {
534        self.l2cap_cid
535    }
536
537    /// Check if the multiplexer session is established
538    pub fn is_session_open(&self) -> bool {
539        self.session_open
540    }
541
542    /// Get number of active channels
543    pub fn channel_count(&self) -> usize {
544        self.channels.len()
545    }
546
547    /// Get a channel by DLCI
548    pub fn get_channel(&self, dlci: u8) -> Option<&RfcommChannel> {
549        self.channels.get(&dlci)
550    }
551
552    /// Start multiplexer session (send SABM on DLCI 0)
553    ///
554    /// Returns the SABM frame to send over L2CAP
555    pub fn start_session(&mut self) -> Result<RfcommFrame, KernelError> {
556        if self.session_open {
557            return Err(KernelError::InvalidState {
558                expected: "session closed",
559                actual: "session open",
560            });
561        }
562        self.is_initiator = true;
563        Ok(RfcommFrame::sabm(DLCI_CONTROL))
564    }
565
566    /// Open a data channel on the given DLCI
567    ///
568    /// Returns the SABM frame to send over L2CAP
569    pub fn open_channel(&mut self, dlci: u8) -> Result<RfcommFrame, KernelError> {
570        if !self.session_open {
571            return Err(KernelError::InvalidState {
572                expected: "session open",
573                actual: "session closed",
574            });
575        }
576        if dlci == DLCI_CONTROL || dlci > MAX_DLCI {
577            return Err(KernelError::InvalidArgument {
578                name: "dlci",
579                value: "must be 1-30",
580            });
581        }
582        if self.channels.contains_key(&dlci) {
583            return Err(KernelError::InvalidArgument {
584                name: "dlci",
585                value: "channel already exists",
586            });
587        }
588
589        let mut channel = RfcommChannel::new(dlci, self.l2cap_cid);
590        channel.state = ChannelState::Opening;
591        self.channels.insert(dlci, channel);
592
593        Ok(RfcommFrame::sabm(dlci))
594    }
595
596    /// Close a data channel on the given DLCI
597    ///
598    /// Returns the DISC frame to send over L2CAP
599    pub fn close_channel(&mut self, dlci: u8) -> Result<RfcommFrame, KernelError> {
600        let channel = self
601            .channels
602            .get_mut(&dlci)
603            .ok_or(KernelError::InvalidArgument {
604                name: "dlci",
605                value: "channel not found",
606            })?;
607
608        if channel.state != ChannelState::Open {
609            return Err(KernelError::InvalidState {
610                expected: "Open",
611                actual: "not Open",
612            });
613        }
614
615        channel.state = ChannelState::Closing;
616        Ok(RfcommFrame::disc(dlci))
617    }
618
619    /// Send data on an open RFCOMM channel
620    ///
621    /// Returns the UIH frame to send, or error if no credits available
622    pub fn send(&mut self, dlci: u8, data: &[u8]) -> Result<RfcommFrame, KernelError> {
623        let channel = self
624            .channels
625            .get_mut(&dlci)
626            .ok_or(KernelError::InvalidArgument {
627                name: "dlci",
628                value: "channel not found",
629            })?;
630
631        if channel.state != ChannelState::Open {
632            return Err(KernelError::InvalidState {
633                expected: "Open",
634                actual: "not Open",
635            });
636        }
637
638        if data.len() > channel.mtu as usize {
639            return Err(KernelError::InvalidArgument {
640                name: "data",
641                value: "exceeds channel MTU",
642            });
643        }
644
645        if !channel.flow_control.consume_credit() {
646            return Err(KernelError::ResourceExhausted {
647                resource: "RFCOMM TX credits",
648            });
649        }
650
651        // Grant credits back if remote is running low
652        let grant = if channel.flow_control.needs_replenish() {
653            let credits = channel.flow_control.initial_credits;
654            channel.flow_control.grant_credits(credits);
655            Some(credits)
656        } else {
657            None
658        };
659
660        Ok(RfcommFrame::uih(dlci, data, grant))
661    }
662
663    /// Process a received RFCOMM frame
664    ///
665    /// Returns an optional response frame to send back
666    pub fn receive(&mut self, frame: &RfcommFrame) -> Result<Option<RfcommFrame>, KernelError> {
667        let dlci = frame.dlci();
668
669        match frame.control {
670            FRAME_SABM => self.handle_sabm(dlci),
671            FRAME_UA => self.handle_ua(dlci),
672            FRAME_DM => self.handle_dm(dlci),
673            FRAME_DISC => self.handle_disc(dlci),
674            FRAME_UIH => self.handle_uih(dlci, frame),
675            _ => {
676                // Unknown frame type: respond with DM
677                Ok(Some(RfcommFrame::dm(dlci)))
678            }
679        }
680    }
681
682    /// Handle incoming SABM (connection request)
683    fn handle_sabm(&mut self, dlci: u8) -> Result<Option<RfcommFrame>, KernelError> {
684        if dlci == DLCI_CONTROL {
685            // Multiplexer session establishment
686            self.session_open = true;
687            Ok(Some(RfcommFrame::ua(DLCI_CONTROL)))
688        } else if dlci > MAX_DLCI {
689            Ok(Some(RfcommFrame::dm(dlci)))
690        } else {
691            // Incoming channel request
692            let mut channel = RfcommChannel::new(dlci, self.l2cap_cid);
693            channel.state = ChannelState::Open;
694            self.channels.insert(dlci, channel);
695            Ok(Some(RfcommFrame::ua(dlci)))
696        }
697    }
698
699    /// Handle incoming UA (acknowledgement)
700    fn handle_ua(&mut self, dlci: u8) -> Result<Option<RfcommFrame>, KernelError> {
701        if dlci == DLCI_CONTROL {
702            self.session_open = true;
703            Ok(None)
704        } else if let Some(channel) = self.channels.get_mut(&dlci) {
705            match channel.state {
706                ChannelState::Opening => {
707                    channel.state = ChannelState::Open;
708                }
709                ChannelState::Closing => {
710                    channel.state = ChannelState::Closed;
711                }
712                _ => {}
713            }
714            Ok(None)
715        } else {
716            Ok(None)
717        }
718    }
719
720    /// Handle incoming DM (rejected)
721    fn handle_dm(&mut self, dlci: u8) -> Result<Option<RfcommFrame>, KernelError> {
722        if dlci == DLCI_CONTROL {
723            self.session_open = false;
724        } else if let Some(channel) = self.channels.get_mut(&dlci) {
725            channel.state = ChannelState::Closed;
726        }
727        Ok(None)
728    }
729
730    /// Handle incoming DISC (disconnect)
731    fn handle_disc(&mut self, dlci: u8) -> Result<Option<RfcommFrame>, KernelError> {
732        if dlci == DLCI_CONTROL {
733            // Close entire multiplexer session
734            self.session_open = false;
735            for channel in self.channels.values_mut() {
736                channel.state = ChannelState::Closed;
737            }
738            Ok(Some(RfcommFrame::ua(DLCI_CONTROL)))
739        } else if let Some(channel) = self.channels.get_mut(&dlci) {
740            channel.state = ChannelState::Closed;
741            Ok(Some(RfcommFrame::ua(dlci)))
742        } else {
743            Ok(Some(RfcommFrame::dm(dlci)))
744        }
745    }
746
747    /// Handle incoming UIH (data)
748    fn handle_uih(
749        &mut self,
750        dlci: u8,
751        frame: &RfcommFrame,
752    ) -> Result<Option<RfcommFrame>, KernelError> {
753        if dlci == DLCI_CONTROL {
754            // Multiplexer control command
755            return self.process_control(frame);
756        }
757
758        let channel = self
759            .channels
760            .get_mut(&dlci)
761            .ok_or(KernelError::InvalidArgument {
762                name: "dlci",
763                value: "channel not found for UIH data",
764            })?;
765
766        if channel.state != ChannelState::Open {
767            return Err(KernelError::InvalidState {
768                expected: "Open",
769                actual: "not Open",
770            });
771        }
772
773        // Process credits from the frame
774        if let Some(credits) = frame.credits {
775            channel.flow_control.add_tx_credits(credits);
776        }
777
778        // Data is available in frame.data for upper-layer consumption
779        // (The caller should read frame.data after this returns Ok)
780
781        Ok(None)
782    }
783
784    /// Process a multiplexer control command (received on DLCI 0)
785    fn process_control(&mut self, frame: &RfcommFrame) -> Result<Option<RfcommFrame>, KernelError> {
786        #[cfg(feature = "alloc")]
787        {
788            if frame.data.is_empty() {
789                return Ok(None);
790            }
791
792            let cmd_type_byte = frame.data[0];
793            let cmd_type = MuxCommandType::from_u8(cmd_type_byte);
794
795            match cmd_type {
796                Some(MuxCommandType::ModemStatusCommand) => {
797                    // MSC: DLCI(1) + signals(1)
798                    if frame.data.len() >= 4 {
799                        let msc_dlci = (frame.data[2] >> 2) & 0x3F;
800                        let signals = ModemSignals::from_byte(frame.data[3]);
801                        if let Some(channel) = self.channels.get_mut(&msc_dlci) {
802                            channel.modem_signals = signals;
803                        }
804                    }
805                    // Respond with MSC acknowledgement (same data, C/R flipped)
806                    let mut resp_data = frame.data.clone();
807                    if !resp_data.is_empty() {
808                        resp_data[0] ^= 0x02; // Flip C/R bit
809                    }
810                    Ok(Some(RfcommFrame::uih(DLCI_CONTROL, &resp_data, None)))
811                }
812                Some(MuxCommandType::ParameterNegotiation) => {
813                    // PN: negotiate MTU and flow control
814                    if frame.data.len() >= 10 {
815                        let pn_dlci = frame.data[2] & 0x3F;
816                        let pn_mtu = u16::from_le_bytes([frame.data[6], frame.data[7]]);
817
818                        if let Some(channel) = self.channels.get_mut(&pn_dlci) {
819                            // Accept peer's MTU if within our limits
820                            channel.mtu = pn_mtu.min(RFCOMM_MAX_MTU);
821                        }
822                    }
823                    // Echo back PN response
824                    Ok(Some(RfcommFrame::uih(DLCI_CONTROL, &frame.data, None)))
825                }
826                Some(MuxCommandType::Test) => {
827                    // Test: echo back
828                    Ok(Some(RfcommFrame::uih(DLCI_CONTROL, &frame.data, None)))
829                }
830                _ => {
831                    // Non-supported command response
832                    let resp = [
833                        MuxCommandType::NonSupported as u8 | 0x03, // type + EA + C/R
834                        0x01,                                      // length EA
835                        cmd_type_byte,
836                    ];
837                    Ok(Some(RfcommFrame::uih(DLCI_CONTROL, &resp, None)))
838                }
839            }
840        }
841        #[cfg(not(feature = "alloc"))]
842        {
843            let _ = frame;
844            Ok(None)
845        }
846    }
847}
848
849// ---------------------------------------------------------------------------
850// Tests
851// ---------------------------------------------------------------------------
852
853#[cfg(test)]
854mod tests {
855    #[cfg(feature = "alloc")]
856    #[allow(unused_imports)]
857    use alloc::vec;
858
859    use super::*;
860
861    #[test]
862    fn test_encode_decode_address() {
863        let addr = encode_address(5, true, true);
864        assert_eq!(decode_dlci(addr), 5);
865        assert!(decode_cr(addr));
866    }
867
868    #[test]
869    fn test_encode_address_dlci_zero() {
870        let addr = encode_address(0, false, true);
871        assert_eq!(decode_dlci(addr), 0);
872        assert!(!decode_cr(addr));
873    }
874
875    #[test]
876    fn test_fcs_calculation() {
877        let data = [0x03, 0x3F]; // typical address + control
878        let fcs = calculate_fcs(&data);
879        assert!(verify_fcs(&data, fcs));
880    }
881
882    #[test]
883    fn test_fcs_verify_bad() {
884        let data = [0x03, 0x3F];
885        let fcs = calculate_fcs(&data);
886        assert!(!verify_fcs(&data, fcs.wrapping_add(1)));
887    }
888
889    #[test]
890    fn test_modem_signals_roundtrip() {
891        let signals = ModemSignals {
892            dtr: true,
893            rts: true,
894            ri: false,
895            dcd: true,
896        };
897        let byte = signals.to_byte();
898        let decoded = ModemSignals::from_byte(byte);
899        assert_eq!(decoded, signals);
900    }
901
902    #[test]
903    fn test_credit_flow_control() {
904        let mut fc = CreditFlowControl::with_initial(5);
905        assert!(fc.can_send());
906        assert_eq!(fc.tx_credits, 5);
907
908        for _ in 0..5 {
909            assert!(fc.consume_credit());
910        }
911        assert!(!fc.can_send());
912        assert!(!fc.consume_credit());
913
914        fc.add_tx_credits(3);
915        assert!(fc.can_send());
916        assert_eq!(fc.tx_credits, 3);
917    }
918
919    #[test]
920    fn test_credit_grant_and_replenish() {
921        let mut fc = CreditFlowControl::with_initial(3);
922        fc.rx_credits = 1;
923        assert!(fc.needs_replenish());
924        fc.grant_credits(3);
925        assert_eq!(fc.rx_credits, 4);
926        assert!(!fc.needs_replenish());
927    }
928
929    #[test]
930    fn test_mux_command_type_parse() {
931        assert_eq!(
932            MuxCommandType::from_u8(0x23),
933            Some(MuxCommandType::ParameterNegotiation)
934        );
935        assert_eq!(
936            MuxCommandType::from_u8(0x3B),
937            Some(MuxCommandType::ModemStatusCommand)
938        );
939        assert_eq!(MuxCommandType::from_u8(0xFF), None);
940    }
941
942    #[cfg(feature = "alloc")]
943    #[test]
944    fn test_multiplexer_session_lifecycle() {
945        let mut mux = RfcommMultiplexer::new(0x0040);
946        assert!(!mux.is_session_open());
947
948        // Start session
949        let sabm = mux.start_session().unwrap();
950        assert_eq!(sabm.dlci(), DLCI_CONTROL);
951        assert_eq!(sabm.control, FRAME_SABM);
952
953        // Simulate UA response
954        let ua = RfcommFrame::ua(DLCI_CONTROL);
955        mux.receive(&ua).unwrap();
956        assert!(mux.is_session_open());
957    }
958
959    #[cfg(feature = "alloc")]
960    #[test]
961    fn test_multiplexer_channel_open_close() {
962        let mut mux = RfcommMultiplexer::new(0x0040);
963        mux.session_open = true;
964
965        // Open channel
966        let sabm = mux.open_channel(5).unwrap();
967        assert_eq!(sabm.dlci(), 5);
968        assert_eq!(mux.channel_count(), 1);
969
970        // Simulate UA
971        let ua = RfcommFrame::ua(5);
972        mux.receive(&ua).unwrap();
973        assert_eq!(mux.get_channel(5).unwrap().state, ChannelState::Open);
974
975        // Close channel
976        let disc = mux.close_channel(5).unwrap();
977        assert_eq!(disc.control, FRAME_DISC);
978
979        // Simulate UA for close
980        let ua_close = RfcommFrame::ua(5);
981        mux.receive(&ua_close).unwrap();
982        assert_eq!(mux.get_channel(5).unwrap().state, ChannelState::Closed);
983    }
984}