1use alloc::vec::Vec;
2
3use byte::BytesExt;
4use embassy_futures::select::Either;
5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
6use embassy_sync::mutex::Mutex;
7use embassy_time::Timer;
8use esp_radio::ieee802154::Config;
9use esp_radio::ieee802154::Frame;
10use esp_radio::ieee802154::Ieee802154;
11use esp_radio::ieee802154::ReceivedFrame;
12use ieee802154::mac::Address;
13use ieee802154::mac::FrameContent;
14use ieee802154::mac::FrameType;
15use ieee802154::mac::FrameVersion;
16use ieee802154::mac::Header;
17use ieee802154::mac::PanId;
18use ieee802154::mac::command::CapabilityInformation;
19use ieee802154::mac::command::Command;
20use ieee802154::mac::security::SecurityContext;
21
22use crate::esp::driver::Ieee802154Driver;
23use crate::mlme::A_BASE_SUPER_FRAME_DURATION;
24use crate::mlme::A_MAX_FRAME_RETRIES;
25use crate::mlme::A_RESPONSE_WAIT_TIME;
26use crate::mlme::AssociationResponse;
27use crate::mlme::MAX_IEEE802154_CHANNELS;
28use crate::mlme::MacConfig;
29use crate::mlme::MacError;
30use crate::mlme::Mlme;
31use crate::mlme::PanDescriptor;
32use crate::mlme::PanDescriptorList;
33use crate::mlme::ScanResult;
34use crate::mlme::ScanType;
35
36mod driver;
37
38const ASSOCIATE_REQUEST_RETRIES: u8 = 3;
42
43const ASSOCIATE_POLL_RETRIES: u8 = 5;
45
46const POLL_DATA_RETRIES: u8 = 5;
48
49pub struct EspMlme<'a> {
56 inner: Mutex<CriticalSectionRawMutex, EspMlmeInner<'a>>,
57 ieee_address: u64,
58}
59
60impl<'a> EspMlme<'a> {
61 pub fn new(ieee802154: Ieee802154<'a>, config: Config) -> Self {
62 let inner = EspMlmeInner {
65 driver: Ieee802154Driver::new(ieee802154, config),
66 seq_number: (esp_hal::rng::Rng::new().random() & 0xff) as u8,
67 };
68 let ieee_address = inner.driver.ieee_address().0;
69 Self {
70 inner: Mutex::new(inner),
71 ieee_address,
72 }
73 }
74
75 pub fn ieee_address(&self) -> u64 {
77 self.ieee_address
78 }
79}
80
81struct EspMlmeInner<'a> {
82 driver: Ieee802154Driver<'a>,
83 seq_number: u8,
84}
85
86impl EspMlmeInner<'_> {
87 fn sequence_number(&mut self) -> u8 {
88 self.seq_number = self.seq_number.wrapping_add(1);
89 self.seq_number
90 }
91
92 async fn transmit_acked(&mut self, frame: &[u8]) -> Result<(), MacError> {
95 for _ in 0..=A_MAX_FRAME_RETRIES {
96 match self.driver.transmit(frame).await {
97 Ok(()) if self.driver.last_tx_acked() => return Ok(()),
98 Ok(()) => {}
99 Err(MacError::TxFailed) => {}
101 Err(MacError::TxTimeout) => log::warn!("[MLME] tx-done timeout, retrying"),
103 Err(e) => return Err(e),
104 }
105 }
106 Err(MacError::NoAck)
107 }
108
109 fn flush(&mut self) {
110 while self.driver.poll_received().is_some() {}
111 }
112
113 fn poll_frame(&mut self) -> Option<Result<ReceivedFrame, MacError>> {
114 self.driver
115 .poll_received()
116 .map(|r| r.map_err(MacError::RadioError))
117 }
118
119 async fn next_frame(&mut self) -> Result<ReceivedFrame, MacError> {
120 loop {
121 self.driver.wait_rx_available().await;
125 if let Some(result) = self.poll_frame() {
126 return result;
127 }
128 self.driver.reset_rx_signal();
129 }
130 }
131
132 fn try_drain(&mut self, buf: &mut [u8]) -> Result<Option<(usize, u8)>, MacError> {
133 while let Some(result) = self.poll_frame() {
134 if let Some(received) = copy_data_payload(result?, buf) {
135 return Ok(Some(received));
136 }
137 }
138 Ok(None)
139 }
140
141 fn beacon_request_frame(&mut self) -> [u8; 10] {
142 let seq_number = self.sequence_number();
143 [0x3, 0x8, seq_number, 0xff, 0xff, 0xff, 0xff, 0x7, 0x0, 0x0]
144 }
145
146 async fn scan_channel_active(
147 &mut self,
148 channel: u8,
149 duration: u8,
150 ) -> Result<Option<PanDescriptorList>, MacError> {
151 self.flush();
152 self.driver.update_driver_config(|config| {
153 config.promiscuous = false;
154 config.channel = channel;
155 });
156 self.driver.start_receive();
157
158 let frame = self.beacon_request_frame();
159 if let Err(e) = self.driver.transmit(&frame).await {
160 log::error!("[MLME-SCAN]: error transmitting beacon: {e}");
161 }
162
163 log::debug!("[MLME-SCAN] sent beacon frame to channel {channel}, waiting for messages...");
164
165 let delay_us: u64 = calculate_scan_duration_max_us(duration).into();
166 log::debug!("[MLME-SCAN] waiting for response for {delay_us}us");
167
168 let mut pds = Vec::new();
169 let deadline = Timer::after_micros(delay_us);
170 let mut deadline = core::pin::pin!(deadline);
171
172 loop {
173 match embassy_futures::select::select(&mut deadline, self.next_frame()).await {
174 Either::First(_) => break,
175 Either::Second(Ok(frame)) => {
176 if let Some(pd) = self.parse_beacon(frame) {
177 pds.push(pd);
178 }
179 }
180 Either::Second(Err(_)) => continue,
181 }
182 }
183
184 Ok(Some(pds))
185 }
186
187 fn parse_beacon(&self, received: ReceivedFrame) -> Option<PanDescriptor> {
188 match received {
189 ReceivedFrame {
190 frame:
191 Frame {
192 header:
193 hdr @ Header {
194 source: Some(source),
195 ..
196 },
197 content: FrameContent::Beacon(beacon_content),
198 payload,
199 ..
200 },
201 channel,
202 lqi,
203 ..
204 } => {
205 log::debug!("[MLME-SCAN] received beacon frame on channel {channel}");
206
207 let zigbee_beacon = match payload.read_with(&mut 0, ()) {
208 Ok(zb) => zb,
209 Err(e) => {
210 log::warn!("[MLME-SCAN] failed to parse zigbee beacon: {e:?}");
211 return None;
212 }
213 };
214
215 Some(PanDescriptor {
216 channel,
217 coord_addr_mode: match source {
218 Address::Short(_, _) => 0x2,
219 Address::Extended(_, _) => 0x3,
220 },
221 coord_pan_id: source.pan_id().0.into(),
222 coord_address: source,
223 superframe_spec: beacon_content.superframe_spec,
224 link_quality: lqi,
225 security_use: hdr.has_security(),
226 zigbee_beacon,
227 })
228 }
229 other => {
230 log::debug!("[MLME-SCAN] received non-beacon frame: {other:?}");
231 None
232 }
233 }
234 }
235
236 fn data_request_frame(&mut self, dest: Address) -> Result<([u8; 20], usize), MacError> {
242 let seq = self.sequence_number();
243 let source = match self.driver.short_address() {
244 Some(short) => Address::Short(dest.pan_id(), ieee802154::mac::ShortAddress(short)),
245 None => Address::Extended(dest.pan_id(), self.driver.ieee_address()),
246 };
247 let frame_header = Header {
248 frame_type: FrameType::MacCommand,
249 frame_pending: false,
250 ack_request: true,
251 pan_id_compress: true,
252 seq_no_suppress: false,
253 ie_present: false,
254 version: FrameVersion::Ieee802154_2003,
255 seq,
256 destination: Some(dest),
257 source: Some(source),
258 auxiliary_security_header: None,
259 };
260 let frame_content = FrameContent::Command(Command::DataRequest);
261
262 let mut buf = [0u8; 20];
263 let offset = &mut 0;
264 buf.write_with(
265 offset,
266 frame_header,
267 &Some(&mut SecurityContext::no_security()),
268 )?;
269 buf.write_with(offset, frame_content, ())?;
270
271 Ok((buf, *offset + 2))
272 }
273
274 async fn recv_association_response(
278 &mut self,
279 timeout_us: u64,
280 ) -> Result<Option<AssociationResponse>, MacError> {
281 let timeout = Timer::after_micros(timeout_us);
286 let receive = async {
287 loop {
288 Timer::after_micros(4000).await;
289 while let Some(result) = self.poll_frame() {
290 if let ReceivedFrame {
291 frame:
292 Frame {
293 content:
294 FrameContent::Command(Command::AssociationResponse(
295 short_addr,
296 status,
297 )),
298 ..
299 },
300 ..
301 } = result?
302 {
303 return Ok::<_, MacError>(AssociationResponse {
304 device_address: zigbee_types::IeeeAddress(self.driver.ieee_address().0),
305 association_address: zigbee_types::ShortAddress(short_addr.0),
306 status,
307 });
308 }
309 }
310 }
311 };
312 match embassy_futures::select::select(timeout, receive).await {
313 Either::First(_) => Ok(None),
314 Either::Second(Ok(r)) => Ok(Some(r)),
315 Either::Second(Err(e)) => Err(e),
316 }
317 }
318
319 fn take_unicast_data(&mut self, buf: &mut [u8]) -> Result<Option<(usize, u8)>, MacError> {
322 while let Some(result) = self.poll_frame() {
323 let received = result?;
324 if matches!(
325 received.frame.header.destination,
326 Some(Address::Short(_, ieee802154::mac::ShortAddress(d))) if d >= 0xfff8
327 ) {
328 continue;
329 }
330 if let Some(data) = copy_data_payload(received, buf) {
331 return Ok(Some(data));
332 }
333 }
334 Ok(None)
335 }
336
337 async fn recv_data_response(
341 &mut self,
342 timeout_us: u64,
343 buf: &mut [u8],
344 ) -> Result<Option<(usize, u8)>, MacError> {
345 let timeout = Timer::after_micros(timeout_us);
346 let receive = async {
347 loop {
348 Timer::after_micros(4000).await;
349 if let Some(data) = self.take_unicast_data(buf)? {
350 return Ok::<_, MacError>(Some(data));
351 }
352 }
353 };
354 match embassy_futures::select::select(timeout, receive).await {
355 Either::First(_) => Ok(None),
356 Either::Second(result) => result,
357 }
358 }
359
360 fn association_request_frame(
361 &mut self,
362 dest: Address,
363 src: Option<Address>,
364 capabilities: CapabilityInformation,
365 ) -> Result<[u8; 21], MacError> {
366 let seq = self.sequence_number();
367 let frame_header = Header {
368 frame_type: FrameType::MacCommand,
369 frame_pending: false,
370 ack_request: true,
371 pan_id_compress: false,
372 seq_no_suppress: false,
373 ie_present: false,
374 version: FrameVersion::Ieee802154_2003,
375 seq,
376 destination: Some(dest),
377 source: src,
378 auxiliary_security_header: None,
379 };
380 let frame_content = FrameContent::Command(Command::AssociationRequest(capabilities));
381
382 let mut buf = [0u8; 21];
383 let offset = &mut 0;
384 buf.write_with(
385 offset,
386 frame_header,
387 &Some(&mut SecurityContext::no_security()),
388 )?;
389 buf.write_with(offset, frame_content, ())?;
390
391 Ok(buf)
392 }
393}
394
395fn copy_data_payload(received: ReceivedFrame, buf: &mut [u8]) -> Option<(usize, u8)> {
397 let ReceivedFrame {
398 frame:
399 Frame {
400 content: FrameContent::Data,
401 payload,
402 ..
403 },
404 lqi,
405 ..
406 } = received
407 else {
408 return None;
409 };
410 let len = payload.len().min(buf.len());
411 buf[..len].copy_from_slice(&payload[..len]);
412 Some((len, lqi))
413}
414
415fn calculate_scan_duration_max_us(duration: u8) -> u32 {
416 16 * A_BASE_SUPER_FRAME_DURATION * (2 * (duration as u32) + 1)
418}
419
420impl EspMlmeInner<'_> {
421 async fn scan_network(
422 &mut self,
423 scan_type: ScanType,
424 channels: core::ops::Range<u8>,
425 duration: u8,
426 ) -> Result<ScanResult, MacError> {
427 if !matches!(scan_type, ScanType::Active) {
428 return Err(MacError::InvalidScanParams);
429 }
430
431 log::debug!("[MLME-SCAN] start scan");
432
433 let mut pan_descriptor = Vec::new();
434 for c in channels {
435 if (c as usize) >= MAX_IEEE802154_CHANNELS {
436 continue;
437 }
438
439 match self.scan_channel_active(c, duration).await {
440 Ok(Some(mut pd)) => {
441 pan_descriptor.append(&mut pd);
442 }
443 Err(e) => {
444 log::error!("[MLME-SCAN] error on channel {c}: {e}");
445 }
446 _ => (),
447 }
448 }
449
450 log::debug!("[MLME-SCAN] success");
451
452 Ok(ScanResult {
453 scan_type,
454 pan_descriptor,
455 })
456 }
457
458 async fn associate(
459 &mut self,
460 channel: u8,
461 dest: Address,
462 capabilities: CapabilityInformation,
463 ) -> Result<AssociationResponse, MacError> {
464 self.driver.update_driver_config(|config| {
468 *config = Default::default();
469 config.channel = channel;
470 config.pan_id = Some(dest.pan_id().0);
471 config.auto_ack_tx = true;
472 config.auto_ack_rx = true;
473 config.promiscuous = false;
474 });
475 self.driver.start_receive();
478
479 let ext_addr = self.driver.ieee_address();
480 let src = Address::Extended(PanId::broadcast(), ext_addr);
482 let timeout_us = (A_RESPONSE_WAIT_TIME as u64) * 16;
483
484 let mut response = None;
489 'association: for _ in 0..ASSOCIATE_REQUEST_RETRIES {
490 let frame = self.association_request_frame(dest, Some(src), capabilities)?;
491 match self.transmit_acked(&frame).await {
492 Ok(()) => {}
493 Err(MacError::NoAck) => {
494 log::debug!("[MLME-ASSOCIATE] request not acked, retrying");
495 continue;
496 }
497 Err(e) => return Err(e),
498 }
499 log::debug!(
500 "[MLME-ASSOCIATE] request acked, ack_pending={:?}",
501 self.driver.last_ack_frame_pending()
502 );
503
504 if let Some(r) = self.recv_association_response(timeout_us).await? {
506 response = Some(r);
507 break 'association;
508 }
509
510 for _ in 0..ASSOCIATE_POLL_RETRIES {
511 let (data_req, len) = self.data_request_frame(dest)?;
512 match self.transmit_acked(&data_req[..len]).await {
513 Ok(()) | Err(MacError::NoAck) => {}
514 Err(e) => return Err(e),
515 }
516 self.driver.start_receive();
520 if let Some(r) = self.recv_association_response(timeout_us).await? {
521 response = Some(r);
522 break 'association;
523 }
524 }
525 }
526 let response = response.ok_or(MacError::NoData)?;
527
528 log::debug!(
529 "[MLME-ASSOCIATE] success, short_addr={:?}",
530 response.association_address
531 );
532
533 let short = response.association_address.0;
535 self.driver.update_driver_config(|config| {
536 config.promiscuous = false;
537 config.short_addr = Some(short);
538 });
539
540 Ok(response)
541 }
542
543 async fn poll_data(
544 &mut self,
545 coord_address: Address,
546 buf: &mut [u8],
547 ) -> Result<(usize, u8), MacError> {
548 if let Some((len, lqi)) = self.take_unicast_data(buf)? {
551 log::debug!("[MLME-POLL] rx buffered data len={len}");
552 return Ok((len, lqi));
553 }
554 let timeout_us = (A_RESPONSE_WAIT_TIME as u64) * 16;
555
556 let mut acked = false;
558 for _ in 0..POLL_DATA_RETRIES {
559 let (data_req, len) = self.data_request_frame(coord_address)?;
560 match self.transmit_acked(&data_req[..len]).await {
561 Ok(()) => acked = true,
563 Err(MacError::NoAck) => {}
564 Err(e) => return Err(e),
565 }
566 self.driver.start_receive();
569 if let Some((len, lqi)) = self.recv_data_response(timeout_us, buf).await? {
570 log::debug!("[MLME-POLL] rx data len={len}");
571 return Ok((len, lqi));
572 }
573 }
574 if acked {
578 Err(MacError::NoData)
579 } else {
580 Err(MacError::NoAck)
581 }
582 }
583
584 async fn transmit_data(&mut self, dest: Address, payload: &[u8]) -> Result<(), MacError> {
585 let seq = self.sequence_number();
586
587 let is_broadcast = matches!(dest, Address::Short(_, sa) if sa.0 >= 0xfff8);
590 let dest = if is_broadcast {
591 Address::Short(dest.pan_id(), ieee802154::mac::ShortAddress(0xffff))
592 } else {
593 dest
594 };
595
596 let source = Some(match self.driver.short_address() {
597 Some(short) => Address::Short(dest.pan_id(), ieee802154::mac::ShortAddress(short)),
598 None => Address::Extended(dest.pan_id(), self.driver.ieee_address()),
599 });
600
601 let frame_header = Header {
602 frame_type: FrameType::Data,
603 frame_pending: false,
604 ack_request: !is_broadcast,
605 pan_id_compress: source.is_some(),
606 seq_no_suppress: false,
607 ie_present: false,
608 version: FrameVersion::Ieee802154_2003,
609 seq,
610 destination: Some(dest),
611 source,
612 auxiliary_security_header: None,
613 };
614
615 let mut frame_buf = [0u8; 127];
616 let offset = &mut 0;
617 frame_buf.write_with(
618 offset,
619 frame_header,
620 &Some(&mut SecurityContext::no_security()),
621 )?;
622 let hdr_len = *offset;
623 let payload_len = payload.len().min(frame_buf.len() - hdr_len - 2);
624 frame_buf[hdr_len..hdr_len + payload_len].copy_from_slice(&payload[..payload_len]);
625 let total_len = hdr_len + payload_len + 2;
629
630 if is_broadcast {
634 self.driver.transmit(&frame_buf[..total_len]).await?;
635 } else {
636 self.transmit_acked(&frame_buf[..total_len]).await?;
637 }
638 log::debug!("[MLME] tx data, len={total_len}");
639
640 Ok(())
641 }
642}
643
644impl Mlme for EspMlme<'_> {
645 fn ieee_address(&self) -> zigbee_types::IeeeAddress {
646 zigbee_types::IeeeAddress(self.ieee_address)
647 }
648
649 async fn configure(&self, config: MacConfig) {
650 let mut inner = self.inner.lock().await;
651 inner.driver.update_driver_config(|driver| {
652 if let Some(channel) = config.channel {
653 driver.channel = channel;
654 }
655 if let Some(pan_id) = config.pan_id {
656 driver.pan_id = Some(pan_id.0);
657 }
658 if let Some(short_address) = config.short_address {
659 driver.short_addr = Some(short_address.0);
660 }
661 if let Some(promiscuous) = config.promiscuous {
662 driver.promiscuous = promiscuous;
663 }
664 if let Some(auto_ack_rx) = config.auto_ack_rx {
665 driver.auto_ack_rx = auto_ack_rx;
666 }
667 if let Some(auto_ack_tx) = config.auto_ack_tx {
668 driver.auto_ack_tx = auto_ack_tx;
669 }
670 });
671 inner.driver.start_receive();
674 }
675
676 async fn scan_network(
677 &self,
678 ty: ScanType,
679 channels: core::ops::Range<u8>,
680 duration: u8,
681 ) -> Result<ScanResult, MacError> {
682 self.inner
683 .lock()
684 .await
685 .scan_network(ty, channels, duration)
686 .await
687 }
688
689 async fn associate(
690 &self,
691 channel: u8,
692 dest: Address,
693 capabilities: CapabilityInformation,
694 ) -> Result<AssociationResponse, MacError> {
695 self.inner
696 .lock()
697 .await
698 .associate(channel, dest, capabilities)
699 .await
700 }
701
702 async fn poll_data(
703 &self,
704 coord_address: Address,
705 buf: &mut [u8],
706 ) -> Result<(usize, u8), MacError> {
707 self.inner.lock().await.poll_data(coord_address, buf).await
708 }
709
710 async fn receive(&self, buf: &mut [u8]) -> Result<(usize, u8), MacError> {
711 loop {
712 {
715 let mut inner = self.inner.lock().await;
716 if let Some(received) = inner.try_drain(buf)? {
717 return Ok(received);
718 }
719 }
720 driver::wait_rx_signal().await;
721 }
722 }
723
724 async fn transmit_data(&self, dest: Address, payload: &[u8]) -> Result<(), MacError> {
725 self.inner.lock().await.transmit_data(dest, payload).await
726 }
727}