1pub mod event;
4pub(crate) mod os_adapter;
5pub(crate) mod state;
6use alloc::collections::vec_deque::VecDeque;
7use core::{
8 fmt::Debug,
9 marker::PhantomData,
10 mem::{self, MaybeUninit},
11 ptr::addr_of,
12 task::Poll,
13 time::Duration,
14};
15
16use enumset::{EnumSet, EnumSetType};
17use esp_hal::{asynch::AtomicWaker, sync::Locked};
18use esp_wifi_sys::include::{
19 esp_eap_client_clear_ca_cert,
20 esp_eap_client_clear_certificate_and_key,
21 esp_eap_client_clear_identity,
22 esp_eap_client_clear_new_password,
23 esp_eap_client_clear_password,
24 esp_eap_client_clear_username,
25 esp_eap_client_set_ca_cert,
26 esp_eap_client_set_certificate_and_key,
27 esp_eap_client_set_disable_time_check,
28 esp_eap_client_set_fast_params,
29 esp_eap_client_set_identity,
30 esp_eap_client_set_new_password,
31 esp_eap_client_set_pac_file,
32 esp_eap_client_set_password,
33 esp_eap_client_set_ttls_phase2_method,
34 esp_eap_client_set_username,
35 esp_eap_fast_config,
36 esp_wifi_sta_enterprise_enable,
37 wifi_pkt_rx_ctrl_t,
38 wifi_scan_channel_bitmap_t,
39 WIFI_PROTOCOL_11AX,
40 WIFI_PROTOCOL_11B,
41 WIFI_PROTOCOL_11G,
42 WIFI_PROTOCOL_11N,
43 WIFI_PROTOCOL_LR,
44};
45#[cfg(feature = "sniffer")]
46use esp_wifi_sys::include::{
47 esp_wifi_80211_tx,
48 esp_wifi_set_promiscuous,
49 esp_wifi_set_promiscuous_rx_cb,
50 wifi_promiscuous_pkt_t,
51 wifi_promiscuous_pkt_type_t,
52};
53use num_derive::FromPrimitive;
54#[doc(hidden)]
55pub(crate) use os_adapter::*;
56#[cfg(feature = "sniffer")]
57use portable_atomic::AtomicBool;
58use portable_atomic::{AtomicUsize, Ordering};
59#[cfg(feature = "serde")]
60use serde::{Deserialize, Serialize};
61#[cfg(feature = "smoltcp")]
62use smoltcp::phy::{Device, DeviceCapabilities, RxToken, TxToken};
63pub use state::*;
64
65use crate::{
66 common_adapter::*,
67 config::PowerSaveMode,
68 esp_wifi_result,
69 hal::ram,
70 wifi::private::EspWifiPacketBuffer,
71 EspWifiController,
72};
73
74const MTU: usize = crate::CONFIG.mtu;
75
76#[cfg(coex)]
77use include::{coex_adapter_funcs_t, coex_pre_init, esp_coex_adapter_register};
78
79#[cfg(all(feature = "csi", esp32c6))]
80use crate::binary::include::wifi_csi_acquire_config_t;
81#[cfg(feature = "csi")]
82pub use crate::binary::include::wifi_csi_info_t;
83#[cfg(feature = "csi")]
84use crate::binary::include::{
85 esp_wifi_set_csi,
86 esp_wifi_set_csi_config,
87 esp_wifi_set_csi_rx_cb,
88 wifi_csi_config_t,
89};
90use crate::binary::{
91 c_types,
92 include::{
93 self,
94 __BindgenBitfieldUnit,
95 esp_err_t,
96 esp_interface_t_ESP_IF_WIFI_AP,
97 esp_interface_t_ESP_IF_WIFI_STA,
98 esp_supplicant_deinit,
99 esp_supplicant_init,
100 esp_wifi_connect,
101 esp_wifi_deinit_internal,
102 esp_wifi_disconnect,
103 esp_wifi_get_mode,
104 esp_wifi_init_internal,
105 esp_wifi_internal_free_rx_buffer,
106 esp_wifi_internal_reg_rxcb,
107 esp_wifi_internal_tx,
108 esp_wifi_scan_start,
109 esp_wifi_set_config,
110 esp_wifi_set_country,
111 esp_wifi_set_mode,
112 esp_wifi_set_protocol,
113 esp_wifi_set_tx_done_cb,
114 esp_wifi_start,
115 esp_wifi_stop,
116 g_wifi_default_wpa_crypto_funcs,
117 wifi_active_scan_time_t,
118 wifi_ap_config_t,
119 wifi_auth_mode_t,
120 wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
121 wifi_config_t,
122 wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
123 wifi_country_t,
124 wifi_init_config_t,
125 wifi_interface_t,
126 wifi_interface_t_WIFI_IF_AP,
127 wifi_interface_t_WIFI_IF_STA,
128 wifi_mode_t,
129 wifi_mode_t_WIFI_MODE_AP,
130 wifi_mode_t_WIFI_MODE_APSTA,
131 wifi_mode_t_WIFI_MODE_NULL,
132 wifi_mode_t_WIFI_MODE_STA,
133 wifi_osi_funcs_t,
134 wifi_pmf_config_t,
135 wifi_scan_config_t,
136 wifi_scan_threshold_t,
137 wifi_scan_time_t,
138 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
139 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
140 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
141 wifi_sta_config_t,
142 wpa_crypto_funcs_t,
143 ESP_WIFI_OS_ADAPTER_MAGIC,
144 ESP_WIFI_OS_ADAPTER_VERSION,
145 WIFI_INIT_CONFIG_MAGIC,
146 },
147};
148
149#[derive(EnumSetType, Debug, PartialOrd)]
151#[cfg_attr(feature = "defmt", derive(defmt::Format))]
152#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
153#[derive(Default)]
154#[allow(clippy::upper_case_acronyms)] pub enum AuthMethod {
156 None,
158
159 WEP,
161
162 WPA,
164
165 #[default]
167 WPA2Personal,
168
169 WPAWPA2Personal,
171
172 WPA2Enterprise,
174
175 WPA3Personal,
177
178 WPA2WPA3Personal,
180
181 WAPIPersonal,
183}
184
185#[derive(EnumSetType, Debug, PartialOrd)]
187#[cfg_attr(feature = "defmt", derive(defmt::Format))]
188#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
189#[derive(Default)]
190pub enum Protocol {
191 P802D11B,
193
194 P802D11BG,
196
197 #[default]
199 P802D11BGN,
200
201 P802D11BGNLR,
203
204 P802D11LR,
206
207 P802D11BGNAX,
209}
210
211#[derive(EnumSetType, Debug, PartialOrd)]
213#[cfg_attr(feature = "defmt", derive(defmt::Format))]
214#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
215#[derive(Default)]
216pub enum SecondaryChannel {
217 #[default]
220 None,
221
222 Above,
224
225 Below,
227}
228
229#[derive(Clone, Debug, Default, PartialEq, Eq)]
231#[cfg_attr(feature = "defmt", derive(defmt::Format))]
232#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
233pub struct AccessPointInfo {
234 pub ssid: heapless::String<32>,
236
237 pub bssid: [u8; 6],
239
240 pub channel: u8,
242
243 pub secondary_channel: SecondaryChannel,
245
246 pub signal_strength: i8,
248
249 #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
251 pub protocols: EnumSet<Protocol>,
252
253 pub auth_method: Option<AuthMethod>,
255}
256
257#[derive(Clone, Debug, PartialEq, Eq)]
259#[cfg_attr(feature = "defmt", derive(defmt::Format))]
260#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
261pub struct AccessPointConfiguration {
262 pub ssid: heapless::String<32>,
264
265 pub ssid_hidden: bool,
267
268 pub channel: u8,
270
271 pub secondary_channel: Option<u8>,
273
274 #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
276 pub protocols: EnumSet<Protocol>,
277
278 pub auth_method: AuthMethod,
280
281 pub password: heapless::String<64>,
283
284 pub max_connections: u16,
286}
287
288impl Default for AccessPointConfiguration {
289 fn default() -> Self {
290 Self {
291 ssid: unwrap!("iot-device".try_into()),
292 ssid_hidden: false,
293 channel: 1,
294 secondary_channel: None,
295 protocols: Protocol::P802D11B | Protocol::P802D11BG | Protocol::P802D11BGN,
296 auth_method: AuthMethod::None,
297 password: heapless::String::new(),
298 max_connections: 255,
299 }
300 }
301}
302
303#[derive(Clone, PartialEq, Eq)]
305#[cfg_attr(feature = "defmt", derive(defmt::Format))]
306#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
307pub struct ClientConfiguration {
308 pub ssid: heapless::String<32>,
310
311 pub bssid: Option<[u8; 6]>,
313
314 pub auth_method: AuthMethod,
317
318 pub password: heapless::String<64>,
320
321 pub channel: Option<u8>,
323}
324
325impl Debug for ClientConfiguration {
326 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
327 f.debug_struct("ClientConfiguration")
328 .field("ssid", &self.ssid)
329 .field("bssid", &self.bssid)
330 .field("auth_method", &self.auth_method)
331 .field("channel", &self.channel)
332 .finish()
333 }
334}
335
336impl Default for ClientConfiguration {
337 fn default() -> Self {
338 ClientConfiguration {
339 ssid: heapless::String::new(),
340 bssid: None,
341 auth_method: Default::default(),
342 password: heapless::String::new(),
343 channel: None,
344 }
345 }
346}
347
348#[cfg(feature = "csi")]
349pub(crate) trait CsiCallback: FnMut(crate::binary::include::wifi_csi_info_t) {}
350
351#[cfg(feature = "csi")]
352impl<T> CsiCallback for T where T: FnMut(crate::binary::include::wifi_csi_info_t) {}
353
354#[cfg(feature = "csi")]
355unsafe extern "C" fn csi_rx_cb<C: CsiCallback>(
356 ctx: *mut crate::wifi::c_types::c_void,
357 data: *mut crate::binary::include::wifi_csi_info_t,
358) {
359 let csi_callback = unsafe { &mut *(ctx as *mut C) };
360 csi_callback(*data);
361}
362
363#[derive(Clone, PartialEq, Eq)]
364#[cfg(all(not(esp32c6), feature = "csi"))]
367pub struct CsiConfig {
368 pub lltf_en: bool,
370 pub htltf_en: bool,
372 pub stbc_htltf2_en: bool,
375 pub ltf_merge_en: bool,
378 pub channel_filter_en: bool,
381 pub manu_scale: bool,
385 pub shift: u8,
388 pub dump_ack_en: bool,
390}
391
392#[derive(Clone, PartialEq, Eq)]
393#[cfg(all(esp32c6, feature = "csi"))]
394pub struct CsiConfig {
396 pub enable: u32,
398 pub acquire_csi_legacy: u32,
400 pub acquire_csi_ht20: u32,
402 pub acquire_csi_ht40: u32,
404 pub acquire_csi_su: u32,
406 pub acquire_csi_mu: u32,
408 pub acquire_csi_dcm: u32,
410 pub acquire_csi_beamformed: u32,
412 pub acquire_csi_he_stbc: u32,
416 pub val_scale_cfg: u32,
418 pub dump_ack_en: u32,
420 pub reserved: u32,
422}
423
424#[cfg(feature = "csi")]
425impl Default for CsiConfig {
426 #[cfg(not(esp32c6))]
427 fn default() -> Self {
428 Self {
429 lltf_en: true,
430 htltf_en: true,
431 stbc_htltf2_en: true,
432 ltf_merge_en: true,
433 channel_filter_en: true,
434 manu_scale: false,
435 shift: 0,
436 dump_ack_en: false,
437 }
438 }
439
440 #[cfg(esp32c6)]
441 fn default() -> Self {
442 Self {
444 enable: 1,
445 acquire_csi_legacy: 1,
446 acquire_csi_ht20: 1,
447 acquire_csi_ht40: 1,
448 acquire_csi_su: 1,
449 acquire_csi_mu: 1,
450 acquire_csi_dcm: 1,
451 acquire_csi_beamformed: 1,
452 acquire_csi_he_stbc: 2,
453 val_scale_cfg: 2,
454 dump_ack_en: 1,
455 reserved: 19,
456 }
457 }
458}
459
460#[cfg(feature = "csi")]
461impl From<CsiConfig> for wifi_csi_config_t {
462 fn from(config: CsiConfig) -> Self {
463 #[cfg(not(esp32c6))]
464 {
465 wifi_csi_config_t {
466 lltf_en: config.lltf_en,
467 htltf_en: config.htltf_en,
468 stbc_htltf2_en: config.stbc_htltf2_en,
469 ltf_merge_en: config.ltf_merge_en,
470 channel_filter_en: config.channel_filter_en,
471 manu_scale: config.manu_scale,
472 shift: config.shift,
473 dump_ack_en: config.dump_ack_en,
474 }
475 }
476 #[cfg(esp32c6)]
477 {
478 wifi_csi_acquire_config_t {
479 _bitfield_align_1: [0; 0],
480 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
481 config.enable,
482 config.acquire_csi_legacy,
483 config.acquire_csi_ht20,
484 config.acquire_csi_ht40,
485 config.acquire_csi_su,
486 config.acquire_csi_mu,
487 config.acquire_csi_dcm,
488 config.acquire_csi_beamformed,
489 config.acquire_csi_he_stbc,
490 config.val_scale_cfg,
491 config.dump_ack_en,
492 config.reserved,
493 ),
494 }
495 }
496 }
497}
498
499#[cfg(feature = "csi")]
500impl CsiConfig {
501 pub(crate) fn apply_config(&self) -> Result<(), WifiError> {
503 let conf: wifi_csi_config_t = self.clone().into();
504
505 unsafe {
506 esp_wifi_result!(esp_wifi_set_csi_config(&conf))?;
507 }
508 Ok(())
509 }
510
511 pub(crate) fn set_receive_cb<C: CsiCallback>(&mut self, cb: C) -> Result<(), WifiError> {
514 let cb = alloc::boxed::Box::new(cb);
515 let cb_ptr = alloc::boxed::Box::into_raw(cb) as *mut crate::wifi::c_types::c_void;
516
517 unsafe {
518 esp_wifi_result!(esp_wifi_set_csi_rx_cb(Some(csi_rx_cb::<C>), cb_ptr))?;
519 }
520 Ok(())
521 }
522
523 pub(crate) fn set_csi(&self, enable: bool) -> Result<(), WifiError> {
525 unsafe {
527 esp_wifi_result!(esp_wifi_set_csi(enable))?;
528 }
529 Ok(())
530 }
531}
532
533#[derive(Clone, Debug, PartialEq, Eq)]
535#[cfg_attr(feature = "defmt", derive(defmt::Format))]
536#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
537pub struct EapFastConfig {
538 pub fast_provisioning: u8,
540 pub fast_max_pac_list_len: u8,
542 pub fast_pac_format_binary: bool,
544}
545
546#[derive(Debug, Clone, PartialEq, Eq)]
548#[cfg_attr(feature = "defmt", derive(defmt::Format))]
549#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
550pub enum TtlsPhase2Method {
551 Eap,
553
554 Mschapv2,
556
557 Mschap,
559
560 Pap,
562
563 Chap,
565}
566
567impl TtlsPhase2Method {
568 fn to_raw(&self) -> u32 {
570 match self {
571 TtlsPhase2Method::Eap => {
572 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_EAP
573 }
574 TtlsPhase2Method::Mschapv2 => {
575 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAPV2
576 }
577 TtlsPhase2Method::Mschap => {
578 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAP
579 }
580 TtlsPhase2Method::Pap => {
581 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_PAP
582 }
583 TtlsPhase2Method::Chap => {
584 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_CHAP
585 }
586 }
587 }
588}
589
590#[derive(Clone, PartialEq, Eq)]
592#[cfg_attr(feature = "defmt", derive(defmt::Format))]
593#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
594pub struct EapClientConfiguration {
595 pub ssid: heapless::String<32>,
597
598 pub bssid: Option<[u8; 6]>,
600
601 pub auth_method: AuthMethod,
604
605 pub identity: Option<heapless::String<128>>,
607
608 pub username: Option<heapless::String<128>>,
611
612 pub password: Option<heapless::String<64>>,
614
615 pub new_password: Option<heapless::String<64>>,
618
619 pub eap_fast_config: Option<EapFastConfig>,
621
622 pub pac_file: Option<&'static [u8]>,
624
625 pub time_check: bool,
628
629 pub ca_cert: Option<&'static [u8]>,
632
633 #[allow(clippy::type_complexity)]
636 pub certificate_and_key: Option<(&'static [u8], &'static [u8], Option<&'static [u8]>)>,
637
638 pub ttls_phase2_method: Option<TtlsPhase2Method>,
640
641 pub channel: Option<u8>,
643}
644
645impl Debug for EapClientConfiguration {
646 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
647 f.debug_struct("EapClientConfiguration")
648 .field("ssid", &self.ssid)
649 .field("bssid", &self.bssid)
650 .field("auth_method", &self.auth_method)
651 .field("channel", &self.channel)
652 .field("identity", &self.identity)
653 .field("username", &self.username)
654 .field("eap_fast_config", &self.eap_fast_config)
655 .field("time_check", &self.time_check)
656 .field("pac_file set", &self.pac_file.is_some())
657 .field("ca_cert set", &self.ca_cert.is_some())
658 .field(
659 "certificate_and_key set",
660 &self.certificate_and_key.is_some(),
661 )
662 .field("ttls_phase2_method", &self.ttls_phase2_method)
663 .finish()
664 }
665}
666
667impl Default for EapClientConfiguration {
668 fn default() -> Self {
669 EapClientConfiguration {
670 ssid: heapless::String::new(),
671 bssid: None,
672 auth_method: AuthMethod::WPA2Enterprise,
673 identity: None,
674 username: None,
675 password: None,
676 channel: None,
677 eap_fast_config: None,
678 time_check: false,
679 new_password: None,
680 pac_file: None,
681 ca_cert: None,
682 certificate_and_key: None,
683 ttls_phase2_method: None,
684 }
685 }
686}
687
688#[derive(EnumSetType, Debug, PartialOrd)]
690#[cfg_attr(feature = "defmt", derive(defmt::Format))]
691#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
692pub enum Capability {
693 Client,
695
696 AccessPoint,
699
700 Mixed,
703}
704
705#[derive(Clone, Debug, PartialEq, Eq)]
707#[cfg_attr(feature = "defmt", derive(defmt::Format))]
708#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
709#[derive(Default)]
710#[allow(clippy::large_enum_variant)]
711pub enum Configuration {
712 #[default]
714 None,
715
716 Client(ClientConfiguration),
718
719 AccessPoint(AccessPointConfiguration),
721
722 Mixed(ClientConfiguration, AccessPointConfiguration),
724
725 #[cfg_attr(feature = "serde", serde(skip))]
727 EapClient(EapClientConfiguration),
728}
729
730impl Configuration {
731 pub fn as_client_conf_ref(&self) -> Option<&ClientConfiguration> {
733 match self {
734 Self::Client(client_conf) | Self::Mixed(client_conf, _) => Some(client_conf),
735 _ => None,
736 }
737 }
738
739 pub fn as_ap_conf_ref(&self) -> Option<&AccessPointConfiguration> {
741 match self {
742 Self::AccessPoint(ap_conf) | Self::Mixed(_, ap_conf) => Some(ap_conf),
743 _ => None,
744 }
745 }
746
747 pub fn as_client_conf_mut(&mut self) -> &mut ClientConfiguration {
750 match self {
751 Self::Client(client_conf) => client_conf,
752 Self::Mixed(_, _) => {
753 let prev = mem::replace(self, Self::None);
754 match prev {
755 Self::Mixed(client_conf, _) => {
756 *self = Self::Client(client_conf);
757 self.as_client_conf_mut()
758 }
759 _ => unreachable!(),
760 }
761 }
762 _ => {
763 *self = Self::Client(Default::default());
764 self.as_client_conf_mut()
765 }
766 }
767 }
768
769 pub fn as_ap_conf_mut(&mut self) -> &mut AccessPointConfiguration {
772 match self {
773 Self::AccessPoint(ap_conf) => ap_conf,
774 Self::Mixed(_, _) => {
775 let prev = mem::replace(self, Self::None);
776 match prev {
777 Self::Mixed(_, ap_conf) => {
778 *self = Self::AccessPoint(ap_conf);
779 self.as_ap_conf_mut()
780 }
781 _ => unreachable!(),
782 }
783 }
784 _ => {
785 *self = Self::AccessPoint(Default::default());
786 self.as_ap_conf_mut()
787 }
788 }
789 }
790
791 pub fn as_mixed_conf_mut(
794 &mut self,
795 ) -> (&mut ClientConfiguration, &mut AccessPointConfiguration) {
796 match self {
797 Self::Mixed(client_conf, ref mut ap_conf) => (client_conf, ap_conf),
798 Self::AccessPoint(_) => {
799 let prev = mem::replace(self, Self::None);
800 match prev {
801 Self::AccessPoint(ap_conf) => {
802 *self = Self::Mixed(Default::default(), ap_conf);
803 self.as_mixed_conf_mut()
804 }
805 _ => unreachable!(),
806 }
807 }
808 Self::Client(_) => {
809 let prev = mem::replace(self, Self::None);
810 match prev {
811 Self::Client(client_conf) => {
812 *self = Self::Mixed(client_conf, Default::default());
813 self.as_mixed_conf_mut()
814 }
815 _ => unreachable!(),
816 }
817 }
818 _ => {
819 *self = Self::Mixed(Default::default(), Default::default());
820 self.as_mixed_conf_mut()
821 }
822 }
823 }
824}
825
826trait AuthMethodExt {
827 fn to_raw(&self) -> wifi_auth_mode_t;
828 fn from_raw(raw: wifi_auth_mode_t) -> Self;
829}
830
831impl AuthMethodExt for AuthMethod {
832 fn to_raw(&self) -> wifi_auth_mode_t {
833 match self {
834 AuthMethod::None => include::wifi_auth_mode_t_WIFI_AUTH_OPEN,
835 AuthMethod::WEP => include::wifi_auth_mode_t_WIFI_AUTH_WEP,
836 AuthMethod::WPA => include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
837 AuthMethod::WPA2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
838 AuthMethod::WPAWPA2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK,
839 AuthMethod::WPA2Enterprise => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE,
840 AuthMethod::WPA3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
841 AuthMethod::WPA2WPA3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK,
842 AuthMethod::WAPIPersonal => include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
843 }
844 }
845
846 fn from_raw(raw: wifi_auth_mode_t) -> Self {
847 match raw {
848 include::wifi_auth_mode_t_WIFI_AUTH_OPEN => AuthMethod::None,
849 include::wifi_auth_mode_t_WIFI_AUTH_WEP => AuthMethod::WEP,
850 include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => AuthMethod::WPA,
851 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => AuthMethod::WPA2Personal,
852 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => AuthMethod::WPAWPA2Personal,
853 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => AuthMethod::WPA2Enterprise,
854 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => AuthMethod::WPA3Personal,
855 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => AuthMethod::WPA2WPA3Personal,
856 include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => AuthMethod::WAPIPersonal,
857 _ => unreachable!(),
858 }
859 }
860}
861
862#[derive(Debug, Clone, Copy, PartialEq)]
864#[cfg_attr(feature = "defmt", derive(defmt::Format))]
865#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
866pub enum WifiMode {
867 Sta,
869 Ap,
871 ApSta,
873}
874
875impl WifiMode {
876 pub(crate) fn current() -> Result<Self, WifiError> {
877 let mut mode = wifi_mode_t_WIFI_MODE_NULL;
878 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?;
879
880 Self::try_from(mode)
881 }
882
883 pub fn is_sta(&self) -> bool {
885 match self {
886 Self::Sta | Self::ApSta => true,
887 Self::Ap => false,
888 }
889 }
890
891 pub fn is_ap(&self) -> bool {
893 match self {
894 Self::Sta => false,
895 Self::Ap | Self::ApSta => true,
896 }
897 }
898}
899
900impl TryFrom<&Configuration> for WifiMode {
901 type Error = WifiError;
902
903 fn try_from(config: &Configuration) -> Result<Self, Self::Error> {
905 let mode = match config {
906 Configuration::None => return Err(WifiError::UnknownWifiMode),
907 Configuration::AccessPoint(_) => Self::Ap,
908 Configuration::Client(_) => Self::Sta,
909 Configuration::Mixed(_, _) => Self::ApSta,
910 Configuration::EapClient(_) => Self::Sta,
911 };
912
913 Ok(mode)
914 }
915}
916
917impl TryFrom<wifi_mode_t> for WifiMode {
918 type Error = WifiError;
919
920 fn try_from(value: wifi_mode_t) -> Result<Self, Self::Error> {
922 #[allow(non_upper_case_globals)]
923 match value {
924 include::wifi_mode_t_WIFI_MODE_STA => Ok(Self::Sta),
925 include::wifi_mode_t_WIFI_MODE_AP => Ok(Self::Ap),
926 include::wifi_mode_t_WIFI_MODE_APSTA => Ok(Self::ApSta),
927 _ => Err(WifiError::UnknownWifiMode),
928 }
929 }
930}
931
932impl From<WifiMode> for wifi_mode_t {
933 fn from(val: WifiMode) -> Self {
934 #[allow(non_upper_case_globals)]
935 match val {
936 WifiMode::Sta => wifi_mode_t_WIFI_MODE_STA,
937 WifiMode::Ap => wifi_mode_t_WIFI_MODE_AP,
938 WifiMode::ApSta => wifi_mode_t_WIFI_MODE_APSTA,
939 }
940 }
941}
942
943const RX_QUEUE_SIZE: usize = crate::CONFIG.rx_queue_size;
944const TX_QUEUE_SIZE: usize = crate::CONFIG.tx_queue_size;
945
946pub(crate) static DATA_QUEUE_RX_AP: Locked<VecDeque<EspWifiPacketBuffer>> =
947 Locked::new(VecDeque::new());
948
949pub(crate) static DATA_QUEUE_RX_STA: Locked<VecDeque<EspWifiPacketBuffer>> =
950 Locked::new(VecDeque::new());
951
952#[derive(Debug, Clone, Copy)]
954#[cfg_attr(feature = "defmt", derive(defmt::Format))]
955pub enum WifiError {
956 NotInitialized,
959
960 InternalError(InternalWifiError),
962
963 Disconnected,
965
966 UnknownWifiMode,
968
969 Unsupported,
971}
972
973#[repr(i32)]
975#[derive(Debug, FromPrimitive, EnumSetType)]
976#[cfg_attr(feature = "defmt", derive(defmt::Format))]
977pub enum WifiEvent {
978 WifiReady = 0,
980 ScanDone,
982 StaStart,
984 StaStop,
986 StaConnected,
988 StaDisconnected,
990 StaAuthmodeChange,
992
993 StaWpsErSuccess,
995 StaWpsErFailed,
997 StaWpsErTimeout,
999 StaWpsErPin,
1001 StaWpsErPbcOverlap,
1003
1004 ApStart,
1006 ApStop,
1008 ApStaconnected,
1010 ApStadisconnected,
1012 ApProbereqrecved,
1014
1015 FtmReport,
1017
1018 StaBssRssiLow,
1020 ActionTxStatus,
1022 RocDone,
1024
1025 StaBeaconTimeout,
1027
1028 ConnectionlessModuleWakeIntervalStart,
1030
1031 ApWpsRgSuccess,
1033 ApWpsRgFailed,
1035 ApWpsRgTimeout,
1037 ApWpsRgPin,
1039 ApWpsRgPbcOverlap,
1041
1042 ItwtSetup,
1044 ItwtTeardown,
1046 ItwtProbe,
1048 ItwtSuspend,
1050 TwtWakeup,
1052 BtwtSetup,
1054 BtwtTeardown,
1056
1057 NanStarted,
1059 NanStopped,
1061 NanSvcMatch,
1063 NanReplied,
1065 NanReceive,
1067 NdpIndication,
1069 NdpConfirm,
1071 NdpTerminated,
1073 HomeChannelChange,
1075
1076 StaNeighborRep,
1078}
1079
1080#[repr(i32)]
1082#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
1083#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1084#[allow(clippy::enum_variant_names)] pub enum InternalWifiError {
1086 EspErrNoMem = 0x101,
1088
1089 EspErrInvalidArg = 0x102,
1091
1092 EspErrWifiNotInit = 0x3001,
1094
1095 EspErrWifiNotStarted = 0x3002,
1097
1098 EspErrWifiNotStopped = 0x3003,
1100
1101 EspErrWifiIf = 0x3004,
1103
1104 EspErrWifiMode = 0x3005,
1106
1107 EspErrWifiState = 0x3006,
1109
1110 EspErrWifiConn = 0x3007,
1112
1113 EspErrWifiNvs = 0x3008,
1115
1116 EspErrWifiMac = 0x3009,
1118
1119 EspErrWifiSsid = 0x300A,
1121
1122 EspErrWifiPassword = 0x300B,
1124
1125 EspErrWifiTimeout = 0x300C,
1127
1128 EspErrWifiWakeFail = 0x300D,
1130
1131 EspErrWifiWouldBlock = 0x300E,
1133
1134 EspErrWifiNotConnect = 0x300F,
1136
1137 EspErrWifiPost = 0x3012,
1139
1140 EspErrWifiInitState = 0x3013,
1142
1143 EspErrWifiStopState = 0x3014,
1145
1146 EspErrWifiNotAssoc = 0x3015,
1148
1149 EspErrWifiTxDisallow = 0x3016,
1151}
1152
1153#[cfg(all(coex, any(esp32, esp32c2, esp32c3, esp32c6, esp32s3)))]
1154static mut G_COEX_ADAPTER_FUNCS: coex_adapter_funcs_t = coex_adapter_funcs_t {
1155 _version: include::COEX_ADAPTER_VERSION as i32,
1156 _task_yield_from_isr: Some(task_yield_from_isr),
1157 _semphr_create: Some(semphr_create),
1158 _semphr_delete: Some(semphr_delete),
1159 _semphr_take_from_isr: Some(semphr_take_from_isr_wrapper),
1160 _semphr_give_from_isr: Some(semphr_give_from_isr_wrapper),
1161 _semphr_take: Some(semphr_take),
1162 _semphr_give: Some(semphr_give),
1163 _is_in_isr: Some(is_in_isr_wrapper),
1164 _malloc_internal: Some(malloc),
1165 _free: Some(free),
1166 _esp_timer_get_time: Some(esp_timer_get_time),
1167 _env_is_chip: Some(env_is_chip),
1168 _magic: include::COEX_ADAPTER_MAGIC as i32,
1169 _timer_disarm: Some(ets_timer_disarm),
1170 _timer_done: Some(ets_timer_done),
1171 _timer_setfn: Some(ets_timer_setfn),
1172 _timer_arm_us: Some(ets_timer_arm_us),
1173
1174 #[cfg(esp32)]
1175 _spin_lock_create: Some(spin_lock_create),
1176 #[cfg(esp32)]
1177 _spin_lock_delete: Some(spin_lock_delete),
1178 #[cfg(esp32)]
1179 _int_disable: Some(wifi_int_disable),
1180 #[cfg(esp32)]
1181 _int_enable: Some(wifi_int_restore),
1182
1183 #[cfg(esp32c2)]
1184 _slowclk_cal_get: Some(slowclk_cal_get),
1185};
1186
1187#[cfg(coex)]
1188unsafe extern "C" fn semphr_take_from_isr_wrapper(
1189 semphr: *mut c_types::c_void,
1190 hptw: *mut c_types::c_void,
1191) -> i32 {
1192 crate::common_adapter::semphr_take_from_isr(semphr as *const (), hptw as *const ())
1193}
1194
1195#[cfg(coex)]
1196unsafe extern "C" fn semphr_give_from_isr_wrapper(
1197 semphr: *mut c_types::c_void,
1198 hptw: *mut c_types::c_void,
1199) -> i32 {
1200 crate::common_adapter::semphr_give_from_isr(semphr as *const (), hptw as *const ())
1201}
1202
1203#[cfg(coex)]
1204unsafe extern "C" fn is_in_isr_wrapper() -> i32 {
1205 0
1207}
1208
1209#[cfg(coex)]
1210pub(crate) fn coex_initialize() -> i32 {
1211 debug!("call coex-initialize");
1212 unsafe {
1213 let res = esp_coex_adapter_register(core::ptr::addr_of_mut!(G_COEX_ADAPTER_FUNCS).cast());
1214 if res != 0 {
1215 error!("Error: esp_coex_adapter_register {}", res);
1216 return res;
1217 }
1218 let res = coex_pre_init();
1219 if res != 0 {
1220 error!("Error: coex_pre_init {}", res);
1221 return res;
1222 }
1223 0
1224 }
1225}
1226
1227pub(crate) unsafe extern "C" fn coex_init() -> i32 {
1228 #[cfg(coex)]
1229 {
1230 debug!("coex-init");
1231 #[allow(clippy::needless_return)]
1232 return include::coex_init();
1233 }
1234
1235 #[cfg(not(coex))]
1236 0
1237}
1238
1239#[no_mangle]
1240static g_wifi_osi_funcs: wifi_osi_funcs_t = wifi_osi_funcs_t {
1241 _version: ESP_WIFI_OS_ADAPTER_VERSION as i32,
1242 _env_is_chip: Some(env_is_chip),
1243 _set_intr: Some(set_intr),
1244 _clear_intr: Some(clear_intr),
1245 _set_isr: Some(os_adapter_chip_specific::set_isr),
1246 _ints_on: Some(ints_on),
1247 _ints_off: Some(ints_off),
1248 _is_from_isr: Some(is_from_isr),
1249 _spin_lock_create: Some(spin_lock_create),
1250 _spin_lock_delete: Some(spin_lock_delete),
1251 _wifi_int_disable: Some(wifi_int_disable),
1252 _wifi_int_restore: Some(wifi_int_restore),
1253 _task_yield_from_isr: Some(task_yield_from_isr),
1254 _semphr_create: Some(semphr_create),
1255 _semphr_delete: Some(semphr_delete),
1256 _semphr_take: Some(semphr_take),
1257 _semphr_give: Some(semphr_give),
1258 _wifi_thread_semphr_get: Some(wifi_thread_semphr_get),
1259 _mutex_create: Some(mutex_create),
1260 _recursive_mutex_create: Some(recursive_mutex_create),
1261 _mutex_delete: Some(mutex_delete),
1262 _mutex_lock: Some(mutex_lock),
1263 _mutex_unlock: Some(mutex_unlock),
1264 _queue_create: Some(queue_create),
1265 _queue_delete: Some(queue_delete),
1266 _queue_send: Some(queue_send),
1267 _queue_send_from_isr: Some(queue_send_from_isr),
1268 _queue_send_to_back: Some(queue_send_to_back),
1269 _queue_send_to_front: Some(queue_send_to_front),
1270 _queue_recv: Some(queue_recv),
1271 _queue_msg_waiting: Some(queue_msg_waiting),
1272 _event_group_create: Some(event_group_create),
1273 _event_group_delete: Some(event_group_delete),
1274 _event_group_set_bits: Some(event_group_set_bits),
1275 _event_group_clear_bits: Some(event_group_clear_bits),
1276 _event_group_wait_bits: Some(event_group_wait_bits),
1277 _task_create_pinned_to_core: Some(task_create_pinned_to_core),
1278 _task_create: Some(task_create),
1279 _task_delete: Some(task_delete),
1280 _task_delay: Some(task_delay),
1281 _task_ms_to_tick: Some(task_ms_to_tick),
1282 _task_get_current_task: Some(task_get_current_task),
1283 _task_get_max_priority: Some(task_get_max_priority),
1284 _malloc: Some(malloc),
1285 _free: Some(free),
1286 _event_post: Some(event_post),
1287 _get_free_heap_size: Some(get_free_heap_size),
1288 _rand: Some(rand),
1289 _dport_access_stall_other_cpu_start_wrap: Some(dport_access_stall_other_cpu_start_wrap),
1290 _dport_access_stall_other_cpu_end_wrap: Some(dport_access_stall_other_cpu_end_wrap),
1291 _wifi_apb80m_request: Some(wifi_apb80m_request),
1292 _wifi_apb80m_release: Some(wifi_apb80m_release),
1293 _phy_disable: Some(phy_disable),
1294 _phy_enable: Some(phy_enable),
1295 _phy_update_country_info: Some(phy_update_country_info),
1296 _read_mac: Some(read_mac),
1297 _timer_arm: Some(ets_timer_arm),
1298 _timer_disarm: Some(ets_timer_disarm),
1299 _timer_done: Some(ets_timer_done),
1300 _timer_setfn: Some(ets_timer_setfn),
1301 _timer_arm_us: Some(ets_timer_arm_us),
1302 _wifi_reset_mac: Some(wifi_reset_mac),
1303 _wifi_clock_enable: Some(wifi_clock_enable),
1304 _wifi_clock_disable: Some(wifi_clock_disable),
1305 _wifi_rtc_enable_iso: Some(wifi_rtc_enable_iso),
1306 _wifi_rtc_disable_iso: Some(wifi_rtc_disable_iso),
1307 _esp_timer_get_time: Some(esp_timer_get_time),
1308 _nvs_set_i8: Some(nvs_set_i8),
1309 _nvs_get_i8: Some(nvs_get_i8),
1310 _nvs_set_u8: Some(nvs_set_u8),
1311 _nvs_get_u8: Some(nvs_get_u8),
1312 _nvs_set_u16: Some(nvs_set_u16),
1313 _nvs_get_u16: Some(nvs_get_u16),
1314 _nvs_open: Some(nvs_open),
1315 _nvs_close: Some(nvs_close),
1316 _nvs_commit: Some(nvs_commit),
1317 _nvs_set_blob: Some(nvs_set_blob),
1318 _nvs_get_blob: Some(nvs_get_blob),
1319 _nvs_erase_key: Some(nvs_erase_key),
1320 _get_random: Some(get_random),
1321 _get_time: Some(get_time),
1322 _random: Some(random),
1323 #[cfg(feature = "sys-logs")]
1324 _log_write: Some(log_write),
1325 #[cfg(not(feature = "sys-logs"))]
1326 _log_write: None,
1327 #[cfg(feature = "sys-logs")]
1328 _log_writev: Some(log_writev),
1329 #[cfg(not(feature = "sys-logs"))]
1330 _log_writev: None,
1331 _log_timestamp: Some(log_timestamp),
1332 _malloc_internal: Some(malloc_internal),
1333 _realloc_internal: Some(realloc_internal),
1334 _calloc_internal: Some(calloc_internal),
1335 _zalloc_internal: Some(zalloc_internal),
1336 _wifi_malloc: Some(wifi_malloc),
1337 _wifi_realloc: Some(wifi_realloc),
1338 _wifi_calloc: Some(wifi_calloc),
1339 _wifi_zalloc: Some(wifi_zalloc),
1340 _wifi_create_queue: Some(wifi_create_queue),
1341 _wifi_delete_queue: Some(wifi_delete_queue),
1342 _coex_init: Some(coex_init),
1343 _coex_deinit: Some(coex_deinit),
1344 _coex_enable: Some(coex_enable),
1345 _coex_disable: Some(coex_disable),
1346 _coex_status_get: Some(coex_status_get),
1347 _coex_condition_set: None,
1348 _coex_wifi_request: Some(coex_wifi_request),
1349 _coex_wifi_release: Some(coex_wifi_release),
1350 _coex_wifi_channel_set: Some(coex_wifi_channel_set),
1351 _coex_event_duration_get: Some(coex_event_duration_get),
1352 _coex_pti_get: Some(coex_pti_get),
1353 _coex_schm_status_bit_clear: Some(coex_schm_status_bit_clear),
1354 _coex_schm_status_bit_set: Some(coex_schm_status_bit_set),
1355 _coex_schm_interval_set: Some(coex_schm_interval_set),
1356 _coex_schm_interval_get: Some(coex_schm_interval_get),
1357 _coex_schm_curr_period_get: Some(coex_schm_curr_period_get),
1358 _coex_schm_curr_phase_get: Some(coex_schm_curr_phase_get),
1359 #[cfg(any(esp32c3, esp32c2, esp32c6, esp32h2, esp32s3, esp32s2))]
1360 _slowclk_cal_get: Some(slowclk_cal_get),
1361 #[cfg(any(esp32, esp32s2))]
1362 _phy_common_clock_disable: Some(os_adapter_chip_specific::phy_common_clock_disable),
1363 #[cfg(any(esp32, esp32s2))]
1364 _phy_common_clock_enable: Some(os_adapter_chip_specific::phy_common_clock_enable),
1365 _coex_register_start_cb: Some(coex_register_start_cb),
1366
1367 #[cfg(esp32c6)]
1368 _regdma_link_set_write_wait_content: Some(
1369 os_adapter_chip_specific::regdma_link_set_write_wait_content_dummy,
1370 ),
1371 #[cfg(esp32c6)]
1372 _sleep_retention_find_link_by_id: Some(
1373 os_adapter_chip_specific::sleep_retention_find_link_by_id_dummy,
1374 ),
1375 _coex_schm_process_restart: Some(coex_schm_process_restart_wrapper),
1376 _coex_schm_register_cb: Some(coex_schm_register_cb_wrapper),
1377
1378 _magic: ESP_WIFI_OS_ADAPTER_MAGIC as i32,
1379
1380 _coex_schm_flexible_period_set: Some(coex_schm_flexible_period_set),
1381 _coex_schm_flexible_period_get: Some(coex_schm_flexible_period_get),
1382};
1383
1384const WIFI_ENABLE_WPA3_SAE: u64 = 1 << 0;
1385const WIFI_ENABLE_ENTERPRISE: u64 = 1 << 7;
1386const WIFI_FEATURE_CAPS: u64 = WIFI_ENABLE_WPA3_SAE | WIFI_ENABLE_ENTERPRISE;
1393
1394#[no_mangle]
1395static mut g_wifi_feature_caps: u64 = WIFI_FEATURE_CAPS;
1396
1397static mut G_CONFIG: wifi_init_config_t = wifi_init_config_t {
1398 osi_funcs: addr_of!(g_wifi_osi_funcs).cast_mut(),
1399
1400 wpa_crypto_funcs: wpa_crypto_funcs_t {
1402 size: 0,
1403 version: 1,
1404 aes_wrap: None,
1405 aes_unwrap: None,
1406 hmac_sha256_vector: None,
1407 sha256_prf: None,
1408 hmac_md5: None,
1409 hamc_md5_vector: None,
1410 hmac_sha1: None,
1411 hmac_sha1_vector: None,
1412 sha1_prf: None,
1413 sha1_vector: None,
1414 pbkdf2_sha1: None,
1415 rc4_skip: None,
1416 md5_vector: None,
1417 aes_encrypt: None,
1418 aes_encrypt_init: None,
1419 aes_encrypt_deinit: None,
1420 aes_decrypt: None,
1421 aes_decrypt_init: None,
1422 aes_decrypt_deinit: None,
1423 aes_128_encrypt: None,
1424 aes_128_decrypt: None,
1425 omac1_aes_128: None,
1426 ccmp_decrypt: None,
1427 ccmp_encrypt: None,
1428 aes_gmac: None,
1429 sha256_vector: None,
1430 crc32: None,
1431 },
1432 static_rx_buf_num: crate::CONFIG.static_rx_buf_num as i32,
1433 dynamic_rx_buf_num: crate::CONFIG.dynamic_rx_buf_num as i32,
1434 tx_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_TX_BUFFER_TYPE as i32,
1435 static_tx_buf_num: crate::CONFIG.static_tx_buf_num as i32,
1436 dynamic_tx_buf_num: crate::CONFIG.dynamic_tx_buf_num as i32,
1437 rx_mgmt_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as i32,
1438 rx_mgmt_buf_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF as i32,
1439 cache_tx_buf_num: esp_wifi_sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32,
1440 csi_enable: cfg!(feature = "csi") as i32,
1441 ampdu_rx_enable: crate::CONFIG.ampdu_rx_enable as i32,
1442 ampdu_tx_enable: crate::CONFIG.ampdu_tx_enable as i32,
1443 amsdu_tx_enable: crate::CONFIG.amsdu_tx_enable as i32,
1444 nvs_enable: 0,
1445 nano_enable: 0,
1446 rx_ba_win: crate::CONFIG.rx_ba_win as i32,
1447 wifi_task_core_id: 0,
1448 beacon_max_len: esp_wifi_sys::include::WIFI_SOFTAP_BEACON_MAX_LEN as i32,
1449 mgmt_sbuf_num: esp_wifi_sys::include::WIFI_MGMT_SBUF_NUM as i32,
1450 feature_caps: WIFI_FEATURE_CAPS,
1451 sta_disconnected_pm: false,
1452 espnow_max_encrypt_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM as i32,
1453 magic: WIFI_INIT_CONFIG_MAGIC as i32,
1454
1455 tx_hetb_queue_num: 3,
1456 dump_hesigb_enable: false,
1457};
1458
1459pub fn sta_mac(mac: &mut [u8; 6]) {
1461 unsafe {
1462 read_mac(mac as *mut u8, 0);
1463 }
1464}
1465
1466pub fn ap_mac(mac: &mut [u8; 6]) {
1468 unsafe {
1469 read_mac(mac as *mut u8, 1);
1470 }
1471}
1472
1473pub(crate) fn wifi_init() -> Result<(), WifiError> {
1474 unsafe {
1475 G_CONFIG.wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs;
1476 G_CONFIG.feature_caps = g_wifi_feature_caps;
1477
1478 #[cfg(coex)]
1479 esp_wifi_result!(coex_init())?;
1480
1481 esp_wifi_result!(esp_wifi_init_internal(addr_of!(G_CONFIG)))?;
1482 esp_wifi_result!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
1483
1484 esp_wifi_result!(esp_supplicant_init())?;
1485
1486 esp_wifi_result!(esp_wifi_set_tx_done_cb(Some(esp_wifi_tx_done_cb)))?;
1487
1488 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1489 esp_interface_t_ESP_IF_WIFI_STA,
1490 Some(recv_cb_sta)
1491 ))?;
1492
1493 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1495 esp_interface_t_ESP_IF_WIFI_AP,
1496 Some(recv_cb_ap)
1497 ))?;
1498
1499 #[cfg(any(esp32, esp32s3))]
1500 {
1501 static mut NVS_STRUCT: [u32; 12] = [0; 12];
1502 chip_specific::g_misc_nvs = addr_of!(NVS_STRUCT) as u32;
1503 }
1504
1505 crate::flags::WIFI.fetch_add(1, Ordering::SeqCst);
1506
1507 Ok(())
1508 }
1509}
1510
1511pub(crate) fn wifi_deinit() -> Result<(), crate::InitializationError> {
1512 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
1513 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1514 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1515 Ok(())
1516}
1517
1518unsafe extern "C" fn recv_cb_sta(
1519 buffer: *mut c_types::c_void,
1520 len: u16,
1521 eb: *mut c_types::c_void,
1522) -> esp_err_t {
1523 let packet = EspWifiPacketBuffer { buffer, len, eb };
1524 if let Ok(()) = DATA_QUEUE_RX_STA.with(|queue| {
1531 if queue.len() < RX_QUEUE_SIZE {
1532 queue.push_back(packet);
1533 Ok(())
1534 } else {
1535 Err(packet)
1536 }
1537 }) {
1538 embassy::STA_RECEIVE_WAKER.wake();
1539 include::ESP_OK as esp_err_t
1540 } else {
1541 debug!("RX QUEUE FULL");
1542 include::ESP_ERR_NO_MEM as esp_err_t
1543 }
1544}
1545
1546unsafe extern "C" fn recv_cb_ap(
1547 buffer: *mut c_types::c_void,
1548 len: u16,
1549 eb: *mut c_types::c_void,
1550) -> esp_err_t {
1551 let packet = EspWifiPacketBuffer { buffer, len, eb };
1552 if let Ok(()) = DATA_QUEUE_RX_AP.with(|queue| {
1559 if queue.len() < RX_QUEUE_SIZE {
1560 queue.push_back(packet);
1561 Ok(())
1562 } else {
1563 Err(packet)
1564 }
1565 }) {
1566 embassy::AP_RECEIVE_WAKER.wake();
1567 include::ESP_OK as esp_err_t
1568 } else {
1569 debug!("RX QUEUE FULL");
1570 include::ESP_ERR_NO_MEM as esp_err_t
1571 }
1572}
1573
1574pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1575
1576fn decrement_inflight_counter() {
1577 unwrap!(
1578 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1579 Some(x.saturating_sub(1))
1580 })
1581 );
1582}
1583
1584#[ram]
1585unsafe extern "C" fn esp_wifi_tx_done_cb(
1586 _ifidx: u8,
1587 _data: *mut u8,
1588 _data_len: *mut u16,
1589 _tx_status: bool,
1590) {
1591 trace!("esp_wifi_tx_done_cb");
1592
1593 decrement_inflight_counter();
1594
1595 embassy::TRANSMIT_WAKER.wake();
1596}
1597
1598pub(crate) fn wifi_start() -> Result<(), WifiError> {
1599 unsafe {
1600 esp_wifi_result!(esp_wifi_start())?;
1601
1602 let mode = WifiMode::current()?;
1603
1604 if mode.is_ap() {
1606 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1607 wifi_interface_t_WIFI_IF_AP,
1608 crate::CONFIG.ap_beacon_timeout
1609 ))?;
1610 }
1611 if mode.is_sta() {
1612 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1613 wifi_interface_t_WIFI_IF_STA,
1614 crate::CONFIG.beacon_timeout
1615 ))?;
1616 };
1617
1618 let mut cntry_code = [0u8; 3];
1619 cntry_code[..crate::CONFIG.country_code.len()]
1620 .copy_from_slice(crate::CONFIG.country_code.as_bytes());
1621 cntry_code[2] = crate::CONFIG.country_code_operating_class;
1622
1623 #[allow(clippy::useless_transmute)]
1624 let country = wifi_country_t {
1625 #[allow(clippy::useless_transmute)]
1627 cc: core::mem::transmute::<[u8; 3], [core::ffi::c_char; 3]>(cntry_code),
1628 schan: 1,
1629 nchan: 13,
1630 max_tx_power: 20,
1631 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
1632 };
1633 esp_wifi_result!(esp_wifi_set_country(&country))?;
1634 }
1635
1636 Ok(())
1637}
1638
1639#[derive(Clone, Copy, PartialEq, Eq)]
1648pub enum ScanTypeConfig {
1649 Active {
1659 min: Duration,
1661 max: Duration,
1663 },
1664 Passive(Duration),
1675}
1676
1677impl Default for ScanTypeConfig {
1678 fn default() -> Self {
1679 Self::Active {
1680 min: Duration::from_millis(10),
1681 max: Duration::from_millis(20),
1682 }
1683 }
1684}
1685
1686impl ScanTypeConfig {
1687 fn validate(&self) {
1688 if matches!(self, Self::Passive(dur) if *dur > Duration::from_millis(1500)) {
1689 warn!("Passive scan duration longer than 1500ms may cause a station to disconnect from the AP");
1690 }
1691 }
1692}
1693
1694#[derive(Clone, Copy, Default, PartialEq, Eq)]
1696pub struct ScanConfig<'a> {
1697 pub ssid: Option<&'a str>,
1702 pub bssid: Option<[u8; 6]>,
1707 pub channel: Option<u8>,
1712 pub show_hidden: bool,
1714 pub scan_type: ScanTypeConfig,
1716}
1717
1718pub(crate) fn wifi_start_scan(
1719 block: bool,
1720 ScanConfig {
1721 ssid,
1722 mut bssid,
1723 channel,
1724 show_hidden,
1725 scan_type,
1726 }: ScanConfig<'_>,
1727) -> i32 {
1728 scan_type.validate();
1729 let (scan_time, scan_type) = match scan_type {
1730 ScanTypeConfig::Active { min, max } => (
1731 wifi_scan_time_t {
1732 active: wifi_active_scan_time_t {
1733 min: min.as_millis() as u32,
1734 max: max.as_millis() as u32,
1735 },
1736 passive: 0,
1737 },
1738 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1739 ),
1740 ScanTypeConfig::Passive(dur) => (
1741 wifi_scan_time_t {
1742 active: wifi_active_scan_time_t { min: 0, max: 0 },
1743 passive: dur.as_millis() as u32,
1744 },
1745 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1746 ),
1747 };
1748
1749 let mut ssid_buf = ssid.map(|m| {
1750 let mut buf = heapless::Vec::<u8, 33>::from_iter(m.bytes());
1751 unwrap!(buf.push(b'\0').ok());
1752 buf
1753 });
1754
1755 let ssid = ssid_buf
1756 .as_mut()
1757 .map(|e| e.as_mut_ptr())
1758 .unwrap_or_else(core::ptr::null_mut);
1759 let bssid = bssid
1760 .as_mut()
1761 .map(|e| e.as_mut_ptr())
1762 .unwrap_or_else(core::ptr::null_mut);
1763
1764 let scan_config = wifi_scan_config_t {
1765 ssid,
1766 bssid,
1767 channel: channel.unwrap_or(0),
1768 show_hidden,
1769 scan_type,
1770 scan_time,
1771 home_chan_dwell_time: 0,
1772 channel_bitmap: wifi_scan_channel_bitmap_t {
1773 ghz_2_channels: 0,
1774 ghz_5_channels: 0,
1775 },
1776 };
1777
1778 unsafe { esp_wifi_scan_start(&scan_config, block) }
1779}
1780
1781mod private {
1782 use super::*;
1783
1784 #[derive(Debug)]
1785 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1786 pub struct EspWifiPacketBuffer {
1794 pub(crate) buffer: *mut c_types::c_void,
1795 pub(crate) len: u16,
1796 pub(crate) eb: *mut c_types::c_void,
1797 }
1798
1799 unsafe impl Send for EspWifiPacketBuffer {}
1800
1801 impl Drop for EspWifiPacketBuffer {
1802 fn drop(&mut self) {
1803 trace!("Dropping EspWifiPacketBuffer, freeing memory");
1804 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1805 }
1806 }
1807
1808 impl EspWifiPacketBuffer {
1809 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1810 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1811 }
1812 }
1813}
1814
1815#[derive(Debug, Clone, Copy)]
1817pub enum WifiDeviceMode {
1818 Sta,
1819 Ap,
1820}
1821
1822impl WifiDeviceMode {
1823 fn mac_address(&self) -> [u8; 6] {
1824 match self {
1825 WifiDeviceMode::Sta => {
1826 let mut mac = [0; 6];
1827 sta_mac(&mut mac);
1828 mac
1829 }
1830 WifiDeviceMode::Ap => {
1831 let mut mac = [0; 6];
1832 ap_mac(&mut mac);
1833 mac
1834 }
1835 }
1836 }
1837
1838 fn data_queue_rx(&self) -> &'static Locked<VecDeque<EspWifiPacketBuffer>> {
1839 match self {
1840 WifiDeviceMode::Sta => &DATA_QUEUE_RX_STA,
1841 WifiDeviceMode::Ap => &DATA_QUEUE_RX_AP,
1842 }
1843 }
1844
1845 fn can_send(&self) -> bool {
1846 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE
1847 }
1848
1849 fn increase_in_flight_counter(&self) {
1850 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1851 }
1852
1853 fn tx_token(&self) -> Option<WifiTxToken> {
1854 if !self.can_send() {
1855 crate::preempt::yield_task();
1856 }
1857
1858 if self.can_send() {
1859 Some(WifiTxToken { mode: *self })
1860 } else {
1861 None
1862 }
1863 }
1864
1865 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1866 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1867 if is_empty || !self.can_send() {
1868 crate::preempt::yield_task();
1869 }
1870
1871 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1872
1873 if !is_empty {
1874 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1875 } else {
1876 None
1877 }
1878 }
1879
1880 fn interface(&self) -> wifi_interface_t {
1881 match self {
1882 WifiDeviceMode::Sta => wifi_interface_t_WIFI_IF_STA,
1883 WifiDeviceMode::Ap => wifi_interface_t_WIFI_IF_AP,
1884 }
1885 }
1886
1887 fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) {
1888 embassy::TRANSMIT_WAKER.register(cx.waker())
1889 }
1890
1891 fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) {
1892 match self {
1893 WifiDeviceMode::Sta => embassy::STA_RECEIVE_WAKER.register(cx.waker()),
1894 WifiDeviceMode::Ap => embassy::AP_RECEIVE_WAKER.register(cx.waker()),
1895 }
1896 }
1897
1898 fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) {
1899 match self {
1900 WifiDeviceMode::Sta => embassy::STA_LINK_STATE_WAKER.register(cx.waker()),
1901 WifiDeviceMode::Ap => embassy::AP_LINK_STATE_WAKER.register(cx.waker()),
1902 }
1903 }
1904
1905 fn link_state(&self) -> embassy_net_driver::LinkState {
1906 match self {
1907 WifiDeviceMode::Sta => {
1908 if matches!(sta_state(), WifiState::StaConnected) {
1909 embassy_net_driver::LinkState::Up
1910 } else {
1911 embassy_net_driver::LinkState::Down
1912 }
1913 }
1914 WifiDeviceMode::Ap => {
1915 if matches!(ap_state(), WifiState::ApStarted) {
1916 embassy_net_driver::LinkState::Up
1917 } else {
1918 embassy_net_driver::LinkState::Down
1919 }
1920 }
1921 }
1922 }
1923}
1924
1925pub struct WifiDevice<'d> {
1927 _phantom: PhantomData<&'d ()>,
1928 mode: WifiDeviceMode,
1929}
1930
1931impl WifiDevice<'_> {
1932 pub fn mac_address(&self) -> [u8; 6] {
1934 self.mode.mac_address()
1935 }
1936
1937 #[cfg(not(feature = "smoltcp"))]
1940 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1941 self.mode.rx_token()
1942 }
1943
1944 #[cfg(not(feature = "smoltcp"))]
1947 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1948 self.mode.tx_token()
1949 }
1950}
1951
1952fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
1953 let str_len = record
1954 .ssid
1955 .iter()
1956 .position(|&c| c == 0)
1957 .unwrap_or(record.ssid.len());
1958 let ssid_ref = unsafe { core::str::from_utf8_unchecked(&record.ssid[..str_len]) };
1959
1960 let mut ssid = heapless::String::<32>::new();
1961 unwrap!(ssid.push_str(ssid_ref));
1962
1963 AccessPointInfo {
1964 ssid,
1965 bssid: record.bssid,
1966 channel: record.primary,
1967 secondary_channel: match record.second {
1968 include::wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
1969 include::wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
1970 include::wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
1971 _ => panic!(),
1972 },
1973 signal_strength: record.rssi,
1974 protocols: EnumSet::empty(), auth_method: Some(AuthMethod::from_raw(record.authmode)),
1976 }
1977}
1978
1979#[cfg(not(any(esp32c6)))]
1982#[derive(Debug, Clone, Copy)]
1983#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1984pub struct RxControlInfo {
1985 pub rssi: i32,
1987 pub rate: u32,
1990 pub sig_mode: u32,
1993 pub mcs: u32,
1996 pub cwb: u32,
1998 pub smoothing: u32,
2001 pub not_sounding: u32,
2004 pub aggregation: u32,
2006 pub stbc: u32,
2009 pub fec_coding: u32,
2012 pub sgi: u32,
2015 pub ampdu_cnt: u32,
2017 pub channel: u32,
2019 pub secondary_channel: u32,
2022 pub timestamp: u32,
2025 pub noise_floor: i32,
2027 pub ant: u32,
2030 pub sig_len: u32,
2032 pub rx_state: u32,
2034}
2035
2036#[cfg(esp32c6)]
2039#[derive(Debug, Clone, Copy)]
2040#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2041pub struct RxControlInfo {
2042 pub rssi: i32,
2044 pub rate: u32,
2047 pub sig_len: u32,
2049 pub rx_state: u32,
2052 pub dump_len: u32,
2054 pub he_sigb_len: u32,
2056 pub cur_single_mpdu: u32,
2058 pub cur_bb_format: u32,
2060 pub rx_channel_estimate_info_vld: u32,
2062 pub rx_channel_estimate_len: u32,
2064 pub second: u32,
2066 pub channel: u32,
2068 pub noise_floor: i32,
2070 pub is_group: u32,
2072 pub rxend_state: u32,
2074 pub rxmatch3: u32,
2076 pub rxmatch2: u32,
2078 pub rxmatch1: u32,
2080 pub rxmatch0: u32,
2082}
2083impl RxControlInfo {
2084 pub unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
2090 #[cfg(not(esp32c6))]
2091 let rx_control_info = RxControlInfo {
2092 rssi: (*rx_cntl).rssi(),
2093 rate: (*rx_cntl).rate(),
2094 sig_mode: (*rx_cntl).sig_mode(),
2095 mcs: (*rx_cntl).mcs(),
2096 cwb: (*rx_cntl).cwb(),
2097 smoothing: (*rx_cntl).smoothing(),
2098 not_sounding: (*rx_cntl).not_sounding(),
2099 aggregation: (*rx_cntl).aggregation(),
2100 stbc: (*rx_cntl).stbc(),
2101 fec_coding: (*rx_cntl).fec_coding(),
2102 sgi: (*rx_cntl).sgi(),
2103 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
2104 channel: (*rx_cntl).channel(),
2105 secondary_channel: (*rx_cntl).secondary_channel(),
2106 timestamp: (*rx_cntl).timestamp(),
2107 noise_floor: (*rx_cntl).noise_floor(),
2108 ant: (*rx_cntl).ant(),
2109 sig_len: (*rx_cntl).sig_len(),
2110 rx_state: (*rx_cntl).rx_state(),
2111 };
2112 #[cfg(esp32c6)]
2113 let rx_control_info = RxControlInfo {
2114 rssi: (*rx_cntl).rssi(),
2115 rate: (*rx_cntl).rate(),
2116 sig_len: (*rx_cntl).sig_len(),
2117 rx_state: (*rx_cntl).rx_state(),
2118 dump_len: (*rx_cntl).dump_len(),
2119 he_sigb_len: (*rx_cntl).he_sigb_len(),
2120 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
2121 cur_bb_format: (*rx_cntl).cur_bb_format(),
2122 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
2123 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
2124 second: (*rx_cntl).second(),
2125 channel: (*rx_cntl).channel(),
2126 noise_floor: (*rx_cntl).noise_floor(),
2127 is_group: (*rx_cntl).is_group(),
2128 rxend_state: (*rx_cntl).rxend_state(),
2129 rxmatch3: (*rx_cntl).rxmatch3(),
2130 rxmatch2: (*rx_cntl).rxmatch2(),
2131 rxmatch1: (*rx_cntl).rxmatch1(),
2132 rxmatch0: (*rx_cntl).rxmatch0(),
2133 };
2134 rx_control_info
2135 }
2136}
2137#[cfg(feature = "sniffer")]
2139pub struct PromiscuousPkt<'a> {
2140 pub rx_cntl: RxControlInfo,
2142 pub frame_type: wifi_promiscuous_pkt_type_t,
2144 pub len: usize,
2146 pub data: &'a [u8],
2148}
2149#[cfg(feature = "sniffer")]
2150impl PromiscuousPkt<'_> {
2151 pub(crate) unsafe fn from_raw(
2155 buf: *const wifi_promiscuous_pkt_t,
2156 frame_type: wifi_promiscuous_pkt_type_t,
2157 ) -> Self {
2158 let rx_cntl = RxControlInfo::from_raw(&(*buf).rx_ctrl);
2159 let len = rx_cntl.sig_len as usize;
2160 PromiscuousPkt {
2161 rx_cntl,
2162 frame_type,
2163 len,
2164 data: core::slice::from_raw_parts(
2165 (buf as *const u8).add(core::mem::size_of::<wifi_pkt_rx_ctrl_t>()),
2166 len,
2167 ),
2168 }
2169 }
2170}
2171
2172#[cfg(feature = "sniffer")]
2173static SNIFFER_CB: Locked<Option<fn(PromiscuousPkt<'_>)>> = Locked::new(None);
2174
2175#[cfg(feature = "sniffer")]
2176unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) {
2177 if let Some(sniffer_callback) = SNIFFER_CB.with(|callback| *callback) {
2178 let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type);
2179 sniffer_callback(promiscuous_pkt);
2180 }
2181}
2182
2183#[cfg(feature = "sniffer")]
2184pub struct Sniffer {
2186 promiscuous_mode_enabled: AtomicBool,
2187}
2188#[cfg(feature = "sniffer")]
2189impl Sniffer {
2190 pub(crate) fn new() -> Self {
2191 unwrap!(esp_wifi_result!(unsafe {
2194 esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb))
2195 }));
2196 Self {
2197 promiscuous_mode_enabled: AtomicBool::new(false),
2198 }
2199 }
2200 pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
2202 esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
2203 self.promiscuous_mode_enabled
2204 .store(enabled, Ordering::Relaxed);
2205 Ok(())
2206 }
2207 pub fn send_raw_frame(
2209 &mut self,
2210 use_sta_interface: bool,
2211 buffer: &[u8],
2212 use_internal_seq_num: bool,
2213 ) -> Result<(), WifiError> {
2214 esp_wifi_result!(unsafe {
2215 esp_wifi_80211_tx(
2216 if use_sta_interface {
2217 wifi_interface_t_WIFI_IF_STA
2218 } else {
2219 wifi_interface_t_WIFI_IF_AP
2220 } as wifi_interface_t,
2221 buffer.as_ptr() as *const _,
2222 buffer.len() as i32,
2223 use_internal_seq_num,
2224 )
2225 })
2226 }
2227 pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt<'_>)) {
2229 SNIFFER_CB.with(|callback| *callback = Some(cb));
2230 }
2231}
2232
2233#[cfg(feature = "smoltcp")]
2235impl Device for WifiDevice<'_> {
2236 type RxToken<'a>
2237 = WifiRxToken
2238 where
2239 Self: 'a;
2240 type TxToken<'a>
2241 = WifiTxToken
2242 where
2243 Self: 'a;
2244
2245 fn receive(
2246 &mut self,
2247 _instant: smoltcp::time::Instant,
2248 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2249 self.mode.rx_token()
2250 }
2251
2252 fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
2253 self.mode.tx_token()
2254 }
2255
2256 fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
2257 let mut caps = DeviceCapabilities::default();
2258 caps.max_transmission_unit = MTU;
2259 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2260 None
2261 } else {
2262 Some(crate::CONFIG.max_burst_size)
2263 };
2264 caps
2265 }
2266}
2267
2268#[doc(hidden)]
2269#[derive(Debug)]
2270pub struct WifiRxToken {
2271 mode: WifiDeviceMode,
2272}
2273
2274impl WifiRxToken {
2275 pub fn consume_token<R, F>(self, f: F) -> R
2278 where
2279 F: FnOnce(&mut [u8]) -> R,
2280 {
2281 let mut data = self.mode.data_queue_rx().with(|queue| {
2282 unwrap!(
2283 queue.pop_front(),
2284 "unreachable: transmit()/receive() ensures there is a packet to process"
2285 )
2286 });
2287
2288 let buffer = data.as_slice_mut();
2295 dump_packet_info(buffer);
2296
2297 f(buffer)
2298 }
2299}
2300
2301#[cfg(feature = "smoltcp")]
2302impl RxToken for WifiRxToken {
2303 fn consume<R, F>(self, f: F) -> R
2304 where
2305 F: FnOnce(&[u8]) -> R,
2306 {
2307 self.consume_token(|t| f(t))
2308 }
2309}
2310
2311#[doc(hidden)]
2312#[derive(Debug)]
2313pub struct WifiTxToken {
2314 mode: WifiDeviceMode,
2315}
2316
2317impl WifiTxToken {
2318 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
2321 where
2322 F: FnOnce(&mut [u8]) -> R,
2323 {
2324 self.mode.increase_in_flight_counter();
2325
2326 static mut BUFFER: [u8; MTU] = [0u8; MTU];
2330
2331 let buffer = unsafe { &mut BUFFER[..len] };
2332
2333 let res = f(buffer);
2334
2335 esp_wifi_send_data(self.mode.interface(), buffer);
2336
2337 res
2338 }
2339}
2340
2341#[cfg(feature = "smoltcp")]
2342impl TxToken for WifiTxToken {
2343 fn consume<R, F>(self, len: usize, f: F) -> R
2344 where
2345 F: FnOnce(&mut [u8]) -> R,
2346 {
2347 self.consume_token(len, f)
2348 }
2349}
2350
2351pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
2356 trace!("sending... {} bytes", data.len());
2357 dump_packet_info(data);
2358
2359 let len = data.len() as u16;
2360 let ptr = data.as_mut_ptr().cast();
2361
2362 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
2363
2364 if res != 0 {
2365 warn!("esp_wifi_internal_tx {}", res);
2366 decrement_inflight_counter();
2367 } else {
2368 trace!("esp_wifi_internal_tx ok");
2369 }
2370}
2371
2372fn apply_ap_config(config: &AccessPointConfiguration) -> Result<(), WifiError> {
2373 let mut cfg = wifi_config_t {
2374 ap: wifi_ap_config_t {
2375 ssid: [0; 32],
2376 password: [0; 64],
2377 ssid_len: 0,
2378 channel: config.channel,
2379 authmode: config.auth_method.to_raw(),
2380 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
2381 max_connection: config.max_connections as u8,
2382 beacon_interval: 100,
2383 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
2384 ftm_responder: false,
2385 pmf_cfg: wifi_pmf_config_t {
2386 capable: true,
2387 required: false,
2388 },
2389 sae_pwe_h2e: 0,
2390 csa_count: 3,
2391 dtim_period: 2,
2392 },
2393 };
2394
2395 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2396 return Err(WifiError::InternalError(
2397 InternalWifiError::EspErrInvalidArg,
2398 ));
2399 }
2400
2401 unsafe {
2402 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2403 cfg.ap.ssid_len = config.ssid.len() as u8;
2404 cfg.ap.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2405
2406 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
2407 }
2408}
2409
2410fn apply_sta_config(config: &ClientConfiguration) -> Result<(), WifiError> {
2411 let mut cfg = wifi_config_t {
2412 sta: wifi_sta_config_t {
2413 ssid: [0; 32],
2414 password: [0; 64],
2415 scan_method: crate::CONFIG.scan_method,
2416 bssid_set: config.bssid.is_some(),
2417 bssid: config.bssid.unwrap_or_default(),
2418 channel: config.channel.unwrap_or(0),
2419 listen_interval: crate::CONFIG.listen_interval,
2420 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2421 threshold: wifi_scan_threshold_t {
2422 rssi: -99,
2423 authmode: config.auth_method.to_raw(),
2424 },
2425 pmf_cfg: wifi_pmf_config_t {
2426 capable: true,
2427 required: false,
2428 },
2429 sae_pwe_h2e: 3,
2430 _bitfield_align_1: [0; 0],
2431 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2432 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2433 _bitfield_align_2: [0; 0],
2434 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2435 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2437 },
2438 };
2439
2440 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2441 return Err(WifiError::InternalError(
2442 InternalWifiError::EspErrInvalidArg,
2443 ));
2444 }
2445
2446 unsafe {
2447 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2448 cfg.sta.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2449
2450 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
2451 }
2452}
2453
2454fn apply_sta_eap_config(config: &EapClientConfiguration) -> Result<(), WifiError> {
2455 let mut cfg = wifi_config_t {
2456 sta: wifi_sta_config_t {
2457 ssid: [0; 32],
2458 password: [0; 64],
2459 scan_method: crate::CONFIG.scan_method,
2460 bssid_set: config.bssid.is_some(),
2461 bssid: config.bssid.unwrap_or_default(),
2462 channel: config.channel.unwrap_or(0),
2463 listen_interval: crate::CONFIG.listen_interval,
2464 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2465 threshold: wifi_scan_threshold_t {
2466 rssi: -99,
2467 authmode: config.auth_method.to_raw(),
2468 },
2469 pmf_cfg: wifi_pmf_config_t {
2470 capable: true,
2471 required: false,
2472 },
2473 sae_pwe_h2e: 3,
2474 _bitfield_align_1: [0; 0],
2475 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2476 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2477 _bitfield_align_2: [0; 0],
2478 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2479 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2481 },
2482 };
2483
2484 unsafe {
2485 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2486 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
2487
2488 if let Some(identity) = &config.identity {
2489 esp_wifi_result!(esp_eap_client_set_identity(
2490 identity.as_str().as_ptr(),
2491 identity.len() as i32
2492 ))?;
2493 } else {
2494 esp_eap_client_clear_identity();
2495 }
2496
2497 if let Some(username) = &config.username {
2498 esp_wifi_result!(esp_eap_client_set_username(
2499 username.as_str().as_ptr(),
2500 username.len() as i32
2501 ))?;
2502 } else {
2503 esp_eap_client_clear_username();
2504 }
2505
2506 if let Some(password) = &config.password {
2507 esp_wifi_result!(esp_eap_client_set_password(
2508 password.as_str().as_ptr(),
2509 password.len() as i32
2510 ))?;
2511 } else {
2512 esp_eap_client_clear_password();
2513 }
2514
2515 if let Some(new_password) = &config.new_password {
2516 esp_wifi_result!(esp_eap_client_set_new_password(
2517 new_password.as_str().as_ptr(),
2518 new_password.len() as i32
2519 ))?;
2520 } else {
2521 esp_eap_client_clear_new_password();
2522 }
2523
2524 if let Some(pac_file) = &config.pac_file {
2525 esp_wifi_result!(esp_eap_client_set_pac_file(
2526 pac_file.as_ptr(),
2527 pac_file.len() as i32
2528 ))?;
2529 }
2530
2531 if let Some(phase2_method) = &config.ttls_phase2_method {
2532 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
2533 phase2_method.to_raw()
2534 ))?;
2535 }
2536
2537 if let Some(ca_cert) = config.ca_cert {
2538 esp_wifi_result!(esp_eap_client_set_ca_cert(
2539 ca_cert.as_ptr(),
2540 ca_cert.len() as i32
2541 ))?;
2542 } else {
2543 esp_eap_client_clear_ca_cert();
2544 }
2545
2546 if let Some((cert, key, password)) = config.certificate_and_key {
2547 let (pwd, pwd_len) = if let Some(pwd) = password {
2548 (pwd.as_ptr(), pwd.len() as i32)
2549 } else {
2550 (core::ptr::null(), 0)
2551 };
2552
2553 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
2554 cert.as_ptr(),
2555 cert.len() as i32,
2556 key.as_ptr(),
2557 key.len() as i32,
2558 pwd,
2559 pwd_len,
2560 ))?;
2561 } else {
2562 esp_eap_client_clear_certificate_and_key();
2563 }
2564
2565 if let Some(cfg) = &config.eap_fast_config {
2566 let params = esp_eap_fast_config {
2567 fast_provisioning: cfg.fast_provisioning as i32,
2568 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
2569 fast_pac_format_binary: cfg.fast_pac_format_binary,
2570 };
2571 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
2572 }
2573
2574 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
2575
2576 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
2583
2584 Ok(())
2585 }
2586}
2587
2588fn dump_packet_info(_buffer: &mut [u8]) {
2589 #[cfg(dump_packets)]
2590 {
2591 info!("@WIFIFRAME {:?}", _buffer);
2592 }
2593}
2594
2595#[doc(hidden)]
2596#[macro_export]
2597macro_rules! esp_wifi_result {
2598 ($value:expr) => {{
2599 use num_traits::FromPrimitive;
2600 let result = $value;
2601 if result != esp_wifi_sys::include::ESP_OK as i32 {
2602 warn!("{} returned an error: {}", stringify!($value), result);
2603 Err(WifiError::InternalError(unwrap!(FromPrimitive::from_i32(
2604 result
2605 ))))
2606 } else {
2607 Ok::<(), WifiError>(())
2608 }
2609 }};
2610}
2611
2612pub(crate) mod embassy {
2613 use embassy_net_driver::{Capabilities, Driver, HardwareAddress, RxToken, TxToken};
2614 use esp_hal::asynch::AtomicWaker;
2615
2616 use super::*;
2617
2618 pub(crate) static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
2621
2622 pub(crate) static AP_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2623 pub(crate) static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2624
2625 pub(crate) static STA_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2626 pub(crate) static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2627
2628 impl RxToken for WifiRxToken {
2629 fn consume<R, F>(self, f: F) -> R
2630 where
2631 F: FnOnce(&mut [u8]) -> R,
2632 {
2633 self.consume_token(f)
2634 }
2635 }
2636
2637 impl TxToken for WifiTxToken {
2638 fn consume<R, F>(self, len: usize, f: F) -> R
2639 where
2640 F: FnOnce(&mut [u8]) -> R,
2641 {
2642 self.consume_token(len, f)
2643 }
2644 }
2645
2646 impl Driver for WifiDevice<'_> {
2647 type RxToken<'a>
2648 = WifiRxToken
2649 where
2650 Self: 'a;
2651 type TxToken<'a>
2652 = WifiTxToken
2653 where
2654 Self: 'a;
2655
2656 fn receive(
2657 &mut self,
2658 cx: &mut core::task::Context<'_>,
2659 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2660 self.mode.register_receive_waker(cx);
2661 self.mode.register_transmit_waker(cx);
2662 self.mode.rx_token()
2663 }
2664
2665 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
2666 self.mode.register_transmit_waker(cx);
2667 self.mode.tx_token()
2668 }
2669
2670 fn link_state(
2671 &mut self,
2672 cx: &mut core::task::Context<'_>,
2673 ) -> embassy_net_driver::LinkState {
2674 self.mode.register_link_state_waker(cx);
2675 self.mode.link_state()
2676 }
2677
2678 fn capabilities(&self) -> Capabilities {
2679 let mut caps = Capabilities::default();
2680 caps.max_transmission_unit = MTU;
2681 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2682 None
2683 } else {
2684 Some(crate::CONFIG.max_burst_size)
2685 };
2686 caps
2687 }
2688
2689 fn hardware_address(&self) -> HardwareAddress {
2690 HardwareAddress::Ethernet(self.mac_address())
2691 }
2692 }
2693}
2694
2695pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
2696 esp_wifi_result!(unsafe { esp_wifi_sys::include::esp_wifi_set_ps(ps.into()) })?;
2697 Ok(())
2698}
2699
2700struct FreeApListOnDrop;
2701impl FreeApListOnDrop {
2702 pub fn defuse(self) {
2703 core::mem::forget(self);
2704 }
2705}
2706
2707impl Drop for FreeApListOnDrop {
2708 fn drop(&mut self) {
2709 unsafe {
2710 include::esp_wifi_clear_ap_list();
2711 }
2712 }
2713}
2714
2715#[non_exhaustive]
2716pub struct Interfaces<'d> {
2717 pub sta: WifiDevice<'d>,
2718 pub ap: WifiDevice<'d>,
2719}
2720
2721pub fn new<'d>(
2725 inited: &'d EspWifiController<'d>,
2726 _device: impl crate::hal::peripheral::Peripheral<P = crate::hal::peripherals::WIFI> + 'd,
2727) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> {
2728 if !inited.wifi() {
2729 crate::wifi::wifi_init()?;
2730 }
2731 Ok((
2732 WifiController {
2733 _phantom: Default::default(),
2734 #[cfg(feature = "sniffer")]
2735 sniffer_taken: AtomicBool::new(false),
2736 },
2737 Interfaces {
2738 sta: WifiDevice {
2739 _phantom: Default::default(),
2740 mode: WifiDeviceMode::Sta,
2741 },
2742 ap: WifiDevice {
2743 _phantom: Default::default(),
2744 mode: WifiDeviceMode::Ap,
2745 },
2746 },
2747 ))
2748}
2749
2750#[non_exhaustive]
2751pub struct WifiController<'d> {
2752 _phantom: PhantomData<&'d ()>,
2753 #[cfg(feature = "sniffer")]
2754 sniffer_taken: AtomicBool,
2755}
2756
2757impl Drop for WifiController<'_> {
2758 fn drop(&mut self) {
2759 if unwrap!(
2760 crate::flags::WIFI.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
2761 Some(x.saturating_sub(1))
2762 })
2763 ) == 0
2764 {
2765 if let Err(e) = crate::wifi::wifi_deinit() {
2766 warn!("Failed to cleanly deinit wifi: {:?}", e);
2767 }
2768 }
2769 }
2770}
2771
2772impl WifiController<'_> {
2773 #[cfg(feature = "sniffer")]
2776 pub fn take_sniffer(&self) -> Option<Sniffer> {
2777 if !self.sniffer_taken.fetch_or(true, Ordering::Acquire) {
2778 Some(Sniffer::new())
2779 } else {
2780 None
2781 }
2782 }
2783
2784 #[cfg(feature = "csi")]
2786 pub fn set_csi(
2787 &mut self,
2788 mut csi: CsiConfig,
2789 cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send,
2790 ) -> Result<(), WifiError> {
2791 csi.apply_config()?;
2792 csi.set_receive_cb(cb)?;
2793 csi.set_csi(true)?;
2794
2795 Ok(())
2796 }
2797
2798 pub fn set_protocol(&mut self, protocols: EnumSet<Protocol>) -> Result<(), WifiError> {
2813 let protocol = protocols
2814 .into_iter()
2815 .map(|v| match v {
2816 Protocol::P802D11B => WIFI_PROTOCOL_11B,
2817 Protocol::P802D11BG => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G,
2818 Protocol::P802D11BGN => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
2819 Protocol::P802D11BGNLR => {
2820 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR
2821 }
2822 Protocol::P802D11LR => WIFI_PROTOCOL_LR,
2823 Protocol::P802D11BGNAX => {
2824 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX
2825 }
2826 })
2827 .fold(0, |combined, protocol| combined | protocol) as u8;
2828
2829 let mode = self.mode()?;
2830 if mode.is_sta() {
2831 esp_wifi_result!(unsafe {
2832 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_STA, protocol)
2833 })?;
2834 }
2835 if mode.is_ap() {
2836 esp_wifi_result!(unsafe {
2837 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_AP, protocol)
2838 })?;
2839 }
2840
2841 Ok(())
2842 }
2843
2844 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
2846 apply_power_saving(ps)
2847 }
2848
2849 pub fn scan_with_config_sync<const N: usize>(
2851 &mut self,
2852 config: ScanConfig<'_>,
2853 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), WifiError> {
2854 esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?;
2855
2856 let count = self.scan_result_count()?;
2857 let result = self.scan_results()?;
2858
2859 Ok((result, count))
2860 }
2861
2862 fn scan_result_count(&mut self) -> Result<usize, WifiError> {
2863 let mut bss_total: u16 = 0;
2864
2865 let guard = FreeApListOnDrop;
2867
2868 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
2869
2870 guard.defuse();
2871
2872 Ok(bss_total as usize)
2873 }
2874
2875 fn scan_results<const N: usize>(
2876 &mut self,
2877 ) -> Result<heapless::Vec<AccessPointInfo, N>, WifiError> {
2878 let mut scanned = heapless::Vec::<AccessPointInfo, N>::new();
2879 let mut bss_total: u16 = N as u16;
2880
2881 let mut records: [MaybeUninit<include::wifi_ap_record_t>; N] = [MaybeUninit::uninit(); N];
2882
2883 let guard = FreeApListOnDrop;
2885
2886 unsafe {
2887 esp_wifi_result!(include::esp_wifi_scan_get_ap_records(
2888 &mut bss_total,
2889 records[0].as_mut_ptr(),
2890 ))?
2891 };
2892
2893 guard.defuse();
2894
2895 for i in 0..bss_total {
2896 let record = unsafe { MaybeUninit::assume_init_ref(&records[i as usize]) };
2897 let ap_info = convert_ap_info(record);
2898
2899 scanned.push(ap_info).ok();
2900 }
2901
2902 Ok(scanned)
2903 }
2904
2905 pub fn scan_n<const N: usize>(
2907 &mut self,
2908 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), WifiError> {
2909 self.scan_with_config_sync(Default::default())
2910 }
2911
2912 pub fn start(&mut self) -> Result<(), WifiError> {
2914 crate::wifi::wifi_start()
2915 }
2916
2917 pub fn stop(&mut self) -> Result<(), WifiError> {
2919 self.stop_impl()
2920 }
2921
2922 pub fn connect(&mut self) -> Result<(), WifiError> {
2930 self.connect_impl()
2931 }
2932
2933 pub fn disconnect(&mut self) -> Result<(), WifiError> {
2935 self.disconnect_impl()
2936 }
2937
2938 pub fn capabilities(&self) -> Result<EnumSet<crate::wifi::Capability>, WifiError> {
2940 let caps =
2941 enumset::enum_set! { Capability::Client | Capability::AccessPoint | Capability::Mixed };
2942 Ok(caps)
2943 }
2944
2945 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), WifiError> {
2950 let mode = match conf {
2951 Configuration::None => {
2952 return Err(WifiError::InternalError(
2953 InternalWifiError::EspErrInvalidArg,
2954 ));
2955 }
2956 Configuration::Client(_) => wifi_mode_t_WIFI_MODE_STA,
2957 Configuration::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
2958 Configuration::Mixed(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
2959 Configuration::EapClient(_) => wifi_mode_t_WIFI_MODE_STA,
2960 };
2961
2962 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
2963
2964 match conf {
2965 Configuration::None => {
2966 return Err(WifiError::InternalError(
2967 InternalWifiError::EspErrInvalidArg,
2968 ));
2969 }
2970 Configuration::Client(config) => {
2971 apply_sta_config(config)?;
2972 }
2973 Configuration::AccessPoint(config) => {
2974 apply_ap_config(config)?;
2975 }
2976 Configuration::Mixed(sta_config, ap_config) => {
2977 apply_ap_config(ap_config)?;
2978 apply_sta_config(sta_config)?;
2979 }
2980 Configuration::EapClient(config) => {
2981 apply_sta_eap_config(config)?;
2982 }
2983 };
2984
2985 Ok(())
2986 }
2987
2988 pub fn set_mode(&mut self, mode: WifiMode) -> Result<(), WifiError> {
2992 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?;
2993 Ok(())
2994 }
2995
2996 fn stop_impl(&mut self) -> Result<(), WifiError> {
2997 esp_wifi_result!(unsafe { esp_wifi_stop() })
2998 }
2999
3000 fn connect_impl(&mut self) -> Result<(), WifiError> {
3001 esp_wifi_result!(unsafe { esp_wifi_connect() })
3002 }
3003
3004 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
3005 esp_wifi_result!(unsafe { esp_wifi_disconnect() })
3006 }
3007
3008 pub fn is_started(&self) -> Result<bool, WifiError> {
3013 if matches!(
3014 crate::wifi::sta_state(),
3015 WifiState::StaStarted | WifiState::StaConnected | WifiState::StaDisconnected
3016 ) {
3017 return Ok(true);
3018 }
3019 if matches!(crate::wifi::ap_state(), WifiState::ApStarted) {
3020 return Ok(true);
3021 }
3022 Ok(false)
3023 }
3024
3025 pub fn is_connected(&self) -> Result<bool, WifiError> {
3030 match crate::wifi::sta_state() {
3031 crate::wifi::WifiState::StaConnected => Ok(true),
3032 crate::wifi::WifiState::StaDisconnected => Err(WifiError::Disconnected),
3033 _ => Ok(false),
3035 }
3036 }
3037
3038 fn mode(&self) -> Result<WifiMode, WifiError> {
3039 WifiMode::current()
3040 }
3041
3042 pub async fn scan_n_async<const N: usize>(
3044 &mut self,
3045 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), WifiError> {
3046 self.scan_with_config_async(Default::default()).await
3047 }
3048
3049 pub async fn scan_with_config_async<const N: usize>(
3051 &mut self,
3052 config: ScanConfig<'_>,
3053 ) -> Result<(heapless::Vec<AccessPointInfo, N>, usize), WifiError> {
3054 Self::clear_events(WifiEvent::ScanDone);
3055 esp_wifi_result!(wifi_start_scan(false, config))?;
3056
3057 let guard = FreeApListOnDrop;
3059 WifiEventFuture::new(WifiEvent::ScanDone).await;
3060
3061 guard.defuse();
3062
3063 let count = self.scan_result_count()?;
3064 let result = self.scan_results()?;
3065
3066 Ok((result, count))
3067 }
3068
3069 pub async fn start_async(&mut self) -> Result<(), WifiError> {
3071 let mut events = enumset::enum_set! {};
3072
3073 let mode = self.mode()?;
3074 if mode.is_ap() {
3075 events |= WifiEvent::ApStart;
3076 }
3077 if mode.is_sta() {
3078 events |= WifiEvent::StaStart;
3079 }
3080
3081 Self::clear_events(events);
3082
3083 wifi_start()?;
3084
3085 self.wait_for_all_events(events, false).await;
3086
3087 Ok(())
3088 }
3089
3090 pub async fn stop_async(&mut self) -> Result<(), WifiError> {
3092 let mut events = enumset::enum_set! {};
3093
3094 let mode = self.mode()?;
3095 if mode.is_ap() {
3096 events |= WifiEvent::ApStop;
3097 }
3098 if mode.is_sta() {
3099 events |= WifiEvent::StaStop;
3100 }
3101
3102 Self::clear_events(events);
3103
3104 crate::wifi::WifiController::stop_impl(self)?;
3105
3106 self.wait_for_all_events(events, false).await;
3107
3108 reset_ap_state();
3109 reset_sta_state();
3110
3111 Ok(())
3112 }
3113
3114 pub async fn connect_async(&mut self) -> Result<(), WifiError> {
3116 Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected);
3117
3118 let err = crate::wifi::WifiController::connect_impl(self).err();
3119
3120 if MultiWifiEventFuture::new(WifiEvent::StaConnected | WifiEvent::StaDisconnected)
3121 .await
3122 .contains(WifiEvent::StaDisconnected)
3123 {
3124 Err(err.unwrap_or(WifiError::Disconnected))
3125 } else {
3126 Ok(())
3127 }
3128 }
3129
3130 pub async fn disconnect_async(&mut self) -> Result<(), WifiError> {
3133 if !matches!(self.is_connected(), Ok(true)) {
3137 return Ok(());
3138 }
3139
3140 Self::clear_events(WifiEvent::StaDisconnected);
3141 crate::wifi::WifiController::disconnect_impl(self)?;
3142 WifiEventFuture::new(WifiEvent::StaDisconnected).await;
3143
3144 Ok(())
3145 }
3146
3147 fn clear_events(events: impl Into<EnumSet<WifiEvent>>) {
3148 WIFI_EVENTS.with(|evts| evts.get_mut().remove_all(events.into()));
3149 }
3150
3151 pub async fn wait_for_event(&mut self, event: WifiEvent) {
3153 Self::clear_events(event);
3154 WifiEventFuture::new(event).await
3155 }
3156
3157 pub async fn wait_for_events(
3160 &mut self,
3161 events: EnumSet<WifiEvent>,
3162 clear_pending: bool,
3163 ) -> EnumSet<WifiEvent> {
3164 if clear_pending {
3165 Self::clear_events(events);
3166 }
3167 MultiWifiEventFuture::new(events).await
3168 }
3169
3170 pub async fn wait_for_all_events(
3172 &mut self,
3173 mut events: EnumSet<WifiEvent>,
3174 clear_pending: bool,
3175 ) {
3176 if clear_pending {
3177 Self::clear_events(events);
3178 }
3179
3180 while !events.is_empty() {
3181 let fired = MultiWifiEventFuture::new(events).await;
3182 events -= fired;
3183 }
3184 }
3185}
3186
3187impl WifiEvent {
3188 pub(crate) fn waker(&self) -> &'static AtomicWaker {
3189 static WAKER: AtomicWaker = AtomicWaker::new();
3193 &WAKER
3194 }
3195}
3196
3197#[must_use = "futures do nothing unless you `.await` or poll them"]
3198pub(crate) struct WifiEventFuture {
3199 event: WifiEvent,
3200}
3201
3202impl WifiEventFuture {
3203 pub fn new(event: WifiEvent) -> Self {
3205 Self { event }
3206 }
3207}
3208
3209impl core::future::Future for WifiEventFuture {
3210 type Output = ();
3211
3212 fn poll(
3213 self: core::pin::Pin<&mut Self>,
3214 cx: &mut core::task::Context<'_>,
3215 ) -> Poll<Self::Output> {
3216 self.event.waker().register(cx.waker());
3217 if WIFI_EVENTS.with(|events| events.get_mut().remove(self.event)) {
3218 Poll::Ready(())
3219 } else {
3220 Poll::Pending
3221 }
3222 }
3223}
3224
3225#[must_use = "futures do nothing unless you `.await` or poll them"]
3226pub(crate) struct MultiWifiEventFuture {
3227 event: EnumSet<WifiEvent>,
3228}
3229
3230impl MultiWifiEventFuture {
3231 pub fn new(event: EnumSet<WifiEvent>) -> Self {
3233 Self { event }
3234 }
3235}
3236
3237impl core::future::Future for MultiWifiEventFuture {
3238 type Output = EnumSet<WifiEvent>;
3239
3240 fn poll(
3241 self: core::pin::Pin<&mut Self>,
3242 cx: &mut core::task::Context<'_>,
3243 ) -> Poll<Self::Output> {
3244 let output = WIFI_EVENTS.with(|events| {
3245 let events = events.get_mut();
3246 let active = events.intersection(self.event);
3247 events.remove_all(active);
3248 active
3249 });
3250 if output.is_empty() {
3251 for event in self.event.iter() {
3252 event.waker().register(cx.waker());
3253 }
3254
3255 Poll::Pending
3256 } else {
3257 Poll::Ready(output)
3258 }
3259 }
3260}