1use core::cell::UnsafeCell;
7use core::ffi::{self, c_void, CStr};
8use core::fmt::Debug;
9use core::marker::PhantomData;
10use core::ops::{Deref, DerefMut};
11use core::ptr::addr_of_mut;
12
13use alloc::boxed::Box;
14use alloc::sync::Arc;
15
16#[allow(unused)]
17use ::log::{debug, info, warn};
18
19use crate::eventloop::{EspEventDeserializer, EspEventSource, EspSystemEventLoop};
20use crate::hal::delay;
21use crate::hal::gpio::{InputPin, OutputPin};
22use crate::hal::uart::Uart;
23#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
24use crate::handle::RawHandle;
25use crate::io::vfs::MountedEventfs;
26#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
27use crate::netif::*;
28use crate::nvs::EspDefaultNvsPartition;
29use crate::private::mutex::{Condvar, Mutex};
30use crate::sys::*;
31use crate::thread::srp::OtSrp;
32
33pub use srp::*;
34
35extern crate alloc;
36
37mod srp;
38
39pub trait Mode {
42 fn init();
43}
44
45#[cfg(esp_idf_soc_ieee802154_supported)]
49#[derive(Debug)]
50pub struct RCP(());
51
52#[cfg(esp_idf_soc_ieee802154_supported)]
53impl Mode for RCP {
54 fn init() {
55 {
57 extern "C" {
58 fn otAppNcpInit(instance: *mut otInstance);
59 }
60
61 unsafe {
62 otAppNcpInit(esp_openthread_get_instance());
63 }
64 }
65 }
66}
67
68#[derive(Debug)]
74pub struct Host(());
75
76impl Mode for Host {
77 fn init() {}
78}
79
80pub mod config {
81 use crate::hal::uart::config::*;
82 use crate::hal::units::*;
83
84 #[cfg(all(esp32c2, esp_idf_xtal_freq_26))]
86 pub const UART_SAFE_BAUD_RATE: Hertz = Hertz(74880);
87
88 #[cfg(not(all(esp32c2, esp_idf_xtal_freq_26)))]
90 pub const UART_SAFE_BAUD_RATE: Hertz = Hertz(115200);
91
92 pub fn uart_default_cfg() -> Config {
94 Config::new()
95 .baudrate(UART_SAFE_BAUD_RATE)
96 .data_bits(DataBits::DataBits8)
97 .parity_none()
98 .stop_bits(StopBits::STOP1)
99 .flow_control(FlowControl::None)
100 .flow_control_rts_threshold(0)
101 }
102}
103
104macro_rules! ot_esp {
105 ($err:expr) => {{
106 $crate::sys::esp!($crate::thread::ot_esp_code($err as u32))
107 }};
108}
109
110pub(crate) use ot_esp;
111
112#[allow(non_upper_case_globals, non_snake_case)]
113pub(crate) const fn ot_esp_code(ot_code: u32) -> esp_err_t {
114 match ot_code {
115 crate::sys::otError_OT_ERROR_NONE => crate::sys::ESP_OK as _,
116 crate::sys::otError_OT_ERROR_FAILED => crate::sys::ESP_FAIL as _,
117 _ => crate::sys::ESP_FAIL as _, }
119}
120
121pub(crate) fn ot_esp_err(ot_code: u32) -> EspError {
122 EspError::from(ot_esp_code(ot_code)).unwrap()
123}
124
125pub struct ActiveScanResult<'a>(&'a otActiveScanResult);
127
128impl<'a> ActiveScanResult<'a> {
129 pub fn extended_address(&self) -> &'a [u8] {
131 &self.0.mExtAddress.m8
132 }
133
134 pub fn network_name_cstr(&self) -> &'a CStr {
136 unsafe { ffi::CStr::from_ptr(&self.0.mNetworkName.m8 as *const _ as *const _) }
137 }
138
139 pub fn extended_pan_id(&self) -> &[u8] {
141 &self.0.mExtendedPanId.m8
142 }
143
144 pub fn steering_data(&self) -> &[u8] {
146 &self.0.mSteeringData.m8
147 }
148
149 pub fn pan_id(&self) -> u16 {
151 self.0.mPanId
152 }
153
154 pub fn joiner_udp_port(&self) -> u16 {
156 self.0.mJoinerUdpPort
157 }
158
159 pub fn channel(&self) -> u8 {
161 self.0.mChannel
162 }
163
164 pub fn max_rssi(&self) -> i8 {
166 self.0.mRssi
167 }
168
169 pub fn lqi(&self) -> u8 {
171 self.0.mLqi
172 }
173
174 pub fn version(&self) -> u8 {
176 self.0.mVersion() as _
177 }
178
179 pub fn native_commissioner(&self) -> bool {
181 self.0.mIsNative()
182 }
183
184 pub fn join_permitted(&self) -> bool {
186 self.0.mIsJoinable()
187 }
188}
189
190pub struct EnergyScanResult<'a>(&'a otEnergyScanResult);
192
193impl EnergyScanResult<'_> {
194 pub fn channel(&self) -> u8 {
196 self.0.mChannel
197 }
198
199 pub fn max_rssi(&self) -> i8 {
201 self.0.mMaxRssi
202 }
203}
204
205#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
207pub enum Role {
208 Disabled,
209 Detached,
210 Child,
211 Router,
212 Leader,
213}
214
215#[allow(non_upper_case_globals, non_snake_case)]
216impl From<otDeviceRole> for Role {
217 fn from(role: otDeviceRole) -> Self {
218 match role {
219 otDeviceRole_OT_DEVICE_ROLE_DISABLED => Role::Disabled,
220 otDeviceRole_OT_DEVICE_ROLE_DETACHED => Role::Detached,
221 otDeviceRole_OT_DEVICE_ROLE_CHILD => Role::Child,
222 otDeviceRole_OT_DEVICE_ROLE_ROUTER => Role::Router,
223 otDeviceRole_OT_DEVICE_ROLE_LEADER => Role::Leader,
224 _ => Role::Disabled,
225 }
226 }
227}
228
229pub struct Ipv6Packet<'a>(&'a otMessage);
231
232impl Ipv6Packet<'_> {
233 pub fn raw(&self) -> &otMessage {
234 self.0
235 }
236
237 #[allow(clippy::len_without_is_empty)]
238 pub fn len(&self) -> usize {
239 unsafe { otMessageGetLength(self.0) as _ }
240 }
241
242 pub fn offset(&self) -> usize {
243 unsafe { otMessageGetOffset(self.0) as _ }
244 }
245
246 pub fn read(&self, offset: usize, buf: &mut [u8]) -> usize {
247 let len = self.len();
248
249 unsafe { otMessageRead(self.0, offset as _, buf.as_mut_ptr() as *mut _, len as _) as _ }
250 }
251}
252
253pub enum Ipv6Incoming<'a> {
255 AddressAdded(core::net::Ipv6Addr),
257 AddressRemoved(core::net::Ipv6Addr),
259 Data(Ipv6Packet<'a>),
261}
262
263pub struct ThreadDriver<'d, T>
279where
280 T: Mode,
281{
282 inner: UnsafeCell<Box<ThreadDriverInner>>,
283 _nvs: EspDefaultNvsPartition,
285 _mounted_event_fs: Arc<MountedEventfs>,
286 _mode: T,
287 _p: PhantomData<&'d mut ()>,
288}
289
290impl<'d> ThreadDriver<'d, Host> {
291 #[cfg(esp_idf_soc_ieee802154_supported)]
294 pub fn new<M: crate::hal::modem::ThreadModemPeripheral + 'd>(
295 modem: M,
296 sysloop: EspSystemEventLoop,
297 nvs: EspDefaultNvsPartition,
298 mounted_event_fs: Arc<MountedEventfs>,
299 ) -> Result<Self, EspError> {
300 Self::internal_new(
301 Self::host_native_cfg(modem),
302 sysloop,
303 nvs,
304 mounted_event_fs,
305 Host(()),
306 )
307 }
308
309 #[cfg(not(esp_idf_version_major = "4"))]
312 #[allow(clippy::too_many_arguments)]
313 pub fn new_spi<S: crate::hal::spi::Spi + 'd>(
314 spi: S,
315 mosi: impl InputPin + 'd,
316 miso: impl OutputPin + 'd,
317 sclk: impl InputPin + OutputPin + 'd,
318 cs: Option<impl InputPin + OutputPin + 'd>,
319 intr: Option<impl InputPin + OutputPin + 'd>,
320 config: &crate::hal::spi::config::Config,
321 sysloop: EspSystemEventLoop,
322 nvs: EspDefaultNvsPartition,
323 mounted_event_fs: Arc<MountedEventfs>,
324 ) -> Result<Self, EspError> {
325 Self::internal_new(
326 Self::host_spi_cfg(spi, mosi, miso, sclk, cs, intr, config),
327 sysloop,
328 nvs,
329 mounted_event_fs,
330 Host(()),
331 )
332 }
333
334 pub fn new_uart<U: Uart + 'd>(
337 uart: U,
338 tx: impl OutputPin + 'd,
339 rx: impl InputPin + 'd,
340 config: &crate::hal::uart::config::Config,
341 sysloop: EspSystemEventLoop,
342 nvs: EspDefaultNvsPartition,
343 mounted_event_fs: Arc<MountedEventfs>,
344 ) -> Result<Self, EspError> {
345 Self::internal_new(
346 Self::host_uart_cfg(uart, tx, rx, config),
347 sysloop,
348 nvs,
349 mounted_event_fs,
350 Host(()),
351 )
352 }
353
354 pub fn enable_ipv6(&self, enable: bool) -> Result<(), EspError> {
356 let _lock = self.inner();
357
358 ot_esp!(unsafe { otIp6SetEnabled(esp_openthread_get_instance(), enable) })
359 }
360
361 pub fn enable_thread(&self, enable: bool) -> Result<(), EspError> {
365 let _lock = self.inner();
366
367 ot_esp!(unsafe { otThreadSetEnabled(esp_openthread_get_instance(), enable) })
368 }
369
370 pub fn role(&self) -> Result<Role, EspError> {
372 let _lock = self.inner();
373
374 Ok(unsafe { otThreadGetDeviceRole(esp_openthread_get_instance()) }.into())
375 }
376
377 #[cfg(esp_idf_openthread_cli)]
381 pub fn init_cli(&mut self) -> Result<(), EspError> {
382 unsafe {
385 esp_openthread_cli_init();
386 }
387
388 #[cfg(esp_idf_openthread_cli_esp_extension)]
389 unsafe {
390 esp_cli_custom_command_init();
391 }
392
393 unsafe {
394 esp_openthread_cli_create_task();
395 }
396
397 Ok(())
398 }
399
400 pub fn tod(&self, buf: &mut [u8]) -> Result<usize, EspError> {
406 let mut inner = self.inner();
407
408 Self::internal_tod(&mut inner, true, buf)
409 }
410
411 pub fn pending_tod(&self, buf: &mut [u8]) -> Result<usize, EspError> {
417 let mut inner = self.inner();
418
419 Self::internal_tod(&mut inner, false, buf)
420 }
421
422 pub fn set_tod(&self, tod: &[u8]) -> Result<(), EspError> {
426 let mut inner = self.inner();
427
428 Self::fill_dataset_tlv(&mut inner.dataset_buf, tod)?;
429
430 Self::internal_set_tod(&mut inner, true)
431 }
432
433 pub fn set_pending_tod(&self, tod: &[u8]) -> Result<(), EspError> {
437 let mut inner = self.inner();
438
439 Self::fill_dataset_tlv(&mut inner.dataset_buf, tod)?;
440
441 Self::internal_set_tod(&mut inner, false)
442 }
443
444 pub fn set_tod_hexstr(&self, tod: &str) -> Result<(), EspError> {
448 let mut inner = self.inner();
449
450 Self::fill_dataset_tlv_hexstr(&mut inner.dataset_buf, tod)?;
451
452 Self::internal_set_tod(&mut inner, true)
453 }
454
455 pub fn set_pending_tod_hexstr(&self, tod: &str) -> Result<(), EspError> {
459 let mut inner = self.inner();
460
461 Self::fill_dataset_tlv_hexstr(&mut inner.dataset_buf, tod)?;
462
463 Self::internal_set_tod(&mut inner, false)
464 }
465
466 #[cfg(not(esp_idf_version_major = "4"))]
470 pub fn set_tod_from_cfg(&self) -> Result<(), EspError> {
471 let _lock = self.inner();
472
473 ot_esp!(unsafe { esp_openthread_auto_start(core::ptr::null_mut()) })
474 }
475
476 pub fn scan<F: FnMut(Option<ActiveScanResult>) + Send + 'static>(
481 &self,
482 callback: F,
483 ) -> Result<(), EspError> {
484 let mut inner = self.inner();
485
486 if inner.scan_cb.is_some() {
487 return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
488 }
489
490 #[allow(clippy::type_complexity)]
491 let callback: Box<Box<dyn FnMut(Option<ActiveScanResult>) + Send + 'static>> =
492 Box::new(Box::new(callback));
493
494 inner.scan_cb = Some(callback);
496
497 match ot_esp!(unsafe {
498 otLinkActiveScan(
499 esp_openthread_get_instance(),
500 0xffff_ffffu32, 200, Some(Self::on_active_scan_result),
503 &mut *inner as *mut ThreadDriverInner as *mut c_void,
504 )
505 }) {
506 Ok(()) => Ok(()),
507 Err(err) => {
508 inner.scan_cb = None;
510 Err(err)
511 }
512 }
513 }
514
515 pub fn is_scan_in_progress(&self) -> Result<bool, EspError> {
517 let _lock = self.inner();
518
519 Ok(unsafe { otLinkIsActiveScanInProgress(esp_openthread_get_instance()) })
520 }
521
522 pub fn energy_scan<F: FnMut(Option<EnergyScanResult>) + Send + 'static>(
527 &self,
528 callback: F,
529 ) -> Result<(), EspError> {
530 let mut inner = self.inner();
531
532 if inner.energy_cb.is_some() {
533 return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
534 }
535
536 #[allow(clippy::type_complexity)]
537 let callback: Box<Box<dyn FnMut(Option<EnergyScanResult>) + Send + 'static>> =
538 Box::new(Box::new(callback));
539
540 inner.energy_cb = Some(callback);
542
543 match ot_esp!(unsafe {
544 otLinkEnergyScan(
545 esp_openthread_get_instance(),
546 0xffff_ffffu32, 200, Some(Self::on_energy_scan_result),
549 &mut *inner as *mut ThreadDriverInner as *mut c_void,
550 )
551 }) {
552 Ok(()) => Ok(()),
553 Err(err) => {
554 inner.energy_cb = None;
556 Err(err)
557 }
558 }
559 }
560
561 pub fn is_energy_scan_in_progress(&self) -> Result<bool, EspError> {
563 let _lock = self.inner();
564
565 Ok(unsafe { otLinkIsEnergyScanInProgress(esp_openthread_get_instance()) })
566 }
567
568 pub fn tx(&self, packet: &[u8]) -> Result<(), EspError> {
570 let _lock = self.inner();
571
572 let message =
573 unsafe { otIp6NewMessage(esp_openthread_get_instance(), core::ptr::null_mut()) };
574 if message.is_null() {
575 Err(EspError::from_infallible::<ESP_FAIL>())?;
576 }
577
578 let result = ot_esp!(unsafe {
579 otMessageAppend(message, packet.as_ptr() as *const _, packet.len() as _)
580 })
581 .and_then(|_| ot_esp!(unsafe { otIp6Send(esp_openthread_get_instance(), message) }));
582
583 unsafe { otMessageFree(message) };
584
585 result
586 }
587
588 pub fn set_rx_callback<R>(&self, callback: Option<R>) -> Result<(), EspError>
590 where
591 R: FnMut(Ipv6Incoming) + Send + 'static,
592 {
593 let mut inner = self.inner();
594
595 Self::internal_set_rx_callback(&mut inner, callback)
596 }
597
598 pub fn set_nonstatic_rx_callback<R>(&self, callback: Option<R>) -> Result<(), EspError>
624 where
625 R: FnMut(Ipv6Incoming) + Send + 'd,
626 {
627 let mut inner = self.inner();
628
629 Self::internal_set_rx_callback(&mut inner, callback)
630 }
631
632 fn internal_set_rx_callback<R>(
635 inner: &mut ThreadDriverInner,
636 callback: Option<R>,
637 ) -> Result<(), EspError>
638 where
639 R: FnMut(Ipv6Incoming) + Send + 'd,
640 {
641 if let Some(callback) = callback {
642 #[allow(clippy::type_complexity)]
643 let callback: Box<Box<dyn FnMut(Ipv6Incoming) + Send + 'd>> =
644 Box::new(Box::new(callback));
645
646 #[allow(clippy::type_complexity)]
647 let callback: Box<Box<dyn FnMut(Ipv6Incoming) + Send + 'static>> =
648 unsafe { core::mem::transmute(callback) };
649
650 inner.ipv6_cb = Some(callback);
652
653 unsafe {
654 otIp6SetAddressCallback(
655 esp_openthread_get_instance(),
656 Some(Self::on_address),
657 &mut *inner as *mut ThreadDriverInner as *mut c_void,
658 );
659 otIp6SetReceiveCallback(
660 esp_openthread_get_instance(),
661 Some(Self::on_packet),
662 &mut *inner as *mut ThreadDriverInner as *mut c_void,
663 );
664 otIp6SetReceiveFilterEnabled(esp_openthread_get_instance(), true);
665
666 }
668 } else {
669 unsafe {
670 otIp6SetAddressCallback(esp_openthread_get_instance(), None, core::ptr::null_mut());
671 otIp6SetReceiveCallback(esp_openthread_get_instance(), None, core::ptr::null_mut());
672 otIp6SetReceiveFilterEnabled(esp_openthread_get_instance(), true);
673 }
675
676 inner.ipv6_cb = None;
677 }
678
679 Ok(())
680 }
681
682 fn internal_tod(
683 inner: &mut ThreadDriverInner,
684 active: bool,
685 buf: &mut [u8],
686 ) -> Result<usize, EspError> {
687 let dataset_buf = &mut inner.dataset_buf;
688
689 ot_esp!(unsafe {
690 if active {
691 otDatasetGetActiveTlvs(esp_openthread_get_instance(), dataset_buf)
692 } else {
693 otDatasetGetPendingTlvs(esp_openthread_get_instance(), dataset_buf)
694 }
695 })?;
696
697 let len = dataset_buf.mLength as usize;
698 if buf.len() < len {
699 Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>())?;
700 }
701
702 buf[..len].copy_from_slice(&dataset_buf.mTlvs[..len]);
703
704 Ok(len)
705 }
706
707 fn internal_set_tod(inner: &mut ThreadDriverInner, active: bool) -> Result<(), EspError> {
708 ot_esp!(unsafe {
709 if active {
710 otDatasetSetActiveTlvs(esp_openthread_get_instance(), &inner.dataset_buf)
711 } else {
712 otDatasetSetPendingTlvs(esp_openthread_get_instance(), &inner.dataset_buf)
713 }
714 })?;
715
716 Ok(())
717 }
718
719 fn fill_dataset_tlv(
720 dataset_buf: &mut otOperationalDatasetTlvs,
721 data: &[u8],
722 ) -> Result<(), EspError> {
723 if data.len() > core::mem::size_of_val(&dataset_buf.mTlvs) {
724 Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>())?;
725 }
726
727 dataset_buf.mLength = data.len() as _;
728 dataset_buf.mTlvs[..data.len()].copy_from_slice(data);
729
730 Ok(())
731 }
732
733 fn fill_dataset_tlv_hexstr(
735 dataset_buf: &mut otOperationalDatasetTlvs,
736 dataset: &str,
737 ) -> Result<(), EspError> {
738 let dataset = dataset.trim();
739 let mut offset = 0;
740
741 for (chf, chs) in dataset
742 .chars()
743 .step_by(2)
744 .zip(dataset.chars().skip(1).step_by(2))
745 {
746 let byte = (chf
747 .to_digit(16)
748 .ok_or(ot_esp_err(otError_OT_ERROR_INVALID_ARGS))?
749 << 4)
750 | chs
751 .to_digit(16)
752 .ok_or(ot_esp_err(otError_OT_ERROR_INVALID_ARGS))?;
753
754 if offset >= dataset_buf.mTlvs.len() {
755 Err(ot_esp_err(otError_OT_ERROR_NO_BUFS))?;
756 }
757
758 dataset_buf.mTlvs[offset] = byte as _;
759 offset += 1;
760 }
761
762 dataset_buf.mLength = offset as _;
763
764 Ok(())
765 }
766
767 unsafe extern "C" fn on_address(
768 address_info: *const otIp6AddressInfo,
769 is_added: bool,
770 context: *mut c_void,
771 ) {
772 let inner = unsafe { (context as *mut ThreadDriverInner).as_mut().unwrap() };
773
774 if let Some(ipv6_cb) = inner.ipv6_cb.as_mut() {
775 let address_info = unsafe { address_info.as_ref() }.unwrap();
776 let ot_address = unsafe { address_info.mAddress.as_ref() }.unwrap();
777
778 let address = core::net::Ipv6Addr::from(ot_address.mFields.m8);
779
780 if is_added {
781 ipv6_cb(Ipv6Incoming::AddressAdded(address));
782 } else {
783 ipv6_cb(Ipv6Incoming::AddressRemoved(address));
784 }
785 }
786 }
787
788 unsafe extern "C" fn on_packet(message: *mut otMessage, context: *mut c_void) {
789 let inner = unsafe { (context as *mut ThreadDriverInner).as_mut().unwrap() };
790
791 if let Some(ipv6_cb) = inner.ipv6_cb.as_mut() {
792 ipv6_cb(Ipv6Incoming::Data(Ipv6Packet(
793 unsafe { message.as_ref() }.unwrap(),
794 )));
795 }
796
797 otMessageFree(message);
798 }
799
800 unsafe extern "C" fn on_active_scan_result(
801 result: *mut otActiveScanResult,
802 context: *mut c_void,
803 ) {
804 let inner = unsafe { (context as *mut ThreadDriverInner).as_mut().unwrap() };
805
806 if let Some(scan_cb) = inner.scan_cb.as_mut() {
807 if result.is_null() {
808 scan_cb(None);
809 } else {
810 scan_cb(Some(ActiveScanResult(unsafe { result.as_ref() }.unwrap())));
811 }
812 }
813
814 if result.is_null() {
815 inner.scan_cb = None;
816 }
817 }
818
819 unsafe extern "C" fn on_energy_scan_result(
820 result: *mut otEnergyScanResult,
821 context: *mut c_void,
822 ) {
823 let inner = unsafe { (context as *mut ThreadDriverInner).as_mut().unwrap() };
824
825 if let Some(energy_cb) = inner.energy_cb.as_mut() {
826 if result.is_null() {
827 energy_cb(None);
828 } else {
829 energy_cb(Some(EnergyScanResult(unsafe { result.as_ref() }.unwrap())));
830 }
831 }
832
833 if result.is_null() {
834 inner.energy_cb = None;
835 }
836 }
837
838 #[cfg(esp_idf_soc_ieee802154_supported)]
839 fn host_native_cfg<M: crate::hal::modem::ThreadModemPeripheral + 'd>(
840 _modem: M,
841 ) -> esp_openthread_platform_config_t {
842 esp_openthread_platform_config_t {
843 radio_config: esp_openthread_radio_config_t {
844 radio_mode: esp_openthread_radio_mode_t_RADIO_MODE_NATIVE,
845 ..Default::default()
846 },
847 host_config: esp_openthread_host_connection_config_t {
848 host_connection_mode:
849 esp_openthread_host_connection_mode_t_HOST_CONNECTION_MODE_NONE,
850 ..Default::default()
851 },
852 port_config: Self::PORT_CONFIG,
853 }
854 }
855
856 #[cfg(not(esp_idf_version_major = "4"))]
857 #[allow(clippy::too_many_arguments)]
858 fn host_spi_cfg<S: crate::hal::spi::Spi + 'd>(
859 _spi: S,
860 mosi: impl InputPin + 'd,
861 miso: impl OutputPin + 'd,
862 sclk: impl InputPin + OutputPin + 'd,
863 cs: Option<impl InputPin + OutputPin + 'd>,
864 intr: Option<impl InputPin + OutputPin + 'd>,
865 config: &crate::hal::spi::config::Config,
866 ) -> esp_openthread_platform_config_t {
867 let cs_pin = if let Some(cs) = cs { cs.pin() as _ } else { -1 };
868
869 let intr_pin = if let Some(intr) = intr {
870 intr.pin() as _
871 } else {
872 -1
873 };
874
875 let mut icfg: spi_device_interface_config_t = config.into();
876 icfg.spics_io_num = cs_pin as _;
877
878 esp_openthread_platform_config_t {
879 radio_config: esp_openthread_radio_config_t {
880 radio_mode: esp_openthread_radio_mode_t_RADIO_MODE_SPI_RCP,
881 __bindgen_anon_1: esp_openthread_radio_config_t__bindgen_ty_1 {
882 radio_spi_config: esp_openthread_spi_host_config_t {
883 host_device: S::device() as _,
884 dma_channel: spi_common_dma_t_SPI_DMA_CH_AUTO,
885 #[cfg(not(esp_idf_version_at_least_6_0_0))]
886 spi_interface: spi_bus_config_t {
887 __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
888 mosi_io_num: mosi.pin() as _,
889 },
890 __bindgen_anon_2: spi_bus_config_t__bindgen_ty_2 {
891 miso_io_num: miso.pin() as _,
892 },
893 sclk_io_num: sclk.pin() as _,
894 ..Default::default()
895 },
896 #[cfg(esp_idf_version_at_least_6_0_0)]
897 spi_interface: spi_bus_config_t {
898 __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
899 __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1__bindgen_ty_1 {
900 data4_io_num: -1,
901 data5_io_num: -1,
902 data6_io_num: -1,
903 data7_io_num: -1,
904 __bindgen_anon_1:
905 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 {
906 mosi_io_num: mosi.pin() as _,
907 },
908 __bindgen_anon_2:
909 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2 {
910 miso_io_num: miso.pin() as _,
911 },
912 __bindgen_anon_3:
913 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_3 {
914 quadwp_io_num: -1,
915 },
916 __bindgen_anon_4:
917 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_4 {
918 quadhd_io_num: -1,
919 },
920 sclk_io_num: sclk.pin() as _,
921 },
922 },
923 ..Default::default()
924 },
925 spi_device: icfg,
926 intr_pin,
927 },
928 },
929 },
930 host_config: esp_openthread_host_connection_config_t {
931 host_connection_mode:
932 esp_openthread_host_connection_mode_t_HOST_CONNECTION_MODE_NONE,
933 ..Default::default()
934 },
935 port_config: Self::PORT_CONFIG,
936 }
937 }
938
939 fn host_uart_cfg<U: Uart + 'd>(
940 _uart: U,
941 tx: impl OutputPin + 'd,
942 rx: impl InputPin + 'd,
943 config: &crate::hal::uart::config::Config,
944 ) -> esp_openthread_platform_config_t {
945 #[cfg(esp_idf_version_major = "4")]
946 let cfg = esp_openthread_platform_config_t {
947 radio_config: esp_openthread_radio_config_t {
948 radio_mode: esp_openthread_radio_mode_t_RADIO_MODE_UART_RCP,
949 radio_uart_config: esp_openthread_uart_config_t {
950 port: U::port() as _,
951 uart_config: config.into(),
952 rx_pin: rx.pin() as _,
953 tx_pin: tx.pin() as _,
954 },
955 },
956 host_config: esp_openthread_host_connection_config_t {
957 host_connection_mode:
958 esp_openthread_host_connection_mode_t_HOST_CONNECTION_MODE_NONE,
959 ..Default::default()
960 },
961 port_config: Self::PORT_CONFIG,
962 };
963
964 #[cfg(not(esp_idf_version_major = "4"))]
965 let cfg = esp_openthread_platform_config_t {
966 radio_config: esp_openthread_radio_config_t {
967 radio_mode: esp_openthread_radio_mode_t_RADIO_MODE_UART_RCP,
968 __bindgen_anon_1: esp_openthread_radio_config_t__bindgen_ty_1 {
969 radio_uart_config: esp_openthread_uart_config_t {
970 port: U::port() as _,
971 uart_config: config.into(),
972 rx_pin: rx.pin() as _,
973 tx_pin: tx.pin() as _,
974 },
975 },
976 },
977 host_config: esp_openthread_host_connection_config_t {
978 host_connection_mode:
979 esp_openthread_host_connection_mode_t_HOST_CONNECTION_MODE_NONE,
980 ..Default::default()
981 },
982 port_config: Self::PORT_CONFIG,
983 };
984
985 cfg
986 }
987}
988
989#[cfg(esp_idf_soc_ieee802154_supported)]
990impl<'d> ThreadDriver<'d, RCP> {
991 #[cfg(not(esp_idf_version_major = "4"))]
994 #[allow(clippy::too_many_arguments)]
995 pub fn new_rcp_spi<
996 M: crate::hal::modem::ThreadModemPeripheral + 'd,
997 S: crate::hal::spi::Spi + 'd,
998 >(
999 modem: M,
1000 spi: S,
1001 mosi: impl InputPin + 'd,
1002 miso: impl OutputPin + 'd,
1003 sclk: impl InputPin + OutputPin + 'd,
1004 cs: Option<impl InputPin + OutputPin + 'd>,
1005 intr: Option<impl InputPin + OutputPin + 'd>,
1006 sysloop: EspSystemEventLoop,
1007 nvs: EspDefaultNvsPartition,
1008 mounted_event_fs: Arc<MountedEventfs>,
1009 ) -> Result<Self, EspError> {
1010 Self::internal_new(
1011 Self::rcp_spi_cfg(modem, spi, mosi, miso, sclk, cs, intr),
1012 sysloop,
1013 nvs,
1014 mounted_event_fs,
1015 RCP(()),
1016 )
1017 }
1018
1019 #[allow(clippy::too_many_arguments)]
1022 pub fn new_rcp_uart<M: crate::hal::modem::ThreadModemPeripheral + 'd, U: Uart + 'd>(
1023 modem: M,
1024 uart: U,
1025 tx: impl OutputPin + 'd,
1026 rx: impl InputPin + 'd,
1027 config: &crate::hal::uart::config::Config,
1028 sysloop: EspSystemEventLoop,
1029 nvs: EspDefaultNvsPartition,
1030 mounted_event_fs: Arc<MountedEventfs>,
1031 ) -> Result<Self, EspError> {
1032 Self::internal_new(
1033 Self::rcp_uart_cfg(modem, uart, tx, rx, config),
1034 sysloop,
1035 nvs,
1036 mounted_event_fs,
1037 RCP(()),
1038 )
1039 }
1040
1041 #[cfg(not(esp_idf_version_major = "4"))]
1042 #[allow(clippy::too_many_arguments)]
1043 fn rcp_spi_cfg<
1044 M: crate::hal::modem::ThreadModemPeripheral + 'd,
1045 S: crate::hal::spi::Spi + 'd,
1046 >(
1047 _modem: M,
1048 _spi: S,
1049 mosi: impl InputPin + 'd,
1050 miso: impl OutputPin + 'd,
1051 sclk: impl InputPin + OutputPin + 'd,
1052 cs: Option<impl InputPin + OutputPin + 'd>,
1053 intr: Option<impl InputPin + OutputPin + 'd>,
1054 ) -> esp_openthread_platform_config_t {
1055 let cs_pin = if let Some(cs) = cs { cs.pin() as _ } else { -1 };
1056
1057 let intr_pin = if let Some(intr) = intr {
1058 intr.pin() as _
1059 } else {
1060 -1
1061 };
1062
1063 esp_openthread_platform_config_t {
1064 radio_config: esp_openthread_radio_config_t {
1065 radio_mode: esp_openthread_radio_mode_t_RADIO_MODE_NATIVE,
1066 ..Default::default()
1067 },
1068 host_config: esp_openthread_host_connection_config_t {
1069 host_connection_mode:
1070 esp_openthread_host_connection_mode_t_HOST_CONNECTION_MODE_RCP_UART,
1071 __bindgen_anon_1: esp_openthread_host_connection_config_t__bindgen_ty_1 {
1072 spi_slave_config: esp_openthread_spi_slave_config_t {
1073 host_device: S::device() as _,
1074 #[cfg(not(esp_idf_version_at_least_6_0_0))]
1075 bus_config: spi_bus_config_t {
1076 __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
1077 mosi_io_num: mosi.pin() as _,
1078 },
1079 __bindgen_anon_2: spi_bus_config_t__bindgen_ty_2 {
1080 miso_io_num: miso.pin() as _,
1081 },
1082 sclk_io_num: sclk.pin() as _,
1083 ..Default::default()
1084 },
1085 #[cfg(esp_idf_version_at_least_6_0_0)]
1086 bus_config: spi_bus_config_t {
1087 __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
1088 __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1__bindgen_ty_1 {
1089 data4_io_num: -1,
1090 data5_io_num: -1,
1091 data6_io_num: -1,
1092 data7_io_num: -1,
1093 __bindgen_anon_1:
1094 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 {
1095 mosi_io_num: mosi.pin() as _,
1096 },
1097 __bindgen_anon_2:
1098 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2 {
1099 miso_io_num: miso.pin() as _,
1100 },
1101 __bindgen_anon_3:
1102 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_3 {
1103 quadwp_io_num: -1,
1104 },
1105 __bindgen_anon_4:
1106 spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_4 {
1107 quadhd_io_num: -1,
1108 },
1109 sclk_io_num: sclk.pin() as _,
1110 },
1111 },
1112 ..Default::default()
1113 },
1114 slave_config: spi_slave_interface_config_t {
1115 spics_io_num: cs_pin as _,
1116 ..Default::default()
1117 },
1118 intr_pin,
1119 },
1120 },
1121 },
1122 port_config: Self::PORT_CONFIG,
1123 }
1124 }
1125
1126 fn rcp_uart_cfg<M: crate::hal::modem::ThreadModemPeripheral + 'd, U: Uart + 'd>(
1127 _modem: M,
1128 _uart: U,
1129 tx: impl OutputPin + 'd,
1130 rx: impl InputPin + 'd,
1131 config: &crate::hal::uart::config::Config,
1132 ) -> esp_openthread_platform_config_t {
1133 #[cfg(esp_idf_version_major = "4")]
1134 let cfg = esp_openthread_platform_config_t {
1135 radio_config: esp_openthread_radio_config_t {
1136 radio_mode: esp_openthread_radio_mode_t_RADIO_MODE_NATIVE,
1137 ..Default::default()
1138 },
1139 host_config: esp_openthread_host_connection_config_t {
1140 host_connection_mode:
1141 esp_openthread_host_connection_mode_t_HOST_CONNECTION_MODE_RCP_UART,
1142 host_uart_config: esp_openthread_uart_config_t {
1143 port: U::port() as _,
1144 uart_config: config.into(),
1145 rx_pin: rx.pin() as _,
1146 tx_pin: tx.pin() as _,
1147 },
1148 },
1149 port_config: Self::PORT_CONFIG,
1150 };
1151
1152 #[cfg(not(esp_idf_version_major = "4"))]
1153 let cfg = esp_openthread_platform_config_t {
1154 radio_config: esp_openthread_radio_config_t {
1155 radio_mode: esp_openthread_radio_mode_t_RADIO_MODE_NATIVE,
1156 ..Default::default()
1157 },
1158 host_config: esp_openthread_host_connection_config_t {
1159 host_connection_mode:
1160 esp_openthread_host_connection_mode_t_HOST_CONNECTION_MODE_RCP_UART,
1161 __bindgen_anon_1: esp_openthread_host_connection_config_t__bindgen_ty_1 {
1162 host_uart_config: esp_openthread_uart_config_t {
1163 port: U::port() as _,
1164 uart_config: config.into(),
1165 rx_pin: rx.pin() as _,
1166 tx_pin: tx.pin() as _,
1167 },
1168 },
1169 },
1170 port_config: Self::PORT_CONFIG,
1171 };
1172
1173 cfg
1174 }
1175}
1176
1177impl<T> ThreadDriver<'_, T>
1178where
1179 T: Mode,
1180{
1181 const PORT_CONFIG: esp_openthread_port_config_t = esp_openthread_port_config_t {
1182 storage_partition_name: b"nvs\0" as *const _ as *const _,
1183 netif_queue_size: 10,
1184 task_queue_size: 10,
1185 };
1186
1187 #[cfg(all(esp_idf_openthread_radio_native, esp_idf_soc_ieee802154_supported))]
1189 pub fn init_coex(&mut self) -> Result<(), EspError> {
1190 let _lock = self.inner();
1191
1192 Self::internal_init_coex()
1193 }
1194
1195 pub fn start(&mut self) -> Result<(), EspError> {
1199 {
1200 let mut inner = self.inner();
1201
1202 if *inner.started.lock() {
1203 Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>())?;
1204 }
1205
1206 #[allow(clippy::manual_c_str_literals)]
1207 unsafe {
1208 crate::hal::task::create(
1209 Self::run,
1210 CStr::from_bytes_with_nul_unchecked(b"ThreadDriver Runner\0"),
1211 12288,
1212 &mut *inner as *mut _ as *mut _,
1213 6,
1214 None,
1215 )?;
1216 }
1217 }
1218
1219 loop {
1220 let inner = unsafe { self.inner.get().as_mut().unwrap() };
1221
1222 let started = inner.started.lock();
1223
1224 if *started {
1225 break;
1226 }
1227
1228 inner.started_condvar.wait(started);
1229 }
1230
1231 info!("ThreadDriver started");
1232
1233 Ok(())
1234 }
1235
1236 pub fn stop(&mut self) -> Result<(), EspError> {
1240 {
1241 let inner = self.inner();
1242
1243 if !*inner.started.lock() {
1244 Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>())?;
1245 }
1246
1247 #[allow(unused_mut)]
1248 #[allow(unused_assignments)]
1249 let mut stop_supported = false;
1250
1251 #[cfg(any(
1252 esp_idf_version_at_least_5_5_0,
1253 all(esp_idf_version = "5.1", esp_idf_version_at_least_5_1_7),
1254 all(esp_idf_version = "5.2", esp_idf_version_at_least_5_2_6),
1255 all(esp_idf_version = "5.3", esp_idf_version_at_least_5_3_4),
1256 all(esp_idf_version = "5.4", esp_idf_version_at_least_5_4_3)
1257 ))]
1258 {
1259 unsafe {
1260 esp_openthread_mainloop_exit();
1261 }
1262
1263 stop_supported = true;
1264 }
1265
1266 if !stop_supported {
1267 panic!("Stopping the Thread driver is supported since ESP-IDF patch-level 5.3.3+, 5.4.3+ and 5.5.1+. Please update to a newer version or don't call `stop`.")
1268 }
1269 }
1270
1271 loop {
1272 let inner = unsafe { self.inner.get().as_mut().unwrap() };
1273
1274 let started = inner.started.lock();
1275
1276 if !*started {
1277 break;
1278 }
1279
1280 inner.started_condvar.wait(started);
1281 }
1282
1283 info!("ThreadDriver stopped");
1284
1285 Ok(())
1286 }
1287
1288 pub fn is_started(&self) -> Result<bool, EspError> {
1290 let inner = self.inner();
1291
1292 let started = *inner.started.lock();
1293
1294 Ok(started)
1295 }
1296
1297 #[allow(clippy::mut_from_ref)]
1300 fn inner(&self) -> ThreadDriverInnerGuard<'_> {
1301 ThreadDriverInnerGuard {
1302 inner: unsafe { &mut *self.inner.get() },
1303 _lock: OtLock::acquire().unwrap(),
1304 }
1305 }
1306
1307 #[cfg(all(esp_idf_openthread_radio_native, esp_idf_soc_ieee802154_supported))]
1310 fn internal_init_coex() -> Result<(), EspError> {
1311 #[cfg(esp_idf_esp_coex_sw_coexist_enable)]
1312 {
1313 esp!(unsafe { esp_coex_wifi_i154_enable() })?;
1314 }
1315
1316 Ok(())
1317 }
1318
1319 fn internal_new(
1320 cfg: esp_openthread_platform_config_t,
1321 _sysloop: EspSystemEventLoop,
1322 nvs: EspDefaultNvsPartition,
1323 mounted_event_fs: Arc<MountedEventfs>,
1324 mode: T,
1325 ) -> Result<Self, EspError> {
1326 let mut inner = {
1327 let mut inner = Box::new_uninit();
1328
1329 unsafe {
1330 ThreadDriverInner::init(inner.as_mut_ptr(), cfg);
1331
1332 inner.assume_init()
1333 }
1334 };
1335
1336 esp!(unsafe { esp_openthread_init(&inner.cfg) })?;
1337
1338 let instance = unsafe { esp_openthread_get_instance() };
1339
1340 #[cfg(not(esp_idf_openthread_radio))]
1341 unsafe {
1342 otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL as _);
1343 }
1344
1345 let srp = &mut inner.srp;
1346
1347 unsafe {
1348 crate::sys::otSrpClientSetCallback(
1349 instance,
1350 Some(OtSrp::plat_c_srp_state_change_callback),
1351 srp as *mut _ as *mut _,
1352 )
1353 }
1354
1355 T::init();
1356
1357 info!("ThreadDriver initialized");
1358
1359 Ok(Self {
1360 inner: UnsafeCell::new(inner),
1361 _nvs: nvs,
1362 _mounted_event_fs: mounted_event_fs,
1363 _mode: mode,
1364 _p: PhantomData,
1365 })
1366 }
1367
1368 fn internal_deinit(&mut self) -> Result<(), EspError> {
1369 let _ = self.stop();
1370
1371 esp!(unsafe { esp_openthread_deinit() })?;
1372
1373 Ok(())
1374 }
1375
1376 extern "C" fn run(arg: *mut core::ffi::c_void) {
1377 {
1378 let _lock = OtLock::acquire().unwrap();
1379
1380 let inner = unsafe { (arg as *mut ThreadDriverInner).as_mut().unwrap() };
1381
1382 *inner.started.lock() = true;
1383 inner.started_condvar.notify_all();
1384 }
1385
1386 unsafe {
1387 esp_openthread_launch_mainloop();
1388 }
1389
1390 {
1391 let _lock = OtLock::acquire().unwrap();
1392
1393 let inner = unsafe { (arg as *mut ThreadDriverInner).as_mut().unwrap() };
1394
1395 *inner.started.lock() = false;
1396 inner.started_condvar.notify_all();
1397 }
1398
1399 unsafe { crate::hal::task::destroy(core::ptr::null_mut()) }
1400 }
1401}
1402
1403impl<T> Drop for ThreadDriver<'_, T>
1404where
1405 T: Mode,
1406{
1407 fn drop(&mut self) {
1408 self.internal_deinit().unwrap();
1409 info!("ThreadDriver deinitialized");
1410 }
1411}
1412
1413unsafe impl<T> Send for ThreadDriver<'_, T> where T: Mode {}
1414unsafe impl<T> Sync for ThreadDriver<'_, T> where T: Mode {}
1415
1416struct ThreadDriverInner {
1417 cfg: esp_openthread_platform_config_t,
1418 dataset_buf: otOperationalDatasetTlvs,
1419 srp: OtSrp,
1420 #[allow(clippy::type_complexity)]
1421 ipv6_cb: Option<Box<Box<dyn FnMut(Ipv6Incoming) + Send + 'static>>>,
1422 #[allow(clippy::type_complexity)]
1423 scan_cb: Option<Box<Box<dyn FnMut(Option<ActiveScanResult>) + Send + 'static>>>,
1424 #[allow(clippy::type_complexity)]
1425 energy_cb: Option<Box<Box<dyn FnMut(Option<EnergyScanResult>) + Send + 'static>>>,
1426 started: Mutex<bool>,
1427 started_condvar: Condvar,
1428}
1429
1430impl ThreadDriverInner {
1431 unsafe fn init(this: *mut Self, cfg: esp_openthread_platform_config_t) {
1432 addr_of_mut!((*this).cfg).write(cfg);
1433 addr_of_mut!((*this).dataset_buf).write_bytes(0, 1);
1434
1435 OtSrp::init(addr_of_mut!((*this).srp));
1436
1437 addr_of_mut!((*this).ipv6_cb).write(None);
1438 addr_of_mut!((*this).scan_cb).write(None);
1439 addr_of_mut!((*this).energy_cb).write(None);
1440 addr_of_mut!((*this).started).write(Mutex::new(false));
1441 addr_of_mut!((*this).started_condvar).write(Condvar::new());
1442 }
1443}
1444
1445struct ThreadDriverInnerGuard<'a> {
1446 inner: &'a mut ThreadDriverInner,
1447 _lock: OtLock,
1448}
1449
1450impl Deref for ThreadDriverInnerGuard<'_> {
1451 type Target = ThreadDriverInner;
1452
1453 fn deref(&self) -> &Self::Target {
1454 self.inner
1455 }
1456}
1457
1458impl DerefMut for ThreadDriverInnerGuard<'_> {
1459 fn deref_mut(&mut self) -> &mut Self::Target {
1460 self.inner
1461 }
1462}
1463
1464struct OtLock(PhantomData<*const ()>);
1465
1466impl OtLock {
1467 pub fn acquire() -> Result<Self, EspError> {
1468 if !unsafe { esp_openthread_lock_acquire(delay::BLOCK) } {
1469 Err(EspError::from_infallible::<ESP_ERR_TIMEOUT>())?;
1470 }
1471
1472 Ok(Self(PhantomData))
1473 }
1474}
1475
1476impl Drop for OtLock {
1477 fn drop(&mut self) {
1478 unsafe {
1479 esp_openthread_lock_release();
1480 }
1481 }
1482}
1483
1484pub trait NetifMode {
1486 fn init(&mut self) -> Result<(), EspError>;
1487 fn deinit(&mut self) -> Result<(), EspError>;
1488}
1489
1490pub struct Node(());
1494
1495impl NetifMode for Node {
1496 fn init(&mut self) -> Result<(), EspError> {
1497 Ok(())
1498 }
1499
1500 fn deinit(&mut self) -> Result<(), EspError> {
1501 Ok(())
1502 }
1503}
1504
1505#[cfg(all(esp_idf_comp_esp_netif_enabled, esp_idf_openthread_border_router))]
1507pub struct BorderRouter(());
1508
1509#[cfg(all(esp_idf_comp_esp_netif_enabled, esp_idf_openthread_border_router))]
1510impl NetifMode for BorderRouter {
1511 fn init(&mut self) -> Result<(), EspError> {
1512 #[cfg(not(esp_idf_version_major = "4"))]
1513 {
1514 esp!(unsafe { esp_openthread_border_router_init() })?;
1515 }
1516
1517 debug!("Border router initialized");
1526
1527 Ok(())
1528 }
1529
1530 fn deinit(&mut self) -> Result<(), EspError> {
1531 esp!(unsafe { esp_openthread_border_router_deinit() })?;
1532
1533 debug!("Border router deinitialized");
1534
1535 Ok(())
1536 }
1537}
1538
1539#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
1550pub struct EspThread<'d, T>
1551where
1552 T: NetifMode,
1553{
1554 driver: ThreadDriver<'d, Host>,
1555 netif: EspNetif,
1556 mode: T,
1557}
1558
1559#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
1560impl<'d> EspThread<'d, Node> {
1561 #[cfg(esp_idf_soc_ieee802154_supported)]
1563 pub fn new<M: crate::hal::modem::ThreadModemPeripheral + 'd>(
1564 modem: M,
1565 sysloop: EspSystemEventLoop,
1566 nvs: EspDefaultNvsPartition,
1567 mounted_event_fs: Arc<MountedEventfs>,
1568 ) -> Result<Self, EspError> {
1569 Self::wrap(ThreadDriver::new(modem, sysloop, nvs, mounted_event_fs)?)
1570 }
1571
1572 #[cfg(not(esp_idf_version_major = "4"))]
1575 #[allow(clippy::too_many_arguments)]
1576 pub fn new_spi<S: crate::hal::spi::Spi + 'd>(
1577 _spi: S,
1578 mosi: impl InputPin + 'd,
1579 miso: impl OutputPin + 'd,
1580 sclk: impl InputPin + OutputPin + 'd,
1581 cs: Option<impl InputPin + OutputPin + 'd>,
1582 intr: Option<impl InputPin + OutputPin + 'd>,
1583 config: &crate::hal::spi::config::Config,
1584 _sysloop: EspSystemEventLoop,
1585 nvs: EspDefaultNvsPartition,
1586 mounted_event_fs: Arc<MountedEventfs>,
1587 ) -> Result<Self, EspError> {
1588 Self::wrap(ThreadDriver::new_spi(
1589 _spi,
1590 mosi,
1591 miso,
1592 sclk,
1593 cs,
1594 intr,
1595 config,
1596 _sysloop,
1597 nvs,
1598 mounted_event_fs,
1599 )?)
1600 }
1601
1602 pub fn new_uart<U: Uart + 'd>(
1605 _uart: U,
1606 tx: impl OutputPin + 'd,
1607 rx: impl InputPin + 'd,
1608 config: &crate::hal::uart::config::Config,
1609 _sysloop: EspSystemEventLoop,
1610 nvs: EspDefaultNvsPartition,
1611 mounted_event_fs: Arc<MountedEventfs>,
1612 ) -> Result<Self, EspError> {
1613 Self::wrap(ThreadDriver::new_uart(
1614 _uart,
1615 tx,
1616 rx,
1617 config,
1618 _sysloop,
1619 nvs,
1620 mounted_event_fs,
1621 )?)
1622 }
1623
1624 pub fn wrap(driver: ThreadDriver<'d, Host>) -> Result<Self, EspError> {
1626 Self::wrap_all(driver, EspNetif::new(NetifStack::Thread)?)
1627 }
1628
1629 pub fn wrap_all(driver: ThreadDriver<'d, Host>, netif: EspNetif) -> Result<Self, EspError> {
1631 Self::internal_init(driver, netif, Node(()))
1632 }
1633}
1634
1635#[cfg(all(esp_idf_comp_esp_netif_enabled, esp_idf_openthread_border_router))]
1636impl<'d> EspThread<'d, BorderRouter> {
1637 #[cfg(not(esp_idf_version_major = "4"))]
1654 pub unsafe fn set_backbone_netif(backbone_netif: Option<&EspNetif>) {
1655 unsafe {
1656 esp_openthread_set_backbone_netif(
1657 backbone_netif
1658 .map(|netif| netif.handle())
1659 .unwrap_or(core::ptr::null_mut()),
1660 );
1661 }
1662 }
1663
1664 #[cfg(esp_idf_soc_ieee802154_supported)]
1666 pub fn new_br<M: crate::hal::modem::ThreadModemPeripheral + 'd>(
1667 modem: M,
1668 sysloop: EspSystemEventLoop,
1669 nvs: EspDefaultNvsPartition,
1670 mounted_event_fs: Arc<MountedEventfs>,
1671 ) -> Result<Self, EspError> {
1672 Self::wrap_br(ThreadDriver::new(modem, sysloop, nvs, mounted_event_fs)?)
1673 }
1674
1675 #[cfg(not(esp_idf_version_major = "4"))]
1678 #[allow(clippy::too_many_arguments)]
1679 pub fn new_br_spi<S: crate::hal::spi::Spi + 'd>(
1680 _spi: S,
1681 mosi: impl InputPin + 'd,
1682 miso: impl OutputPin + 'd,
1683 sclk: impl InputPin + OutputPin + 'd,
1684 cs: Option<impl InputPin + OutputPin + 'd>,
1685 intr: Option<impl InputPin + OutputPin + 'd>,
1686 config: &crate::hal::spi::config::Config,
1687 _sysloop: EspSystemEventLoop,
1688 nvs: EspDefaultNvsPartition,
1689 mounted_event_fs: Arc<MountedEventfs>,
1690 ) -> Result<Self, EspError> {
1691 Self::wrap_br(ThreadDriver::new_spi(
1692 _spi,
1693 mosi,
1694 miso,
1695 sclk,
1696 cs,
1697 intr,
1698 config,
1699 _sysloop,
1700 nvs,
1701 mounted_event_fs,
1702 )?)
1703 }
1704
1705 #[allow(clippy::too_many_arguments)]
1708 pub fn new_br_uart<U: Uart + 'd>(
1709 _uart: U,
1710 tx: impl OutputPin + 'd,
1711 rx: impl InputPin + 'd,
1712 config: &crate::hal::uart::config::Config,
1713 _sysloop: EspSystemEventLoop,
1714 nvs: EspDefaultNvsPartition,
1715 mounted_event_fs: Arc<MountedEventfs>,
1716 ) -> Result<Self, EspError> {
1717 Self::wrap_br(ThreadDriver::new_uart(
1718 _uart,
1719 tx,
1720 rx,
1721 config,
1722 _sysloop,
1723 nvs,
1724 mounted_event_fs,
1725 )?)
1726 }
1727
1728 pub fn wrap_br(driver: ThreadDriver<'d, Host>) -> Result<Self, EspError> {
1731 Self::wrap_br_all(driver, EspNetif::new(NetifStack::Thread)?)
1732 }
1733
1734 pub fn wrap_br_all(driver: ThreadDriver<'d, Host>, netif: EspNetif) -> Result<Self, EspError> {
1737 Self::internal_init(driver, netif, BorderRouter(()))
1738 }
1739}
1740
1741#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
1742impl<'d, T> EspThread<'d, T>
1743where
1744 T: NetifMode,
1745{
1746 pub fn driver(&self) -> &ThreadDriver<'d, Host> {
1748 &self.driver
1749 }
1750
1751 pub fn driver_mut(&mut self) -> &mut ThreadDriver<'d, Host> {
1753 &mut self.driver
1754 }
1755
1756 #[cfg(all(esp_idf_openthread_radio_native, esp_idf_soc_ieee802154_supported))]
1758 pub fn init_coex(&mut self) -> Result<(), EspError> {
1759 self.driver.init_coex()
1760 }
1761
1762 pub fn netif(&self) -> &EspNetif {
1764 &self.netif
1765 }
1766
1767 pub fn enable_ipv6(&self, enabled: bool) -> Result<(), EspError> {
1769 self.driver().enable_ipv6(enabled)
1770 }
1771
1772 pub fn enable_thread(&self, enabled: bool) -> Result<(), EspError> {
1776 self.driver().enable_thread(enabled)
1777 }
1778
1779 pub fn role(&self) -> Result<Role, EspError> {
1781 self.driver().role()
1782 }
1783
1784 pub fn tod(&self, buf: &mut [u8]) -> Result<usize, EspError> {
1790 self.driver().tod(buf)
1791 }
1792
1793 pub fn pending_tod(&self, buf: &mut [u8]) -> Result<usize, EspError> {
1799 self.driver().pending_tod(buf)
1800 }
1801
1802 pub fn set_tod(&self, tod: &[u8]) -> Result<(), EspError> {
1806 self.driver().set_tod(tod)
1807 }
1808
1809 pub fn set_tod_hexstr(&self, tod: &str) -> Result<(), EspError> {
1813 self.driver().set_tod_hexstr(tod)
1814 }
1815
1816 pub fn set_pending_tod(&self, tod: &[u8]) -> Result<(), EspError> {
1820 self.driver().set_pending_tod(tod)
1821 }
1822
1823 pub fn set_pending_tod_hexstr(&self, tod: &str) -> Result<(), EspError> {
1827 self.driver().set_pending_tod_hexstr(tod)
1828 }
1829
1830 #[cfg(not(esp_idf_version_major = "4"))]
1834 pub fn set_tod_from_cfg(&self) -> Result<(), EspError> {
1835 self.driver().set_tod_from_cfg()
1836 }
1837
1838 pub fn scan<F: FnMut(Option<ActiveScanResult>) + Send + 'static>(
1843 &self,
1844 callback: F,
1845 ) -> Result<(), EspError> {
1846 self.driver().scan(callback)
1847 }
1848
1849 pub fn is_scan_in_progress(&self) -> Result<bool, EspError> {
1851 self.driver().is_scan_in_progress()
1852 }
1853
1854 pub fn energy_scan<F: FnMut(Option<EnergyScanResult>) + Send + 'static>(
1859 &self,
1860 callback: F,
1861 ) -> Result<(), EspError> {
1862 self.driver().energy_scan(callback)
1863 }
1864
1865 pub fn is_energy_scan_in_progress(&self) -> Result<bool, EspError> {
1867 self.driver().is_energy_scan_in_progress()
1868 }
1869
1870 pub fn start(&mut self) -> Result<(), EspError> {
1874 self.driver_mut().start()
1875 }
1876
1877 pub fn stop(&mut self) -> Result<(), EspError> {
1881 self.driver_mut().start()
1882 }
1883
1884 pub fn is_started(&self) -> Result<bool, EspError> {
1886 self.driver().is_started()
1887 }
1888
1889 fn internal_init(
1892 driver: ThreadDriver<'d, Host>,
1893 netif: EspNetif,
1894 mut mode: T,
1895 ) -> Result<Self, EspError> {
1896 let inner = driver.inner();
1897
1898 let glue = unsafe { esp_openthread_netif_glue_init(&inner.cfg) };
1899 assert!(!glue.is_null());
1900
1901 esp!(unsafe { esp_netif_attach(netif.handle() as *mut _, glue) })?;
1902
1903 mode.init()?;
1904
1905 info!("EspThread initialized");
1906
1907 Ok(Self {
1908 netif,
1909 mode,
1910 driver,
1911 })
1912 }
1913
1914 fn internal_deinit(&mut self) -> Result<(), EspError> {
1915 let _lock = self.driver.inner();
1916
1917 self.mode.deinit()?;
1918
1919 unsafe {
1920 esp_openthread_netif_glue_deinit();
1921 }
1922
1923 Ok(())
1924 }
1925}
1926
1927#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
1928impl<T> Drop for EspThread<'_, T>
1929where
1930 T: NetifMode,
1931{
1932 fn drop(&mut self) {
1933 self.internal_deinit().unwrap();
1934 info!("EspThread deinitialized");
1935 }
1936}
1937
1938#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
1939unsafe impl<T> Send for EspThread<'_, T> where T: NetifMode {}
1940#[cfg(all(esp_idf_comp_esp_netif_enabled, not(esp_idf_openthread_radio)))]
1941unsafe impl<T> Sync for EspThread<'_, T> where T: NetifMode {}
1942
1943#[derive(Copy, Clone, Debug)]
1945pub enum ThreadEvent {
1946 Started,
1948 Stopped,
1950 #[cfg(not(esp_idf_version_major = "4"))]
1952 Detached,
1953 #[cfg(not(esp_idf_version_major = "4"))]
1955 Attached,
1956 #[cfg(not(esp_idf_version_major = "4"))]
1958 RoleChanged {
1959 current_role: Role,
1960 previous_role: Role,
1961 },
1962 IfUp,
1964 IfDown,
1966 GotIpv6,
1968 LostIpv6,
1970 MulticastJoined,
1972 MulticastLeft,
1974 #[cfg(not(esp_idf_version_major = "4"))]
1976 TrelIpv6Added,
1977 #[cfg(not(esp_idf_version_major = "4"))]
1979 TrelIpv6Removed,
1980 #[cfg(not(esp_idf_version_major = "4"))]
1982 TrelMulticastJoined,
1983 #[cfg(all(
1986 not(esp_idf_version_major = "4"),
1987 not(all(esp_idf_version_major = "5", esp_idf_version_minor = "0"))
1988 ))]
1989 DnsServerSet,
1990 #[cfg(any(
1993 not(any(esp_idf_version_major = "4", esp_idf_version_major = "5")),
1994 all(
1995 esp_idf_version_major = "5",
1996 not(esp_idf_version_minor = "0"),
1997 not(esp_idf_version_minor = "1"),
1998 not(all(
1999 esp_idf_version_minor = "2",
2000 any(esp_idf_version_patch = "0", esp_idf_version_patch = "1")
2001 )),
2002 ),
2003 ))]
2004 MeshcopEPublishStarted,
2005 #[cfg(any(
2008 not(any(esp_idf_version_major = "4", esp_idf_version_major = "5")),
2009 all(
2010 esp_idf_version_major = "5",
2011 not(esp_idf_version_minor = "0"),
2012 not(esp_idf_version_minor = "1"),
2013 not(all(
2014 esp_idf_version_minor = "2",
2015 any(esp_idf_version_patch = "0", esp_idf_version_patch = "1")
2016 )),
2017 ),
2018 ))]
2019 MeshcopERemoveStarted,
2020 #[cfg(any(
2021 esp_idf_version_patch_at_least_5_1_6,
2022 esp_idf_version_patch_at_least_5_2_4,
2023 esp_idf_version_patch_at_least_5_3_2,
2024 esp_idf_version_at_least_5_4_0
2025 ))]
2026 DatasetChanged,
2027
2028 Other(i32),
2034}
2035
2036unsafe impl EspEventSource for ThreadEvent {
2037 fn source() -> Option<&'static ffi::CStr> {
2038 Some(unsafe { ffi::CStr::from_ptr(OPENTHREAD_EVENT) })
2039 }
2040}
2041
2042impl EspEventDeserializer for ThreadEvent {
2043 type Data<'d> = ThreadEvent;
2044
2045 #[allow(non_upper_case_globals, non_snake_case)]
2046 fn deserialize(data: &crate::eventloop::EspEvent) -> ThreadEvent {
2047 let event_id = data.event_id as u32;
2048
2049 match event_id {
2050 esp_openthread_event_t_OPENTHREAD_EVENT_START => ThreadEvent::Started,
2051 esp_openthread_event_t_OPENTHREAD_EVENT_STOP => ThreadEvent::Stopped,
2052 #[cfg(not(esp_idf_version_major = "4"))]
2053 esp_openthread_event_t_OPENTHREAD_EVENT_DETACHED => ThreadEvent::Detached,
2054 #[cfg(not(esp_idf_version_major = "4"))]
2055 esp_openthread_event_t_OPENTHREAD_EVENT_ATTACHED => ThreadEvent::Attached,
2056 #[cfg(not(esp_idf_version_major = "4"))]
2057 esp_openthread_event_t_OPENTHREAD_EVENT_ROLE_CHANGED => {
2058 let payload = unsafe {
2059 (data.payload.unwrap() as *const _
2060 as *const esp_openthread_role_changed_event_t)
2061 .as_ref()
2062 }
2063 .unwrap();
2064
2065 ThreadEvent::RoleChanged {
2066 current_role: payload.current_role.into(),
2067 previous_role: payload.previous_role.into(),
2068 }
2069 }
2070 esp_openthread_event_t_OPENTHREAD_EVENT_IF_UP => ThreadEvent::IfUp,
2071 esp_openthread_event_t_OPENTHREAD_EVENT_IF_DOWN => ThreadEvent::IfDown,
2072 esp_openthread_event_t_OPENTHREAD_EVENT_GOT_IP6 => ThreadEvent::GotIpv6,
2073 esp_openthread_event_t_OPENTHREAD_EVENT_LOST_IP6 => ThreadEvent::LostIpv6,
2074 esp_openthread_event_t_OPENTHREAD_EVENT_MULTICAST_GROUP_JOIN => {
2075 ThreadEvent::MulticastJoined
2076 }
2077 esp_openthread_event_t_OPENTHREAD_EVENT_MULTICAST_GROUP_LEAVE => {
2078 ThreadEvent::MulticastLeft
2079 }
2080 #[cfg(not(esp_idf_version_major = "4"))]
2081 esp_openthread_event_t_OPENTHREAD_EVENT_TREL_ADD_IP6 => ThreadEvent::TrelIpv6Added,
2082 #[cfg(not(esp_idf_version_major = "4"))]
2083 esp_openthread_event_t_OPENTHREAD_EVENT_TREL_REMOVE_IP6 => ThreadEvent::TrelIpv6Removed,
2084 #[cfg(not(esp_idf_version_major = "4"))]
2085 esp_openthread_event_t_OPENTHREAD_EVENT_TREL_MULTICAST_GROUP_JOIN => {
2086 ThreadEvent::TrelMulticastJoined
2087 }
2088 #[cfg(all(
2089 not(esp_idf_version_major = "4"),
2090 not(all(esp_idf_version_major = "5", esp_idf_version_minor = "0"))
2091 ))]
2092 esp_openthread_event_t_OPENTHREAD_EVENT_SET_DNS_SERVER => ThreadEvent::DnsServerSet,
2093 #[cfg(any(
2095 not(any(esp_idf_version_major = "4", esp_idf_version_major = "5")),
2096 all(
2097 esp_idf_version_major = "5",
2098 not(esp_idf_version_minor = "0"),
2099 not(esp_idf_version_minor = "1"),
2100 not(all(
2101 esp_idf_version_minor = "2",
2102 any(esp_idf_version_patch = "0", esp_idf_version_patch = "1")
2103 )),
2104 ),
2105 ))]
2106 esp_openthread_event_t_OPENTHREAD_EVENT_PUBLISH_MESHCOP_E => {
2107 ThreadEvent::MeshcopEPublishStarted
2108 }
2109 #[cfg(any(
2111 not(any(esp_idf_version_major = "4", esp_idf_version_major = "5")),
2112 all(
2113 esp_idf_version_major = "5",
2114 not(esp_idf_version_minor = "0"),
2115 not(esp_idf_version_minor = "1"),
2116 not(all(
2117 esp_idf_version_minor = "2",
2118 any(esp_idf_version_patch = "0", esp_idf_version_patch = "1")
2119 )),
2120 ),
2121 ))]
2122 esp_openthread_event_t_OPENTHREAD_EVENT_REMOVE_MESHCOP_E => {
2123 ThreadEvent::MeshcopERemoveStarted
2124 }
2125 #[cfg(any(
2126 esp_idf_version_patch_at_least_5_1_6,
2127 esp_idf_version_patch_at_least_5_2_4,
2128 esp_idf_version_patch_at_least_5_3_2,
2129 esp_idf_version_at_least_5_4_0
2130 ))]
2131 esp_openthread_event_t_OPENTHREAD_EVENT_DATASET_CHANGED => ThreadEvent::DatasetChanged,
2132 _ => {
2133 warn!("ThreadEvent: unknown event ID {event_id}, ignoring");
2134 ThreadEvent::Other(event_id as i32)
2135 }
2136 }
2137 }
2138}