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}