1use defmt::{Format, Formatter, println};
6use heapless::Vec;
7use num_enum::TryFromPrimitive;
8use num_traits::float::FloatCore;
9
10use crate::EspError;
11
12pub const MAX_HCI_EVS: usize = 2; const MAX_NUM_ADV_DATA: usize = 5; pub const MAX_NUM_ADV_REPS: usize = 3; const HCI_HDR_SIZE: usize = 3;
20
21const HCI_TX_MAX_LEN: usize = 30; #[derive(Clone, Copy, PartialEq, TryFromPrimitive, Format)]
24#[repr(u8)]
25pub enum HciPkt {
26 Cmd = 0x01,
27 Acl = 0x02,
28 Sco = 0x03,
29 Evt = 0x04,
30}
31
32#[derive(Clone, Copy, PartialEq, Format, TryFromPrimitive)]
34#[repr(u8)]
35pub enum HciOgf {
36 NoOperation = 0x00,
37 LinkControl = 0x01,
38 LinkPolicy = 0x02,
39 ControllerAndBaseboard = 0x03,
40 InformationParams = 0x04,
41 StatusParams = 0x05,
42 TestingCmds = 0x06,
43 LeController = 0x08,
44 VendorSPecificCmds = 0x3f,
45}
46
47#[derive(Clone, Copy, PartialEq, Format, TryFromPrimitive)]
49#[repr(u16)]
50pub enum HciOcf {
51 SetAdvertisingData = 0x0008,
52 SetScanResponseData = 0x0009,
53 SetAdvertisingEnable = 0x000a,
54 SetScanParams = 0x000b,
55 SetScanEnable = 0x000c,
56 CreateConnection = 0x000d,
57 CreateConnectionCancel = 0x000e,
58 ReadFilterAcceptListSize = 0x000f,
59 ClearFilterAcceptList = 0x0010,
60 AddDeviceToFilterAcceptList = 0x0011,
61 RemoveDeviceFromFilterAcceptList = 0x0012,
62 PeriodicAdvertisingCreateSync = 0x0044,
63 PeriodicAdvertisingCreateSyncCancel = 0x0045,
64 AddDeviceToPeriodicAdvertiserList = 0x0047,
65 RemoveDeviceFromPeriodicAdvertiserList = 0x00487,
66 PeriodicAdvertisingReceiveEnable = 0x0059,
67 PeriodicAdvertisingSyncTransfer = 0x005a,
68}
69
70pub fn make_hci_opcode(ogf: HciOgf, ocf: HciOcf) -> u16 {
71 ((ogf as u16) << 10) | ocf as u16
72}
73
74#[derive(Format)]
75pub enum AdvData<'a> {
77 Flags(u8),
78 Incomplete16BitUuids(&'a [u8]),
79 Complete16BitUuids(&'a [u8]), Incomplete32BitUuids(&'a [u8]),
81 Complete32BitUuids(&'a [u8]),
82 Incomplete128BitUuids(&'a [u8]),
83 Complete128BitUuids(&'a [u8]),
84 ShortenedLocalName(&'a str),
85 CompleteLocalName(&'a str),
86 ClassOfDevice(&'a [u8]), DeviceId(&'a [u8]), ServiceData16Bit(&'a [u8]),
89 Manufacturer { company: u16, data: &'a [u8] },
90 Other { typ: u8, data: &'a [u8] },
91}
92
93pub struct AdvReport<'a> {
97 pub evt_type: u8, pub addr_type: u8, pub addr: [u8; 6], pub data: &'a [u8], pub rssi: i8, pub data_parsed: Vec<AdvData<'a>, MAX_NUM_ADV_DATA>,
103}
104
105impl<'a> Format for AdvReport<'a> {
107 fn format(&self, f: Formatter) {
108 defmt::write!(
110 f,
111 "AdvReport {{ evt_type: {}, addr_type: {}, addr: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}, \
112 rssi: {} dBm, data_len: {}",
113 self.evt_type,
114 self.addr_type,
115 self.addr[5],
117 self.addr[4],
118 self.addr[3],
119 self.addr[2],
120 self.addr[1],
121 self.addr[0],
122 self.rssi,
123 self.data.len(),
124 );
125
126 defmt::write!(f, ", data_parsed: \n[");
128
129 let mut first = true;
131 for ad in &self.data_parsed {
132 if !first {
133 defmt::write!(f, ", ");
134 }
135 first = false;
136 defmt::write!(f, "{}", ad); }
138
139 defmt::write!(f, "] }}");
141 }
142}
143
144#[derive(Clone, Copy, Format, TryFromPrimitive, Default)]
145#[repr(u8)]
146pub enum BleScanType {
147 Passive = 0,
148 #[default]
149 Active = 1,
150}
151
152#[derive(Clone, Copy, Format)]
153#[repr(u8)]
154pub enum BleOwnAddrType {
155 Public = 0,
156 Private = 1,
157}
158
159#[derive(Clone, Copy, Format)]
160#[repr(u8)]
161pub enum FilterPolicy {
162 AcceptAll = 0,
163 WhitelistOnly = 1,
164}
165
166pub struct BleScanParams {
167 pub scan_type: BleScanType,
168 pub interval: u16, pub window: u16, pub own_address_type: BleOwnAddrType,
172 pub filter_policy: FilterPolicy,
173}
174
175impl BleScanParams {
176 pub fn to_bytes(&self) -> [u8; 7] {
177 let mut result = [0; 7];
178
179 let interval = ((self.interval as f32) / 0.625).round() as u16;
181 let window = ((self.window as f32) / 0.625).round() as u16;
182
183 result[0] = self.scan_type as u8;
184 result[1..3].copy_from_slice(&interval.to_le_bytes());
185 result[3..5].copy_from_slice(&window.to_le_bytes());
186 result[5] = self.own_address_type as u8;
187 result[6] = self.filter_policy as u8;
188
189 result
190 }
191}
192
193pub fn make_hci_cmd(opcode: u16, params: &[u8]) -> ([u8; HCI_TX_MAX_LEN], usize) {
196 let mut payload = [0; HCI_TX_MAX_LEN];
197
198 payload[0..2].copy_from_slice(&(opcode).to_le_bytes());
200 payload[2] = params.len() as u8;
201 payload[3..3 + params.len()].copy_from_slice(params);
202
203 (payload, HCI_HDR_SIZE + params.len())
206}
207
208pub fn parse_adv_data(mut d: &[u8]) -> Vec<AdvData<'_>, MAX_NUM_ADV_DATA> {
209 let mut result = Vec::<AdvData, MAX_NUM_ADV_DATA>::new();
210
211 while !d.is_empty() {
212 let len = d[0] as usize;
213 if len == 0 || len > d.len() - 1 {
214 break;
215 }
216
217 let ad_type = d[1];
218 let val = &d[2..1 + len];
219
220 match ad_type {
222 0x01 if val.len() == 1 => {
223 let _ = result.push(AdvData::Flags(val[0]));
224 }
225 0x02 if val.len() == 1 => {
226 let _ = result.push(AdvData::Incomplete16BitUuids(val));
227 }
228 0x03 => {
229 let _ = result.push(AdvData::Complete16BitUuids(val));
230 }
231 0x04 => {
232 let _ = result.push(AdvData::Incomplete32BitUuids(val));
233 }
234 0x05 => {
235 let _ = result.push(AdvData::Complete32BitUuids(val));
236 }
237 0x06 => {
238 let _ = result.push(AdvData::Incomplete128BitUuids(val));
239 }
240 0x07 => {
241 let _ = result.push(AdvData::Complete128BitUuids(val));
242 }
243 0x08 => {
244 if let Ok(s) = core::str::from_utf8(val) {
245 let _ = result.push(AdvData::ShortenedLocalName(s));
246 }
247 }
248 0x09 => {
249 if let Ok(s) = core::str::from_utf8(val) {
250 let _ = result.push(AdvData::CompleteLocalName(s));
251 }
252 }
253 0x16 => {
254 let _ = result.push(AdvData::ServiceData16Bit(val));
255 }
256 0x0d => {
257 let _ = result.push(AdvData::ClassOfDevice(val));
258 }
259 0x10 => {
260 let _ = result.push(AdvData::DeviceId(val));
261 }
262 0xFF if val.len() >= 2 => {
263 let company = u16::from_le_bytes([val[0], val[1]]);
264 let _ = result.push(AdvData::Manufacturer {
265 company,
266 data: &val[2..],
267 });
268 }
269 _ => {
270 let _ = result.push(AdvData::Other {
271 typ: ad_type,
272 data: val,
273 });
274 }
275 }
276
277 d = &d[1 + len..];
278 }
279
280 result
283}
284
285pub enum HciEvent<'a> {
287 CommandComplete {
288 n_cmd: u8, opcode: u16,
290 status: u8,
291 rest: &'a [u8],
292 },
293 AdvertisingReport {
294 reports: Vec<AdvReport<'a>, MAX_NUM_ADV_REPS>, },
296 Unknown {
297 evt: u8,
298 params: &'a [u8],
299 },
300}
301
302impl<'a> Format for HciEvent<'a> {
304 fn format(&self, fmt: Formatter) {
305 match self {
306 HciEvent::CommandComplete {
307 n_cmd,
308 opcode,
309 status,
310 rest,
311 } => {
312 defmt::write!(
313 fmt,
314 "CommandComplete {{ n_cmd: {}, opcode: {}, status: {}, rest: {=[u8]} }}",
315 *n_cmd,
316 *opcode,
317 *status,
318 rest
319 );
320 }
321 HciEvent::AdvertisingReport { reports } => {
322 defmt::write!(fmt, "Advertising reports:");
324 for rep in reports {
325 defmt::write!(fmt, "\n-{}; ", rep);
326 }
327 }
328 HciEvent::Unknown { evt, params } => {
329 defmt::write!(fmt, "Unknown {{ evt: {}, params: {=[u8]} }}", *evt, params);
330 }
331 }
332 }
333}
334
335#[derive(Clone, Copy, PartialEq, Format, TryFromPrimitive)]
336#[repr(u8)]
337pub enum HciEventType {
338 InquiryComplete = 0x01,
339 InquiryResult = 0x02,
340 ConnectionComplete = 0x03,
341 ConnectionRequest = 0x04,
342 CommandComplete = 0x0E,
343 LeAdvertising = 0x3E,
344 }
346
347pub fn parse_hci_events(buf: &[u8]) -> Result<Vec<HciEvent, MAX_HCI_EVS>, EspError> {
348 let mut result = Vec::<HciEvent, MAX_HCI_EVS>::new();
349
350 let mut i = 0;
351
352 while i + HCI_HDR_SIZE <= buf.len() {
353 if buf[i] != HciPkt::Evt as u8 {
355 return Ok(result);
361 }
362
363 let evt_type: HciEventType = match buf[i + 1].try_into() {
365 Ok(evt) => evt,
366 Err(e) => {
367 println!("Error parsing HCI event: {:?}", buf[i + 1]); return Err(EspError::InvalidData);
369 }
370 };
371
372 let packet_len = buf[i + 2] as usize;
373
374 if i + 3 + packet_len > buf.len() {
375 println!("Buf not long enough for HCI event");
376 return Err(EspError::InvalidData);
377 }
378
379 let params = &buf[i + 3..i + 3 + packet_len];
380
381 match evt_type {
382 HciEventType::CommandComplete => {
383 let n_cmd = params[0];
384 let opcode = u16::from_le_bytes([params[1], params[2]]);
385
386 let status = params[3];
387 result
388 .push(HciEvent::CommandComplete {
389 n_cmd,
390 opcode,
391 status,
392 rest: ¶ms[4..],
393 })
394 .ok();
395 }
396
397 HciEventType::LeAdvertising => {
399 if params[0] == 0x02 {
400 let num = params[1] as usize;
402 let mut idx = 2;
403 let mut reports = Vec::<AdvReport, MAX_NUM_ADV_REPS>::new();
404
405 for _ in 0..num {
406 if idx + 10 > params.len() {
409 break;
410 }
411
412 let evt_type = params[idx];
413 idx += 1;
414 let addr_type = params[idx];
415 idx += 1;
416
417 let mut addr = [0u8; 6];
418 addr.copy_from_slice(¶ms[idx..idx + 6]);
419 idx += 6;
420
421 let data_len = params[idx] as usize;
422 idx += 1;
423 if idx + data_len + 1 > params.len() {
424 break;
425 }
426
427 let data = ¶ms[idx..idx + data_len];
428 idx += data_len;
429
430 let rssi = params[idx] as i8;
431 idx += 1;
432
433 reports
434 .push(AdvReport {
435 evt_type,
436 addr_type,
437 addr,
438 data,
439 rssi,
440 data_parsed: parse_adv_data(data),
441 })
442 .ok();
443 }
444
445 result.push(HciEvent::AdvertisingReport { reports }).ok();
448 }
449 }
450
451 _ => {
452 println!("\n\nUnknown HCI evt type: {:?}", evt_type);
453
454 if result
455 .push(HciEvent::Unknown {
456 evt: evt_type as u8,
457 params,
458 })
459 .is_err()
460 {
461 return Err(EspError::Capacity);
462 }
463 }
464 }
465
466 i += HCI_HDR_SIZE + packet_len;
467 }
468
469 Ok(result)
472}