1use super::{addr::BleAddr, io::BleStream};
4use crate::transport::TransportError;
5use std::collections::VecDeque;
6use thiserror::Error;
7use tokio::sync::Mutex;
8
9pub const BLE_V2_MAGIC: [u8; 2] = *b"FB";
10pub const BLE_V2_VERSION: u8 = 2;
11pub const BLE_V2_HEADER_LEN: usize = 6;
12
13#[derive(Debug, Clone, PartialEq, Eq, Error)]
14pub enum BleFrameError {
15 #[error("BLE frame payload must not be empty")]
16 EmptyPayload,
17 #[error("BLE frame payload {payload_len} exceeds configured maximum {max_payload_len}")]
18 Oversized {
19 payload_len: usize,
20 max_payload_len: usize,
21 },
22 #[error("invalid BLE frame magic")]
23 InvalidMagic,
24 #[error("unsupported BLE frame version {0}")]
25 UnsupportedVersion(u8),
26 #[error("unsupported BLE frame flags 0x{0:02x}")]
27 UnsupportedFlags(u8),
28}
29
30pub fn encode_frame(payload: &[u8], max_payload_len: usize) -> Result<Vec<u8>, BleFrameError> {
31 if payload.is_empty() {
32 return Err(BleFrameError::EmptyPayload);
33 }
34 let effective_max = max_payload_len.min(u16::MAX as usize);
35 if payload.len() > effective_max {
36 return Err(BleFrameError::Oversized {
37 payload_len: payload.len(),
38 max_payload_len: effective_max,
39 });
40 }
41
42 let payload_len = payload.len() as u16;
43 let mut encoded = Vec::with_capacity(BLE_V2_HEADER_LEN + payload.len());
44 encoded.extend_from_slice(&BLE_V2_MAGIC);
45 encoded.push(BLE_V2_VERSION);
46 encoded.push(0);
47 encoded.extend_from_slice(&payload_len.to_be_bytes());
48 encoded.extend_from_slice(payload);
49 Ok(encoded)
50}
51
52#[derive(Debug)]
53pub struct BleFrameDecoder {
54 max_payload_len: usize,
55 buffered: Vec<u8>,
56}
57
58impl BleFrameDecoder {
59 pub fn new(max_payload_len: usize) -> Self {
60 Self {
61 max_payload_len: max_payload_len.min(u16::MAX as usize),
62 buffered: Vec::new(),
63 }
64 }
65
66 pub fn push(&mut self, mut bytes: &[u8]) -> Result<Vec<Vec<u8>>, BleFrameError> {
67 let mut packets = Vec::new();
68 while !bytes.is_empty() {
69 if self.buffered.len() < BLE_V2_HEADER_LEN {
70 let needed = BLE_V2_HEADER_LEN - self.buffered.len();
71 let take = needed.min(bytes.len());
72 self.buffered.extend_from_slice(&bytes[..take]);
73 bytes = &bytes[take..];
74 if self.buffered.len() < BLE_V2_HEADER_LEN {
75 break;
76 }
77 }
78
79 let payload_len = match validate_header(&self.buffered, self.max_payload_len) {
80 Ok(payload_len) => payload_len,
81 Err(error) => {
82 self.buffered.clear();
83 return Err(error);
84 }
85 };
86 let frame_len = BLE_V2_HEADER_LEN + payload_len;
87 let needed = frame_len - self.buffered.len();
88 let take = needed.min(bytes.len());
89 self.buffered.extend_from_slice(&bytes[..take]);
90 bytes = &bytes[take..];
91
92 if self.buffered.len() == frame_len {
93 packets.push(self.buffered[BLE_V2_HEADER_LEN..].to_vec());
94 self.buffered.clear();
95 }
96 }
97 Ok(packets)
98 }
99
100 pub(crate) fn buffered_len(&self) -> usize {
101 self.buffered.len()
102 }
103}
104
105fn validate_header(header: &[u8], max_payload_len: usize) -> Result<usize, BleFrameError> {
106 debug_assert!(header.len() >= BLE_V2_HEADER_LEN);
107 if header[..2] != BLE_V2_MAGIC {
108 return Err(BleFrameError::InvalidMagic);
109 }
110 if header[2] != BLE_V2_VERSION {
111 return Err(BleFrameError::UnsupportedVersion(header[2]));
112 }
113 if header[3] != 0 {
114 return Err(BleFrameError::UnsupportedFlags(header[3]));
115 }
116 let payload_len = u16::from_be_bytes([header[4], header[5]]) as usize;
117 if payload_len == 0 {
118 return Err(BleFrameError::EmptyPayload);
119 }
120 if payload_len > max_payload_len {
121 return Err(BleFrameError::Oversized {
122 payload_len,
123 max_payload_len,
124 });
125 }
126 Ok(payload_len)
127}
128
129pub struct FramedBleStream<S> {
134 raw: S,
135 max_payload: u16,
136 send_lock: Mutex<()>,
137 receive: Mutex<ReceiveState>,
138}
139
140struct ReceiveState {
141 decoder: BleFrameDecoder,
142 pending: VecDeque<Vec<u8>>,
143 raw_buffer: Vec<u8>,
144}
145
146impl<S: BleStream> FramedBleStream<S> {
147 pub fn new(raw: S, max_payload: u16) -> Self {
148 let raw_buffer_len = usize::from(raw.recv_mtu()).max(BLE_V2_HEADER_LEN);
149 Self {
150 raw,
151 max_payload,
152 send_lock: Mutex::new(()),
153 receive: Mutex::new(ReceiveState {
154 decoder: BleFrameDecoder::new(max_payload.into()),
155 pending: VecDeque::new(),
156 raw_buffer: vec![0; raw_buffer_len],
157 }),
158 }
159 }
160
161 fn copy_packet(packet: &[u8], output: &mut [u8]) -> Result<usize, TransportError> {
162 if packet.len() > output.len() {
163 return Err(TransportError::RecvFailed(format!(
164 "BLE v2 frame {} exceeds receive buffer {}",
165 packet.len(),
166 output.len()
167 )));
168 }
169 output[..packet.len()].copy_from_slice(packet);
170 Ok(packet.len())
171 }
172}
173
174impl<S: BleStream> BleStream for FramedBleStream<S> {
175 async fn send(&self, data: &[u8]) -> Result<(), TransportError> {
176 let encoded = encode_frame(data, self.max_payload.into())
177 .map_err(|error| TransportError::SendFailed(error.to_string()))?;
178 let raw_mtu = usize::from(self.raw.send_mtu()).max(1);
179 let _guard = self.send_lock.lock().await;
180 for chunk in encoded.chunks(raw_mtu) {
181 self.raw.send(chunk).await?;
182 }
183 Ok(())
184 }
185
186 async fn recv(&self, output: &mut [u8]) -> Result<usize, TransportError> {
187 let mut state = self.receive.lock().await;
188 if let Some(packet) = state.pending.pop_front() {
189 return Self::copy_packet(&packet, output);
190 }
191
192 loop {
193 let mut raw_buffer = std::mem::take(&mut state.raw_buffer);
194 let received = self.raw.recv(&mut raw_buffer).await?;
195 if received == 0 {
196 let had_partial_frame = state.decoder.buffered_len() != 0;
197 state.raw_buffer = raw_buffer;
198 return if had_partial_frame {
199 Err(TransportError::RecvFailed(
200 "BLE v2 stream closed in the middle of a frame".into(),
201 ))
202 } else {
203 Ok(0)
204 };
205 }
206
207 let decoded = state
208 .decoder
209 .push(&raw_buffer[..received])
210 .map_err(|error| TransportError::RecvFailed(error.to_string()))?;
211 state.raw_buffer = raw_buffer;
212 state.pending.extend(decoded);
213 if let Some(packet) = state.pending.pop_front() {
214 return Self::copy_packet(&packet, output);
215 }
216 }
217 }
218
219 fn send_mtu(&self) -> u16 {
220 self.max_payload
221 }
222
223 fn recv_mtu(&self) -> u16 {
224 self.max_payload
225 }
226
227 fn remote_addr(&self) -> &BleAddr {
228 self.raw.remote_addr()
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use crate::transport::ble::{addr::BleAddr, io::MockBleStream};
236
237 const MAX_PAYLOAD: usize = 2048;
238
239 #[test]
240 fn encodes_v2_header_and_payload() {
241 let encoded = encode_frame(&[1, 2, 3], MAX_PAYLOAD).unwrap();
242 assert_eq!(
243 encoded,
244 [
245 BLE_V2_MAGIC.as_slice(),
246 &[BLE_V2_VERSION, 0, 0, 3],
247 &[1, 2, 3]
248 ]
249 .concat()
250 );
251 }
252
253 #[test]
254 fn decodes_a_frame_delivered_one_byte_at_a_time() {
255 let encoded = encode_frame(b"fragmented", MAX_PAYLOAD).unwrap();
256 let mut decoder = BleFrameDecoder::new(MAX_PAYLOAD);
257 let mut packets = Vec::new();
258 for byte in encoded {
259 packets.extend(decoder.push(&[byte]).unwrap());
260 }
261 assert_eq!(packets, vec![b"fragmented".to_vec()]);
262 assert_eq!(decoder.buffered_len(), 0);
263 }
264
265 #[test]
266 fn decodes_multiple_coalesced_frames() {
267 let bytes = [
268 encode_frame(b"one", MAX_PAYLOAD).unwrap(),
269 encode_frame(b"two", MAX_PAYLOAD).unwrap(),
270 ]
271 .concat();
272 let mut decoder = BleFrameDecoder::new(MAX_PAYLOAD);
273 assert_eq!(
274 decoder.push(&bytes).unwrap(),
275 vec![b"one".to_vec(), b"two".to_vec()]
276 );
277 assert_eq!(decoder.buffered_len(), 0);
278 }
279
280 #[test]
281 fn rejects_oversized_length_before_payload_arrives() {
282 let mut decoder = BleFrameDecoder::new(32);
283 let header = [BLE_V2_MAGIC[0], BLE_V2_MAGIC[1], BLE_V2_VERSION, 0, 0, 33];
284 assert_eq!(
285 decoder.push(&header),
286 Err(BleFrameError::Oversized {
287 payload_len: 33,
288 max_payload_len: 32,
289 })
290 );
291 assert!(decoder.buffered_len() <= BLE_V2_HEADER_LEN);
292 }
293
294 #[test]
295 fn rejects_empty_payloads() {
296 assert_eq!(
297 encode_frame(&[], MAX_PAYLOAD),
298 Err(BleFrameError::EmptyPayload)
299 );
300 }
301
302 #[test]
303 fn rejects_wrong_magic_version_and_flags() {
304 let mut decoder = BleFrameDecoder::new(MAX_PAYLOAD);
305 assert_eq!(
306 decoder.push(&[b'X', b'B', BLE_V2_VERSION, 0, 0, 1]),
307 Err(BleFrameError::InvalidMagic)
308 );
309
310 let mut decoder = BleFrameDecoder::new(MAX_PAYLOAD);
311 assert_eq!(
312 decoder.push(&[b'F', b'B', 3, 0, 0, 1]),
313 Err(BleFrameError::UnsupportedVersion(3))
314 );
315
316 let mut decoder = BleFrameDecoder::new(MAX_PAYLOAD);
317 assert_eq!(
318 decoder.push(&[b'F', b'B', BLE_V2_VERSION, 1, 0, 1]),
319 Err(BleFrameError::UnsupportedFlags(1))
320 );
321 }
322
323 #[tokio::test]
324 async fn framed_stream_segments_and_reassembles_packets() {
325 let left = BleAddr::from_mac("hci0", [0, 0, 0, 0, 0, 1]);
326 let right = BleAddr::from_mac("hci0", [0, 0, 0, 0, 0, 2]);
327 let (a, b) = MockBleStream::pair(left, right, 4);
328 let a = FramedBleStream::new(a, 128);
329 let b = FramedBleStream::new(b, 128);
330
331 a.send(b"one packet split across raw writes").await.unwrap();
332 let mut output = [0u8; 128];
333 let received = b.recv(&mut output).await.unwrap();
334 assert_eq!(&output[..received], b"one packet split across raw writes");
335 }
336
337 #[tokio::test]
338 async fn framed_stream_preserves_consecutive_packet_boundaries() {
339 let left = BleAddr::from_mac("hci0", [0, 0, 0, 0, 0, 1]);
340 let right = BleAddr::from_mac("hci0", [0, 0, 0, 0, 0, 2]);
341 let (a, b) = MockBleStream::pair(left, right, 64);
342 let a = FramedBleStream::new(a, 128);
343 let b = FramedBleStream::new(b, 128);
344
345 a.send(b"one").await.unwrap();
346 a.send(b"two").await.unwrap();
347
348 let mut output = [0u8; 128];
349 let first = b.recv(&mut output).await.unwrap();
350 assert_eq!(&output[..first], b"one");
351 let second = b.recv(&mut output).await.unwrap();
352 assert_eq!(&output[..second], b"two");
353 }
354}