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