1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! L2CAP over LE: the thin framing layer between raw ACL data and the LE
//! host protocols (ATT, SMP, and the L2CAP signaling channel).
//!
//! On an LE link, L2CAP runs only in *basic mode* on a set of fixed
//! channels — there's no channel-establishment handshake as on Classic
//! BR/EDR. Each L2CAP PDU is a "B-frame": a 4-byte basic header
//! (`length(2)` + `cid(2)`, both little-endian) followed by `length` bytes
//! of payload, where the CID names the protocol above ([`CID_ATT`],
//! [`CID_SMP`], [`CID_LE_SIGNALING`]).
//!
//! A single B-frame can be larger than one ACL data packet, so the
//! controller splits it: the first ACL fragment carries the basic header
//! and the start of the payload ([`super::AclData::first_fragment`] set),
//! and continuation fragments carry the rest. [`Reassembler`] rebuilds the
//! whole B-frame from those fragments; [`send`] does the reverse, prefixing
//! a payload with the basic header and handing it to
//! [`super::Bluetooth::send_acl`], which re-fragments it to the ACL packet
//! size on the way out.
//!
//! This layer does not interpret the payload beyond the basic header — a
//! reassembled [`Pdu`]'s bytes are handed up by CID to whichever protocol
//! owns that channel.
use ;
/// L2CAP CID of the Attribute Protocol (ATT) fixed channel — the transport
/// for GATT.
pub const CID_ATT: u16 = 0x0004;
/// L2CAP CID of the LE signaling fixed channel (L2CAP control commands,
/// e.g. Connection Parameter Update).
pub const CID_LE_SIGNALING: u16 = 0x0005;
/// L2CAP CID of the Security Manager Protocol (SMP) fixed channel — the
/// transport for LE pairing.
pub const CID_SMP: u16 = 0x0006;
/// Length of the L2CAP basic (B-frame) header: a 2-byte payload length and
/// a 2-byte channel identifier.
pub const HEADER_LEN: usize = 4;
/// Largest L2CAP payload this layer buffers, in bytes — comfortably above
/// any ATT MTU a peripheral like a keyboard negotiates. A B-frame whose
/// payload exceeds this is dropped on receive ([`Reassembler::feed`]) and
/// rejected on send ([`send`] returns [`Error::PayloadTooLarge`]).
pub const MAX_PAYLOAD: usize = 512;
/// A fully reassembled L2CAP B-frame, borrowing its payload from the
/// [`Reassembler`] that produced it. The `cid` selects the protocol the
/// `payload` belongs to.
/// Reassembles L2CAP B-frames from the ACL fragments delivered by
/// [`super::Bluetooth::poll`].
///
/// Feed every inbound [`AclData`] to [`Self::feed`]; it accumulates
/// fragments and, once a complete B-frame has arrived, returns it as a
/// [`Pdu`]. A first fragment ([`AclData::first_fragment`]) starts a new
/// frame — on LE each ACL "first" fragment begins exactly one B-frame — so
/// receiving one always resets any partial frame in progress.
///
/// Sized for a single connection's inbound stream: the buffer holds one
/// in-progress B-frame at a time, which is all a point-to-point peripheral
/// needs.
/// Sends an L2CAP B-frame: prefixes `payload` with the basic header
/// (`length` + `cid`) and hands the whole frame to
/// [`Bluetooth::send_acl`], which fragments it to the controller's ACL
/// packet size.
///
/// `payload` is the protocol PDU for the channel (an ATT PDU for
/// [`CID_ATT`], etc.); this builds the L2CAP header around it but does not
/// touch its contents. Returns [`Error::PayloadTooLarge`] if `payload`
/// exceeds [`MAX_PAYLOAD`], or a transport error from the underlying send.