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 = 64;
22
23#[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 SetAdvertisingParams = 0x0006,
52 SetAdvertisingData = 0x0008,
53 SetScanResponseData = 0x0009,
54 SetAdvertisingEnable = 0x000a,
55 SetScanParams = 0x000b,
56 SetScanEnable = 0x000c,
57 CreateConnection = 0x000d,
58 CreateConnectionCancel = 0x000e,
59 ReadFilterAcceptListSize = 0x000f,
60 ClearFilterAcceptList = 0x0010,
61 AddDeviceToFilterAcceptList = 0x0011,
62 RemoveDeviceFromFilterAcceptList = 0x0012,
63 PeriodicAdvertisingCreateSync = 0x0044,
64 PeriodicAdvertisingCreateSyncCancel = 0x0045,
65 AddDeviceToPeriodicAdvertiserList = 0x0047,
66 RemoveDeviceFromPeriodicAdvertiserList = 0x0048,
67 PeriodicAdvertisingReceiveEnable = 0x0059,
68 PeriodicAdvertisingSyncTransfer = 0x005a,
69}
70
71pub fn make_hci_opcode(ogf: HciOgf, ocf: HciOcf) -> u16 {
72 ((ogf as u16) << 10) | ocf as u16
73}
74
75#[derive(Format)]
76pub enum AdvData<'a> {
78 Flags(u8),
79 Incomplete16BitUuids(&'a [u8]),
80 Complete16BitUuids(&'a [u8]), Incomplete32BitUuids(&'a [u8]),
82 Complete32BitUuids(&'a [u8]),
83 Incomplete128BitUuids(&'a [u8]),
84 Complete128BitUuids(&'a [u8]),
85 ShortenedLocalName(&'a str),
86 CompleteLocalName(&'a str),
87 ClassOfDevice(&'a [u8]), DeviceId(&'a [u8]), ServiceData16Bit(&'a [u8]),
90 Manufacturer { company: u16, data: &'a [u8] },
91 Other { typ: u8, data: &'a [u8] },
92}
93
94pub struct AdvReport<'a> {
98 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>,
104}
105
106impl<'a> Format for AdvReport<'a> {
108 fn format(&self, f: Formatter) {
109 defmt::write!(
111 f,
112 "AdvReport {{ evt_type: {}, addr_type: {}, addr: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}, \
113 rssi: {} dBm, data_len: {}",
114 self.evt_type,
115 self.addr_type,
116 self.addr[5],
118 self.addr[4],
119 self.addr[3],
120 self.addr[2],
121 self.addr[1],
122 self.addr[0],
123 self.rssi,
124 self.data.len(),
125 );
126
127 defmt::write!(f, ", data_parsed: \n[");
129
130 let mut first = true;
132 for ad in &self.data_parsed {
133 if !first {
134 defmt::write!(f, ", ");
135 }
136 first = false;
137 defmt::write!(f, "{}", ad); }
139
140 defmt::write!(f, "] }}");
142 }
143}
144
145#[derive(Clone, Copy, Format, TryFromPrimitive, Default)]
146#[repr(u8)]
147pub enum BleScanType {
148 Passive = 0,
149 #[default]
150 Active = 1,
151}
152
153#[derive(Clone, Copy, Format)]
154#[repr(u8)]
155pub enum BleOwnAddrType {
156 Public = 0,
157 Private = 1,
158}
159
160#[derive(Clone, Copy, Format)]
161#[repr(u8)]
162pub enum FilterPolicy {
163 AcceptAll = 0,
164 WhitelistOnly = 1,
165}
166
167pub struct BleScanParams {
168 pub scan_type: BleScanType,
169 pub interval: u16, pub window: u16, pub own_address_type: BleOwnAddrType,
173 pub filter_policy: FilterPolicy,
174}
175
176impl BleScanParams {
177 pub fn to_bytes(&self) -> [u8; 7] {
178 let mut result = [0; 7];
179
180 let interval = ((self.interval as f32) / 0.625).round() as u16;
182 let window = ((self.window as f32) / 0.625).round() as u16;
183
184 result[0] = self.scan_type as u8;
185 result[1..3].copy_from_slice(&interval.to_le_bytes());
186 result[3..5].copy_from_slice(&window.to_le_bytes());
187 result[5] = self.own_address_type as u8;
188 result[6] = self.filter_policy as u8;
189
190 result
191 }
192}
193
194pub fn make_hci_cmd(opcode: u16, params: &[u8]) -> ([u8; HCI_TX_MAX_LEN], usize) {
197 let mut payload = [0; HCI_TX_MAX_LEN];
198
199 payload[0..2].copy_from_slice(&(opcode).to_le_bytes());
201 payload[2] = params.len() as u8;
202 payload[3..3 + params.len()].copy_from_slice(params);
203
204 (payload, HCI_HDR_SIZE + params.len())
207}
208
209pub fn parse_adv_data(mut d: &[u8]) -> Vec<AdvData<'_>, MAX_NUM_ADV_DATA> {
210 let mut result = Vec::<AdvData, MAX_NUM_ADV_DATA>::new();
211
212 while !d.is_empty() {
213 let len = d[0] as usize;
214 if len == 0 || len > d.len() - 1 {
215 break;
216 }
217
218 let ad_type = d[1];
219 let val = &d[2..1 + len];
220
221 match ad_type {
223 0x01 if val.len() == 1 => {
224 let _ = result.push(AdvData::Flags(val[0]));
225 }
226 0x02 if val.len() == 1 => {
227 let _ = result.push(AdvData::Incomplete16BitUuids(val));
228 }
229 0x03 => {
230 let _ = result.push(AdvData::Complete16BitUuids(val));
231 }
232 0x04 => {
233 let _ = result.push(AdvData::Incomplete32BitUuids(val));
234 }
235 0x05 => {
236 let _ = result.push(AdvData::Complete32BitUuids(val));
237 }
238 0x06 => {
239 let _ = result.push(AdvData::Incomplete128BitUuids(val));
240 }
241 0x07 => {
242 let _ = result.push(AdvData::Complete128BitUuids(val));
243 }
244 0x08 => {
245 if let Ok(s) = core::str::from_utf8(val) {
246 let _ = result.push(AdvData::ShortenedLocalName(s));
247 }
248 }
249 0x09 => {
250 if let Ok(s) = core::str::from_utf8(val) {
251 let _ = result.push(AdvData::CompleteLocalName(s));
252 }
253 }
254 0x16 => {
255 let _ = result.push(AdvData::ServiceData16Bit(val));
256 }
257 0x0d => {
258 let _ = result.push(AdvData::ClassOfDevice(val));
259 }
260 0x10 => {
261 let _ = result.push(AdvData::DeviceId(val));
262 }
263 0xFF if val.len() >= 2 => {
264 let company = u16::from_le_bytes([val[0], val[1]]);
265 let _ = result.push(AdvData::Manufacturer {
266 company,
267 data: &val[2..],
268 });
269 }
270 _ => {
271 let _ = result.push(AdvData::Other {
272 typ: ad_type,
273 data: val,
274 });
275 }
276 }
277
278 d = &d[1 + len..];
279 }
280
281 result
284}
285
286pub enum HciEvent<'a> {
288 CommandComplete {
289 n_cmd: u8, opcode: u16,
291 status: u8,
292 rest: &'a [u8],
293 },
294 AdvertisingReport {
295 reports: Vec<AdvReport<'a>, MAX_NUM_ADV_REPS>, },
297 Unknown {
298 evt: u8,
299 params: &'a [u8],
300 },
301}
302
303impl<'a> Format for HciEvent<'a> {
305 fn format(&self, fmt: Formatter) {
306 match self {
307 HciEvent::CommandComplete {
308 n_cmd,
309 opcode,
310 status,
311 rest,
312 } => {
313 defmt::write!(
314 fmt,
315 "CommandComplete {{ n_cmd: {}, opcode: {}, status: {}, rest: {=[u8]} }}",
316 *n_cmd,
317 *opcode,
318 *status,
319 rest
320 );
321 }
322 HciEvent::AdvertisingReport { reports } => {
323 defmt::write!(fmt, "Advertising reports:");
325 for rep in reports {
326 defmt::write!(fmt, "\n-{}; ", rep);
327 }
328 }
329 HciEvent::Unknown { evt, params } => {
330 defmt::write!(fmt, "Unknown {{ evt: {}, params: {=[u8]} }}", *evt, params);
331 }
332 }
333 }
334}
335
336#[derive(Clone, Copy, PartialEq, Format, TryFromPrimitive)]
337#[repr(u8)]
338pub enum HciEventType {
339 InquiryComplete = 0x01,
340 InquiryResult = 0x02,
341 ConnectionComplete = 0x03,
342 ConnectionRequest = 0x04,
343 CommandComplete = 0x0E,
344 LeAdvertising = 0x3E,
345 }
347
348pub fn parse_hci_events(buf: &[u8]) -> Result<Vec<HciEvent, MAX_HCI_EVS>, EspError> {
349 let mut result = Vec::<HciEvent, MAX_HCI_EVS>::new();
350
351 let mut i = 0;
352
353 while i + HCI_HDR_SIZE <= buf.len() {
354 if buf[i] != HciPkt::Evt as u8 {
356 return Ok(result);
362 }
363
364 let evt_type: HciEventType = match buf[i + 1].try_into() {
366 Ok(evt) => evt,
367 Err(e) => {
368 println!("Error parsing HCI event: {:?}", buf[i + 1]); return Err(EspError::InvalidData);
370 }
371 };
372
373 let packet_len = buf[i + 2] as usize;
374
375 if i + 3 + packet_len > buf.len() {
376 println!("Buf not long enough for HCI event");
377 return Err(EspError::InvalidData);
378 }
379
380 let params = &buf[i + 3..i + 3 + packet_len];
381
382 match evt_type {
383 HciEventType::CommandComplete => {
384 let n_cmd = params[0];
385 let opcode = u16::from_le_bytes([params[1], params[2]]);
386
387 let status = params[3];
388 result
389 .push(HciEvent::CommandComplete {
390 n_cmd,
391 opcode,
392 status,
393 rest: ¶ms[4..],
394 })
395 .ok();
396 }
397
398 HciEventType::LeAdvertising => {
400 if params[0] == 0x02 {
401 let num = params[1] as usize;
403 let mut idx = 2;
404 let mut reports = Vec::<AdvReport, MAX_NUM_ADV_REPS>::new();
405
406 for _ in 0..num {
407 if idx + 10 > params.len() {
410 break;
411 }
412
413 let evt_type = params[idx];
414 idx += 1;
415 let addr_type = params[idx];
416 idx += 1;
417
418 let mut addr = [0u8; 6];
419 addr.copy_from_slice(¶ms[idx..idx + 6]);
420 idx += 6;
421
422 let data_len = params[idx] as usize;
423 idx += 1;
424 if idx + data_len + 1 > params.len() {
425 break;
426 }
427
428 let data = ¶ms[idx..idx + data_len];
429 idx += data_len;
430
431 let rssi = params[idx] as i8;
432 idx += 1;
433
434 reports
435 .push(AdvReport {
436 evt_type,
437 addr_type,
438 addr,
439 data,
440 rssi,
441 data_parsed: parse_adv_data(data),
442 })
443 .ok();
444 }
445
446 result.push(HciEvent::AdvertisingReport { reports }).ok();
449 }
450 }
451
452 _ => {
453 println!("\n\nUnknown HCI evt type: {:?}", evt_type);
454
455 if result
456 .push(HciEvent::Unknown {
457 evt: evt_type as u8,
458 params,
459 })
460 .is_err()
461 {
462 return Err(EspError::Capacity);
463 }
464 }
465 }
466
467 i += HCI_HDR_SIZE + packet_len;
468 }
469
470 Ok(result)
473}
474
475#[inline]
477fn ms_to_0p625_units(ms: u16) -> u16 {
478 let v = (ms as u32) * 1600 + 500; (v / 1_000) as u16
481}
482
483pub fn le_set_adv_params_bytes(interval_ms: u16, adv_type: u8) -> [u8; 15] {
485 let units = ms_to_0p625_units(interval_ms);
486 let mut p = [0u8; 15];
487
488 p[0..2].copy_from_slice(&units.to_le_bytes());
490 p[2..4].copy_from_slice(&units.to_le_bytes());
491
492 p[4] = adv_type;
494
495 p[5] = 0x00;
497
498 p[6] = 0x00;
500
501 p[13] = 0x07;
506
507 p[14] = 0x00;
509
510 p
511}
512
513pub fn le_set_adv_data_manu(company_id: u16, manu_data: &[u8]) -> Result<[u8; 32], EspError> {
515 let used = 1 + 1 + 2 + manu_data.len();
519 if used > 31 {
520 return Err(EspError::Capacity);
521 }
522
523 let mut params = [0u8; 32];
524 params[0] = used as u8; let ad_len = (1 + 2 + manu_data.len()) as u8;
527 params[1] = ad_len; params[2] = 0xFF; params[3..5].copy_from_slice(&company_id.to_le_bytes());
530 params[5..5 + manu_data.len()].copy_from_slice(manu_data);
531 Ok(params)
534}
535
536pub fn le_set_scan_rsp_name(name: &str) -> Result<[u8; 32], EspError> {
537 let b = name.as_bytes();
538 let use_len = core::cmp::min(b.len(), 29); let typ = if b.len() <= 29 { 0x09 } else { 0x08 }; let mut p = [0u8; 32];
542 p[0] = (1 + 1 + use_len) as u8; p[1] = (1 + use_len) as u8; p[2] = typ; p[3..3 + use_len].copy_from_slice(&b[..use_len]);
546
547 Ok(p)
548}