Skip to main content

trouble_host/types/
l2cap.rs

1use bt_hci::{FixedSizeValue, WriteHci};
2
3pub(crate) const L2CAP_CID_ATT: u16 = 0x0004;
4pub(crate) const L2CAP_CID_LE_U_SIGNAL: u16 = 0x0005;
5pub(crate) const L2CAP_CID_LE_U_SECURITY_MANAGER: u16 = 0x0006;
6pub(crate) const L2CAP_CID_DYN_START: u16 = 0x0040;
7
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9#[derive(Debug, Clone, Copy)]
10#[repr(C)]
11pub struct L2capHeader {
12    pub length: u16,
13    pub channel: u16,
14}
15
16unsafe impl FixedSizeValue for L2capHeader {
17    fn is_valid(data: &[u8]) -> bool {
18        true
19    }
20}
21
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23#[repr(C)]
24#[derive(Debug, Clone, Copy)]
25pub struct L2capSignalHeader {
26    pub code: L2capSignalCode,
27    pub identifier: u8,
28    pub length: u16,
29}
30
31unsafe impl FixedSizeValue for L2capSignalHeader {
32    fn is_valid(data: &[u8]) -> bool {
33        true
34    }
35}
36
37#[cfg(not(feature = "defmt"))]
38pub trait L2capSignal: WriteHci + FixedSizeValue + core::fmt::Debug {
39    fn channel() -> u16 {
40        L2CAP_CID_LE_U_SIGNAL
41    }
42    fn code() -> L2capSignalCode;
43}
44
45#[cfg(feature = "defmt")]
46pub trait L2capSignal: WriteHci + FixedSizeValue + defmt::Format {
47    fn channel() -> u16 {
48        L2CAP_CID_LE_U_SIGNAL
49    }
50    fn code() -> L2capSignalCode;
51}
52
53#[derive(Clone, Copy, PartialEq)]
54#[repr(transparent)]
55pub struct L2capSignalCode(pub u8);
56
57unsafe impl FixedSizeValue for L2capSignalCode {
58    fn is_valid(_data: &[u8]) -> bool {
59        true
60    }
61}
62
63impl core::fmt::Debug for L2capSignalCode {
64    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65        match *self {
66            Self::COMMAND_REJECT_RES => write!(f, "CommandRejectRes"),
67            Self::CONNECTION_REQ => write!(f, "ConnectionReq"),
68            Self::CONNECTION_RES => write!(f, "ConnectionRes"),
69            Self::CONFIGURATION_REQ => write!(f, "ConfigurationReq"),
70            Self::CONFIGURATION_RES => write!(f, "ConfigurationRes"),
71            Self::DISCONNECTION_REQ => write!(f, "DisconnectionReq"),
72            Self::DISCONNECTION_RES => write!(f, "DisconnectionRes"),
73            Self::ECHO_REQ => write!(f, "EchoReq"),
74            Self::ECHO_RES => write!(f, "EchoRes"),
75            Self::INFORMATION_REQ => write!(f, "InformationReq"),
76            Self::INFORMATION_RES => write!(f, "InformationRes"),
77            Self::CONN_PARAM_UPDATE_REQ => write!(f, "ConnParamUpdateReq"),
78            Self::CONN_PARAM_UPDATE_RES => write!(f, "ConnParamUpdateRes"),
79            Self::LE_CREDIT_CONN_REQ => write!(f, "LeCreditConnReq"),
80            Self::LE_CREDIT_CONN_RES => write!(f, "LeCreditConnRes"),
81            Self::LE_CREDIT_FLOW_IND => write!(f, "LeCreditFlowInd"),
82            Self::CREDIT_CONN_REQ => write!(f, "CreditConnReq"),
83            Self::CREDIT_CONN_RES => write!(f, "CreditConnRes"),
84            Self::CREDIT_CONN_RECONFIG_REQ => write!(f, "CreditConnReconfigReq"),
85            Self::CREDIT_CONN_RECONFIG_RES => write!(f, "CreditConnReconfigRes"),
86            _ => write!(f, "Unknown(0x{:02x})", self.0),
87        }
88    }
89}
90
91#[cfg(feature = "defmt")]
92impl defmt::Format for L2capSignalCode {
93    fn format(&self, f: defmt::Formatter<'_>) {
94        match *self {
95            Self::COMMAND_REJECT_RES => defmt::write!(f, "CommandRejectRes"),
96            Self::CONNECTION_REQ => defmt::write!(f, "ConnectionReq"),
97            Self::CONNECTION_RES => defmt::write!(f, "ConnectionRes"),
98            Self::CONFIGURATION_REQ => defmt::write!(f, "ConfigurationReq"),
99            Self::CONFIGURATION_RES => defmt::write!(f, "ConfigurationRes"),
100            Self::DISCONNECTION_REQ => defmt::write!(f, "DisconnectionReq"),
101            Self::DISCONNECTION_RES => defmt::write!(f, "DisconnectionRes"),
102            Self::ECHO_REQ => defmt::write!(f, "EchoReq"),
103            Self::ECHO_RES => defmt::write!(f, "EchoRes"),
104            Self::INFORMATION_REQ => defmt::write!(f, "InformationReq"),
105            Self::INFORMATION_RES => defmt::write!(f, "InformationRes"),
106            Self::CONN_PARAM_UPDATE_REQ => defmt::write!(f, "ConnParamUpdateReq"),
107            Self::CONN_PARAM_UPDATE_RES => defmt::write!(f, "ConnParamUpdateRes"),
108            Self::LE_CREDIT_CONN_REQ => defmt::write!(f, "LeCreditConnReq"),
109            Self::LE_CREDIT_CONN_RES => defmt::write!(f, "LeCreditConnRes"),
110            Self::LE_CREDIT_FLOW_IND => defmt::write!(f, "LeCreditFlowInd"),
111            Self::CREDIT_CONN_REQ => defmt::write!(f, "CreditConnReq"),
112            Self::CREDIT_CONN_RES => defmt::write!(f, "CreditConnRes"),
113            Self::CREDIT_CONN_RECONFIG_REQ => defmt::write!(f, "CreditConnReconfigReq"),
114            Self::CREDIT_CONN_RECONFIG_RES => defmt::write!(f, "CreditConnReconfigRes"),
115            _ => defmt::write!(f, "Unknown(0x{:02x})", self.0),
116        }
117    }
118}
119
120impl From<u8> for L2capSignalCode {
121    fn from(val: u8) -> Self {
122        Self(val)
123    }
124}
125
126#[allow(non_upper_case_globals)]
127impl L2capSignalCode {
128    pub const COMMAND_REJECT_RES: Self = Self(0x01);
129    pub const CONNECTION_REQ: Self = Self(0x02);
130    pub const CONNECTION_RES: Self = Self(0x03);
131    pub const CONFIGURATION_REQ: Self = Self(0x04);
132    pub const CONFIGURATION_RES: Self = Self(0x05);
133    pub const DISCONNECTION_REQ: Self = Self(0x06);
134    pub const DISCONNECTION_RES: Self = Self(0x07);
135    pub const ECHO_REQ: Self = Self(0x08);
136    pub const ECHO_RES: Self = Self(0x09);
137    pub const INFORMATION_REQ: Self = Self(0x0A);
138    pub const INFORMATION_RES: Self = Self(0x0B);
139    pub const CONN_PARAM_UPDATE_REQ: Self = Self(0x12);
140    pub const CONN_PARAM_UPDATE_RES: Self = Self(0x13);
141    pub const LE_CREDIT_CONN_REQ: Self = Self(0x14);
142    pub const LE_CREDIT_CONN_RES: Self = Self(0x15);
143    pub const LE_CREDIT_FLOW_IND: Self = Self(0x16);
144    pub const CREDIT_CONN_REQ: Self = Self(0x17);
145    pub const CREDIT_CONN_RES: Self = Self(0x18);
146    pub const CREDIT_CONN_RECONFIG_REQ: Self = Self(0x19);
147    pub const CREDIT_CONN_RECONFIG_RES: Self = Self(0x1A);
148}
149
150#[cfg_attr(feature = "defmt", derive(defmt::Format))]
151#[repr(C)]
152#[derive(Debug, Clone, Copy)]
153pub struct LeCreditConnReq {
154    pub spsm: u16,
155    pub scid: u16,
156    pub mtu: u16,
157    pub mps: u16,
158    pub credits: u16,
159}
160
161unsafe impl FixedSizeValue for LeCreditConnReq {
162    fn is_valid(data: &[u8]) -> bool {
163        true
164    }
165}
166
167impl L2capSignal for LeCreditConnReq {
168    fn code() -> L2capSignalCode {
169        L2capSignalCode::LE_CREDIT_CONN_REQ
170    }
171}
172
173#[cfg_attr(feature = "defmt", derive(defmt::Format))]
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175#[repr(u16)]
176/// Result code for an LE Credit Based Connection Response.
177pub enum LeCreditConnResultCode {
178    /// Connection successful.
179    Success = 0x0000,
180    /// Connection refused — LE_PSM not supported.
181    SpsmNotSupported = 0x0002,
182    /// Connection refused — no resources available.
183    NoResources = 0x0004,
184    /// Connection refused — insufficient authentication.
185    InsufficientAuthentication = 0x0005,
186    /// Connection refused — insufficient authorization.
187    InsufficientAuthorization = 0x0006,
188    /// Connection refused — encryption key size too short.
189    EncryptionKeyTooShort = 0x0007,
190    /// Connection refused — insufficient encryption.
191    InsufficientEncryption = 0x0008,
192    /// Connection refused — invalid Source CID.
193    InvalidSourceId = 0x0009,
194    /// Connection refused — Source CID already allocated.
195    ScidAlreadyAllocated = 0x000A,
196    /// Connection refused — unacceptable parameters.
197    UnacceptableParameters = 0x000B,
198}
199
200#[cfg_attr(feature = "defmt", derive(defmt::Format))]
201#[repr(C)]
202#[derive(Debug, Clone, Copy)]
203pub struct LeCreditConnRes {
204    pub dcid: u16,
205    pub mtu: u16,
206    pub mps: u16,
207    pub credits: u16,
208    pub result: LeCreditConnResultCode,
209}
210
211impl LeCreditConnRes {
212    pub const fn reject(result: LeCreditConnResultCode) -> Self {
213        Self {
214            dcid: 0,
215            mtu: 0,
216            mps: 0,
217            credits: 0,
218            result,
219        }
220    }
221}
222
223impl L2capSignal for LeCreditConnRes {
224    fn code() -> L2capSignalCode {
225        L2capSignalCode::LE_CREDIT_CONN_RES
226    }
227}
228
229unsafe impl FixedSizeValue for LeCreditConnRes {
230    fn is_valid(data: &[u8]) -> bool {
231        true
232    }
233}
234
235#[repr(C)]
236#[derive(Debug, Clone, Copy)]
237#[cfg_attr(feature = "defmt", derive(defmt::Format))]
238pub struct LeCreditFlowInd {
239    pub cid: u16,
240    pub credits: u16,
241}
242
243unsafe impl FixedSizeValue for LeCreditFlowInd {
244    fn is_valid(data: &[u8]) -> bool {
245        true
246    }
247}
248
249impl L2capSignal for LeCreditFlowInd {
250    fn code() -> L2capSignalCode {
251        L2capSignalCode::LE_CREDIT_FLOW_IND
252    }
253}
254
255#[cfg_attr(feature = "defmt", derive(defmt::Format))]
256#[repr(C)]
257#[derive(Debug, Clone, Copy)]
258pub struct CommandRejectRes {
259    pub reason: u16,
260    // TODO: Optional fields pub data: u16,
261}
262
263unsafe impl FixedSizeValue for CommandRejectRes {
264    fn is_valid(data: &[u8]) -> bool {
265        true
266    }
267}
268
269impl L2capSignal for CommandRejectRes {
270    fn code() -> L2capSignalCode {
271        L2capSignalCode::COMMAND_REJECT_RES
272    }
273}
274
275#[cfg_attr(feature = "defmt", derive(defmt::Format))]
276#[repr(C)]
277#[derive(Debug, Clone, Copy)]
278pub struct DisconnectionReq {
279    pub dcid: u16,
280    pub scid: u16,
281}
282
283unsafe impl FixedSizeValue for DisconnectionReq {
284    fn is_valid(data: &[u8]) -> bool {
285        true
286    }
287}
288
289impl L2capSignal for DisconnectionReq {
290    fn code() -> L2capSignalCode {
291        L2capSignalCode::DISCONNECTION_REQ
292    }
293}
294
295#[cfg_attr(feature = "defmt", derive(defmt::Format))]
296#[repr(C)]
297#[derive(Debug, Clone, Copy)]
298pub struct DisconnectionRes {
299    pub dcid: u16,
300    pub scid: u16,
301}
302
303unsafe impl FixedSizeValue for DisconnectionRes {
304    fn is_valid(data: &[u8]) -> bool {
305        true
306    }
307}
308
309impl L2capSignal for DisconnectionRes {
310    fn code() -> L2capSignalCode {
311        L2capSignalCode::DISCONNECTION_RES
312    }
313}
314
315#[cfg_attr(feature = "defmt", derive(defmt::Format))]
316#[repr(C)]
317#[derive(Debug, Clone, Copy)]
318pub struct ConnParamUpdateReq {
319    pub interval_min: u16,
320    pub interval_max: u16,
321    pub latency: u16,
322    pub timeout: u16,
323}
324
325unsafe impl FixedSizeValue for ConnParamUpdateReq {
326    fn is_valid(data: &[u8]) -> bool {
327        true
328    }
329}
330
331impl L2capSignal for ConnParamUpdateReq {
332    fn code() -> L2capSignalCode {
333        L2capSignalCode::CONN_PARAM_UPDATE_REQ
334    }
335}
336
337#[cfg_attr(feature = "defmt", derive(defmt::Format))]
338#[repr(C)]
339#[derive(Debug, Clone, Copy)]
340pub struct ConnParamUpdateRes {
341    pub result: u16,
342}
343
344unsafe impl FixedSizeValue for ConnParamUpdateRes {
345    fn is_valid(data: &[u8]) -> bool {
346        true
347    }
348}
349
350impl L2capSignal for ConnParamUpdateRes {
351    fn code() -> L2capSignalCode {
352        L2capSignalCode::CONN_PARAM_UPDATE_RES
353    }
354}