1pub mod event;
4mod internal;
5pub(crate) mod os_adapter;
6pub(crate) mod state;
7use alloc::{collections::vec_deque::VecDeque, string::String};
8use core::{
9 fmt::Debug,
10 marker::PhantomData,
11 mem::{self, MaybeUninit},
12 ptr::addr_of,
13 task::Poll,
14 time::Duration,
15};
16
17use enumset::{EnumSet, EnumSetType};
18use esp_hal::{asynch::AtomicWaker, sync::Locked};
19use esp_wifi_sys::include::{
20 WIFI_PROTOCOL_11AX,
21 WIFI_PROTOCOL_11B,
22 WIFI_PROTOCOL_11G,
23 WIFI_PROTOCOL_11N,
24 WIFI_PROTOCOL_LR,
25 esp_eap_client_clear_ca_cert,
26 esp_eap_client_clear_certificate_and_key,
27 esp_eap_client_clear_identity,
28 esp_eap_client_clear_new_password,
29 esp_eap_client_clear_password,
30 esp_eap_client_clear_username,
31 esp_eap_client_set_ca_cert,
32 esp_eap_client_set_certificate_and_key,
33 esp_eap_client_set_disable_time_check,
34 esp_eap_client_set_fast_params,
35 esp_eap_client_set_identity,
36 esp_eap_client_set_new_password,
37 esp_eap_client_set_pac_file,
38 esp_eap_client_set_password,
39 esp_eap_client_set_ttls_phase2_method,
40 esp_eap_client_set_username,
41 esp_eap_fast_config,
42 esp_wifi_sta_enterprise_enable,
43 wifi_pkt_rx_ctrl_t,
44 wifi_scan_channel_bitmap_t,
45};
46#[cfg(feature = "sniffer")]
47use esp_wifi_sys::include::{
48 esp_wifi_80211_tx,
49 esp_wifi_set_promiscuous,
50 esp_wifi_set_promiscuous_rx_cb,
51 wifi_promiscuous_pkt_t,
52 wifi_promiscuous_pkt_type_t,
53};
54use num_derive::FromPrimitive;
55#[doc(hidden)]
56pub(crate) use os_adapter::*;
57use portable_atomic::{AtomicUsize, Ordering};
58#[cfg(feature = "serde")]
59use serde::{Deserialize, Serialize};
60#[cfg(feature = "smoltcp")]
61use smoltcp::phy::{Device, DeviceCapabilities, RxToken, TxToken};
62pub use state::*;
63
64use crate::{
65 EspWifiController,
66 common_adapter::*,
67 config::PowerSaveMode,
68 esp_wifi_result,
69 hal::ram,
70 wifi::private::EspWifiPacketBuffer,
71};
72
73const MTU: usize = crate::CONFIG.mtu;
74
75#[cfg(all(feature = "csi", esp32c6))]
76use crate::binary::include::wifi_csi_acquire_config_t;
77#[cfg(feature = "csi")]
78pub use crate::binary::include::wifi_csi_info_t;
79#[cfg(feature = "csi")]
80use crate::binary::include::{
81 esp_wifi_set_csi,
82 esp_wifi_set_csi_config,
83 esp_wifi_set_csi_rx_cb,
84 wifi_csi_config_t,
85};
86use crate::binary::{
87 c_types,
88 include::{
89 self,
90 __BindgenBitfieldUnit,
91 esp_err_t,
92 esp_interface_t_ESP_IF_WIFI_AP,
93 esp_interface_t_ESP_IF_WIFI_STA,
94 esp_supplicant_deinit,
95 esp_supplicant_init,
96 esp_wifi_connect,
97 esp_wifi_deinit_internal,
98 esp_wifi_disconnect,
99 esp_wifi_get_mode,
100 esp_wifi_init_internal,
101 esp_wifi_internal_free_rx_buffer,
102 esp_wifi_internal_reg_rxcb,
103 esp_wifi_internal_tx,
104 esp_wifi_scan_start,
105 esp_wifi_set_config,
106 esp_wifi_set_country,
107 esp_wifi_set_mode,
108 esp_wifi_set_protocol,
109 esp_wifi_set_tx_done_cb,
110 esp_wifi_sta_get_rssi,
111 esp_wifi_start,
112 esp_wifi_stop,
113 g_wifi_default_wpa_crypto_funcs,
114 wifi_active_scan_time_t,
115 wifi_ap_config_t,
116 wifi_auth_mode_t,
117 wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
118 wifi_config_t,
119 wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
120 wifi_country_t,
121 wifi_interface_t,
122 wifi_interface_t_WIFI_IF_AP,
123 wifi_interface_t_WIFI_IF_STA,
124 wifi_mode_t,
125 wifi_mode_t_WIFI_MODE_AP,
126 wifi_mode_t_WIFI_MODE_APSTA,
127 wifi_mode_t_WIFI_MODE_NULL,
128 wifi_mode_t_WIFI_MODE_STA,
129 wifi_pmf_config_t,
130 wifi_scan_config_t,
131 wifi_scan_threshold_t,
132 wifi_scan_time_t,
133 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
134 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
135 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
136 wifi_sta_config_t,
137 },
138};
139
140#[derive(EnumSetType, Debug, PartialOrd)]
142#[cfg_attr(feature = "defmt", derive(defmt::Format))]
143#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
144#[derive(Default)]
145#[allow(clippy::upper_case_acronyms)] pub enum AuthMethod {
147 None,
149
150 WEP,
152
153 WPA,
155
156 #[default]
158 WPA2Personal,
159
160 WPAWPA2Personal,
162
163 WPA2Enterprise,
165
166 WPA3Personal,
168
169 WPA2WPA3Personal,
171
172 WAPIPersonal,
174}
175
176#[derive(EnumSetType, Debug, PartialOrd)]
178#[cfg_attr(feature = "defmt", derive(defmt::Format))]
179#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
180#[derive(Default)]
181pub enum Protocol {
182 P802D11B,
184
185 P802D11BG,
187
188 #[default]
190 P802D11BGN,
191
192 P802D11BGNLR,
194
195 P802D11LR,
197
198 P802D11BGNAX,
200}
201
202#[derive(EnumSetType, Debug, PartialOrd)]
204#[cfg_attr(feature = "defmt", derive(defmt::Format))]
205#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
206#[derive(Default)]
207pub enum SecondaryChannel {
208 #[default]
211 None,
212
213 Above,
215
216 Below,
218}
219
220#[derive(Clone, Debug, Default, PartialEq, Eq)]
222#[cfg_attr(feature = "defmt", derive(defmt::Format))]
223#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
224pub struct AccessPointInfo {
225 #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
228 pub ssid: String,
229
230 pub bssid: [u8; 6],
232
233 pub channel: u8,
235
236 pub secondary_channel: SecondaryChannel,
238
239 pub signal_strength: i8,
241
242 pub auth_method: Option<AuthMethod>,
244}
245
246#[derive(Clone, PartialEq, Eq)]
248#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
249pub struct AccessPointConfiguration {
250 pub ssid: String,
252
253 pub ssid_hidden: bool,
255
256 pub channel: u8,
258
259 pub secondary_channel: Option<u8>,
261
262 pub protocols: EnumSet<Protocol>,
264
265 pub auth_method: AuthMethod,
267
268 pub password: String,
270
271 pub max_connections: u16,
273}
274
275impl AccessPointConfiguration {
276 fn validate(&self) -> Result<(), WifiError> {
277 if self.ssid.len() > 32 {
278 return Err(WifiError::InvalidArguments);
279 }
280
281 if self.password.len() > 64 {
282 return Err(WifiError::InvalidArguments);
283 }
284
285 Ok(())
286 }
287}
288
289impl Default for AccessPointConfiguration {
290 fn default() -> Self {
291 Self {
292 ssid: String::from("iot-device"),
293 ssid_hidden: false,
294 channel: 1,
295 secondary_channel: None,
296 protocols: (Protocol::P802D11B | Protocol::P802D11BG | Protocol::P802D11BGN),
297 auth_method: AuthMethod::None,
298 password: String::new(),
299 max_connections: 255,
300 }
301 }
302}
303
304impl core::fmt::Debug for AccessPointConfiguration {
305 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
306 f.debug_struct("AccessPointConfiguration")
307 .field("ssid", &self.ssid)
308 .field("ssid_hidden", &self.ssid_hidden)
309 .field("channel", &self.channel)
310 .field("secondary_channel", &self.secondary_channel)
311 .field("protocols", &self.protocols)
312 .field("auth_method", &self.auth_method)
313 .field("password", &"**REDACTED**")
314 .field("max_connections", &self.max_connections)
315 .finish()
316 }
317}
318
319#[cfg(feature = "defmt")]
320impl defmt::Format for AccessPointConfiguration {
321 fn format(&self, fmt: defmt::Formatter<'_>) {
322 #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Default)]
323 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
324 pub struct ProtocolSet(EnumSet<Protocol>);
325
326 #[cfg(feature = "defmt")]
327 impl defmt::Format for ProtocolSet {
328 fn format(&self, fmt: defmt::Formatter<'_>) {
329 for (i, p) in self.0.into_iter().enumerate() {
330 if i > 0 {
331 defmt::write!(fmt, " ");
332 }
333 defmt::write!(fmt, "{}", p);
334 }
335 }
336 }
337
338 let protocol_set = ProtocolSet(self.protocols);
339
340 defmt::write!(
341 fmt,
342 "AccessPointConfiguration {{\
343 ssid: {}, \
344 ssid_hidden: {}, \
345 channel: {}, \
346 secondary_channel: {}, \
347 protocols: {}, \
348 auth_method: {}, \
349 password: **REDACTED**, \
350 max_connections: {}, \
351 }}",
352 self.ssid.as_str(),
353 self.ssid_hidden,
354 self.channel,
355 self.secondary_channel,
356 protocol_set,
357 self.auth_method,
358 self.max_connections
359 );
360 }
361}
362
363#[derive(Clone, PartialEq, Eq, Default)]
365#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
366pub struct ClientConfiguration {
367 pub ssid: String,
369
370 pub bssid: Option<[u8; 6]>,
372
373 pub auth_method: AuthMethod,
376
377 pub password: String,
379
380 pub channel: Option<u8>,
382}
383
384impl ClientConfiguration {
385 fn validate(&self) -> Result<(), WifiError> {
386 if self.ssid.len() > 32 {
387 return Err(WifiError::InvalidArguments);
388 }
389
390 if self.password.len() > 64 {
391 return Err(WifiError::InvalidArguments);
392 }
393
394 Ok(())
395 }
396}
397
398impl core::fmt::Debug for ClientConfiguration {
399 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
400 f.debug_struct("ClientConfiguration")
401 .field("ssid", &self.ssid)
402 .field("bssid", &self.bssid)
403 .field("auth_method", &self.auth_method)
404 .field("password", &"**REDACTED**")
405 .field("channel", &self.channel)
406 .finish()
407 }
408}
409
410#[cfg(feature = "defmt")]
411impl defmt::Format for ClientConfiguration {
412 fn format(&self, fmt: defmt::Formatter<'_>) {
413 defmt::write!(
414 fmt,
415 "ClientConfiguration {{\
416 ssid: {}, \
417 bssid: {:?}, \
418 auth_method: {:?}, \
419 password: **REDACTED**, \
420 channel: {:?}, \
421 }}",
422 self.ssid.as_str(),
423 self.bssid,
424 self.auth_method,
425 self.channel
426 )
427 }
428}
429
430#[derive(Clone, Debug, PartialEq, Eq)]
432#[cfg_attr(feature = "defmt", derive(defmt::Format))]
433#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
434pub struct EapFastConfig {
435 pub fast_provisioning: u8,
437 pub fast_max_pac_list_len: u8,
439 pub fast_pac_format_binary: bool,
441}
442
443#[derive(Debug, Clone, PartialEq, Eq)]
445#[cfg_attr(feature = "defmt", derive(defmt::Format))]
446#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
447pub enum TtlsPhase2Method {
448 Eap,
450
451 Mschapv2,
453
454 Mschap,
456
457 Pap,
459
460 Chap,
462}
463
464impl TtlsPhase2Method {
465 fn to_raw(&self) -> u32 {
467 match self {
468 TtlsPhase2Method::Eap => {
469 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_EAP
470 }
471 TtlsPhase2Method::Mschapv2 => {
472 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAPV2
473 }
474 TtlsPhase2Method::Mschap => {
475 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAP
476 }
477 TtlsPhase2Method::Pap => {
478 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_PAP
479 }
480 TtlsPhase2Method::Chap => {
481 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_CHAP
482 }
483 }
484 }
485}
486
487#[derive(Clone, PartialEq, Eq)]
489#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
490pub struct EapClientConfiguration {
491 pub ssid: String,
493
494 pub bssid: Option<[u8; 6]>,
496
497 pub auth_method: AuthMethod,
500
501 pub identity: Option<String>,
503
504 pub username: Option<String>,
507
508 pub password: Option<String>,
510
511 pub new_password: Option<String>,
514
515 pub eap_fast_config: Option<EapFastConfig>,
517
518 pub pac_file: Option<&'static [u8]>,
520
521 pub time_check: bool,
524
525 pub ca_cert: Option<&'static [u8]>,
528
529 #[allow(clippy::type_complexity)]
532 pub certificate_and_key: Option<(&'static [u8], &'static [u8], Option<&'static [u8]>)>,
533
534 pub ttls_phase2_method: Option<TtlsPhase2Method>,
536
537 pub channel: Option<u8>,
539}
540
541impl EapClientConfiguration {
542 fn validate(&self) -> Result<(), WifiError> {
543 if self.ssid.len() > 32 {
544 return Err(WifiError::InvalidArguments);
545 }
546
547 if self.identity.as_ref().unwrap_or(&String::new()).len() > 128 {
548 return Err(WifiError::InvalidArguments);
549 }
550
551 if self.username.as_ref().unwrap_or(&String::new()).len() > 128 {
552 return Err(WifiError::InvalidArguments);
553 }
554
555 if self.password.as_ref().unwrap_or(&String::new()).len() > 64 {
556 return Err(WifiError::InvalidArguments);
557 }
558
559 if self.new_password.as_ref().unwrap_or(&String::new()).len() > 64 {
560 return Err(WifiError::InvalidArguments);
561 }
562
563 Ok(())
564 }
565}
566
567impl Debug for EapClientConfiguration {
568 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
569 f.debug_struct("EapClientConfiguration")
570 .field("ssid", &self.ssid)
571 .field("bssid", &self.bssid)
572 .field("auth_method", &self.auth_method)
573 .field("channel", &self.channel)
574 .field("identity", &self.identity)
575 .field("username", &self.username)
576 .field("password", &"**REDACTED**")
577 .field("new_password", &"**REDACTED**")
578 .field("eap_fast_config", &self.eap_fast_config)
579 .field("time_check", &self.time_check)
580 .field("pac_file set", &self.pac_file.is_some())
581 .field("ca_cert set", &self.ca_cert.is_some())
582 .field("certificate_and_key set", &"**REDACTED**")
583 .field("ttls_phase2_method", &self.ttls_phase2_method)
584 .finish()
585 }
586}
587
588#[cfg(feature = "defmt")]
589impl defmt::Format for EapClientConfiguration {
590 fn format(&self, fmt: defmt::Formatter<'_>) {
591 defmt::write!(
592 fmt,
593 "EapClientConfiguration {{\
594 ssid: {}, \
595 bssid: {:?}, \
596 auth_method: {:?}, \
597 channel: {:?}, \
598 identity: {:?}, \
599 username: {:?}, \
600 password: **REDACTED**, \
601 new_password: **REDACTED**, \
602 eap_fast_config: {:?}, \
603 time_check: {}, \
604 pac_file: {}, \
605 ca_cert: {}, \
606 certificate_and_key: **REDACTED**, \
607 ttls_phase2_method: {:?}, \
608 }}",
609 self.ssid.as_str(),
610 self.bssid,
611 self.auth_method,
612 self.channel,
613 &self.identity.as_ref().map_or("", |v| v.as_str()),
614 &self.username.as_ref().map_or("", |v| v.as_str()),
615 self.eap_fast_config,
616 self.time_check,
617 self.pac_file,
618 self.ca_cert,
619 self.ttls_phase2_method,
620 )
621 }
622}
623
624impl Default for EapClientConfiguration {
625 fn default() -> Self {
626 EapClientConfiguration {
627 ssid: String::new(),
628 bssid: None,
629 auth_method: AuthMethod::WPA2Enterprise,
630 identity: None,
631 username: None,
632 password: None,
633 channel: None,
634 eap_fast_config: None,
635 time_check: false,
636 new_password: None,
637 pac_file: None,
638 ca_cert: None,
639 certificate_and_key: None,
640 ttls_phase2_method: None,
641 }
642 }
643}
644
645#[derive(EnumSetType, Debug, PartialOrd)]
647#[cfg_attr(feature = "defmt", derive(defmt::Format))]
648#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
649pub enum Capability {
650 Client,
652
653 AccessPoint,
656
657 Mixed,
660}
661
662#[derive(Clone, Debug, PartialEq, Eq, Default)]
664#[cfg_attr(feature = "defmt", derive(defmt::Format))]
665#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
666#[allow(clippy::large_enum_variant)]
667pub enum Configuration {
668 #[default]
670 None,
671
672 Client(ClientConfiguration),
674
675 AccessPoint(AccessPointConfiguration),
677
678 Mixed(ClientConfiguration, AccessPointConfiguration),
680
681 #[cfg_attr(feature = "serde", serde(skip))]
683 EapClient(EapClientConfiguration),
684}
685
686impl Configuration {
687 fn validate(&self) -> Result<(), WifiError> {
688 match self {
689 Configuration::None => Ok(()),
690 Configuration::Client(client_configuration) => client_configuration.validate(),
691 Configuration::AccessPoint(access_point_configuration) => {
692 access_point_configuration.validate()
693 }
694 Configuration::Mixed(client_configuration, access_point_configuration) => {
695 client_configuration.validate()?;
696 access_point_configuration.validate()
697 }
698 Configuration::EapClient(eap_client_configuration) => {
699 eap_client_configuration.validate()
700 }
701 }
702 }
703
704 pub fn as_client_conf_ref(&self) -> Option<&ClientConfiguration> {
706 match self {
707 Self::Client(client_conf) | Self::Mixed(client_conf, _) => Some(client_conf),
708 _ => None,
709 }
710 }
711
712 pub fn as_ap_conf_ref(&self) -> Option<&AccessPointConfiguration> {
714 match self {
715 Self::AccessPoint(ap_conf) | Self::Mixed(_, ap_conf) => Some(ap_conf),
716 _ => None,
717 }
718 }
719
720 pub fn as_client_conf_mut(&mut self) -> &mut ClientConfiguration {
723 match self {
724 Self::Client(client_conf) => client_conf,
725 Self::Mixed(_, _) => {
726 let prev = mem::replace(self, Self::None);
727 match prev {
728 Self::Mixed(client_conf, _) => {
729 *self = Self::Client(client_conf);
730 self.as_client_conf_mut()
731 }
732 _ => unreachable!(),
733 }
734 }
735 _ => {
736 *self = Self::Client(Default::default());
737 self.as_client_conf_mut()
738 }
739 }
740 }
741
742 pub fn as_ap_conf_mut(&mut self) -> &mut AccessPointConfiguration {
745 match self {
746 Self::AccessPoint(ap_conf) => ap_conf,
747 Self::Mixed(_, _) => {
748 let prev = mem::replace(self, Self::None);
749 match prev {
750 Self::Mixed(_, ap_conf) => {
751 *self = Self::AccessPoint(ap_conf);
752 self.as_ap_conf_mut()
753 }
754 _ => unreachable!(),
755 }
756 }
757 _ => {
758 *self = Self::AccessPoint(Default::default());
759 self.as_ap_conf_mut()
760 }
761 }
762 }
763
764 pub fn as_mixed_conf_mut(
767 &mut self,
768 ) -> (&mut ClientConfiguration, &mut AccessPointConfiguration) {
769 match self {
770 Self::Mixed(client_conf, ap_conf) => (client_conf, ap_conf),
771 Self::AccessPoint(_) => {
772 let prev = mem::replace(self, Self::None);
773 match prev {
774 Self::AccessPoint(ap_conf) => {
775 *self = Self::Mixed(Default::default(), ap_conf);
776 self.as_mixed_conf_mut()
777 }
778 _ => unreachable!(),
779 }
780 }
781 Self::Client(_) => {
782 let prev = mem::replace(self, Self::None);
783 match prev {
784 Self::Client(client_conf) => {
785 *self = Self::Mixed(client_conf, Default::default());
786 self.as_mixed_conf_mut()
787 }
788 _ => unreachable!(),
789 }
790 }
791 _ => {
792 *self = Self::Mixed(Default::default(), Default::default());
793 self.as_mixed_conf_mut()
794 }
795 }
796 }
797}
798
799trait AuthMethodExt {
800 fn to_raw(&self) -> wifi_auth_mode_t;
801 fn from_raw(raw: wifi_auth_mode_t) -> Self;
802}
803
804impl AuthMethodExt for AuthMethod {
805 fn to_raw(&self) -> wifi_auth_mode_t {
806 match self {
807 AuthMethod::None => include::wifi_auth_mode_t_WIFI_AUTH_OPEN,
808 AuthMethod::WEP => include::wifi_auth_mode_t_WIFI_AUTH_WEP,
809 AuthMethod::WPA => include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
810 AuthMethod::WPA2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
811 AuthMethod::WPAWPA2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK,
812 AuthMethod::WPA2Enterprise => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE,
813 AuthMethod::WPA3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
814 AuthMethod::WPA2WPA3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK,
815 AuthMethod::WAPIPersonal => include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
816 }
817 }
818
819 fn from_raw(raw: wifi_auth_mode_t) -> Self {
820 match raw {
821 include::wifi_auth_mode_t_WIFI_AUTH_OPEN => AuthMethod::None,
822 include::wifi_auth_mode_t_WIFI_AUTH_WEP => AuthMethod::WEP,
823 include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => AuthMethod::WPA,
824 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => AuthMethod::WPA2Personal,
825 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => AuthMethod::WPAWPA2Personal,
826 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => AuthMethod::WPA2Enterprise,
827 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => AuthMethod::WPA3Personal,
828 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => AuthMethod::WPA2WPA3Personal,
829 include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => AuthMethod::WAPIPersonal,
830 _ => unreachable!(),
831 }
832 }
833}
834
835#[derive(Debug, Clone, Copy, PartialEq)]
837#[cfg_attr(feature = "defmt", derive(defmt::Format))]
838#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
839pub enum WifiMode {
840 Sta,
842 Ap,
844 ApSta,
846}
847
848impl WifiMode {
849 pub(crate) fn current() -> Result<Self, WifiError> {
850 let mut mode = wifi_mode_t_WIFI_MODE_NULL;
851 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?;
852
853 Self::try_from(mode)
854 }
855
856 pub fn is_sta(&self) -> bool {
858 match self {
859 Self::Sta | Self::ApSta => true,
860 Self::Ap => false,
861 }
862 }
863
864 pub fn is_ap(&self) -> bool {
866 match self {
867 Self::Sta => false,
868 Self::Ap | Self::ApSta => true,
869 }
870 }
871}
872
873impl TryFrom<&Configuration> for WifiMode {
874 type Error = WifiError;
875
876 fn try_from(config: &Configuration) -> Result<Self, Self::Error> {
878 let mode = match config {
879 Configuration::None => return Err(WifiError::UnknownWifiMode),
880 Configuration::AccessPoint(_) => Self::Ap,
881 Configuration::Client(_) => Self::Sta,
882 Configuration::Mixed(_, _) => Self::ApSta,
883 Configuration::EapClient(_) => Self::Sta,
884 };
885
886 Ok(mode)
887 }
888}
889
890impl TryFrom<wifi_mode_t> for WifiMode {
891 type Error = WifiError;
892
893 fn try_from(value: wifi_mode_t) -> Result<Self, Self::Error> {
895 #[allow(non_upper_case_globals)]
896 match value {
897 include::wifi_mode_t_WIFI_MODE_STA => Ok(Self::Sta),
898 include::wifi_mode_t_WIFI_MODE_AP => Ok(Self::Ap),
899 include::wifi_mode_t_WIFI_MODE_APSTA => Ok(Self::ApSta),
900 _ => Err(WifiError::UnknownWifiMode),
901 }
902 }
903}
904
905impl From<WifiMode> for wifi_mode_t {
906 fn from(val: WifiMode) -> Self {
907 #[allow(non_upper_case_globals)]
908 match val {
909 WifiMode::Sta => wifi_mode_t_WIFI_MODE_STA,
910 WifiMode::Ap => wifi_mode_t_WIFI_MODE_AP,
911 WifiMode::ApSta => wifi_mode_t_WIFI_MODE_APSTA,
912 }
913 }
914}
915
916#[cfg(feature = "csi")]
917pub(crate) trait CsiCallback: FnMut(crate::binary::include::wifi_csi_info_t) {}
918
919#[cfg(feature = "csi")]
920impl<T> CsiCallback for T where T: FnMut(crate::binary::include::wifi_csi_info_t) {}
921
922#[cfg(feature = "csi")]
923unsafe extern "C" fn csi_rx_cb<C: CsiCallback>(
924 ctx: *mut crate::wifi::c_types::c_void,
925 data: *mut crate::binary::include::wifi_csi_info_t,
926) {
927 unsafe {
928 let csi_callback = &mut *(ctx as *mut C);
929 csi_callback(*data);
930 }
931}
932
933#[derive(Clone, PartialEq, Eq)]
934#[cfg(all(not(esp32c6), feature = "csi"))]
937pub struct CsiConfig {
938 pub lltf_en: bool,
940 pub htltf_en: bool,
942 pub stbc_htltf2_en: bool,
945 pub ltf_merge_en: bool,
948 pub channel_filter_en: bool,
951 pub manu_scale: bool,
955 pub shift: u8,
958 pub dump_ack_en: bool,
960}
961
962#[derive(Clone, PartialEq, Eq)]
963#[cfg(all(esp32c6, feature = "csi"))]
964pub struct CsiConfig {
966 pub enable: u32,
968 pub acquire_csi_legacy: u32,
970 pub acquire_csi_ht20: u32,
972 pub acquire_csi_ht40: u32,
974 pub acquire_csi_su: u32,
976 pub acquire_csi_mu: u32,
978 pub acquire_csi_dcm: u32,
980 pub acquire_csi_beamformed: u32,
982 pub acquire_csi_he_stbc: u32,
986 pub val_scale_cfg: u32,
988 pub dump_ack_en: u32,
990 pub reserved: u32,
992}
993
994#[cfg(feature = "csi")]
995impl Default for CsiConfig {
996 #[cfg(not(esp32c6))]
997 fn default() -> Self {
998 Self {
999 lltf_en: true,
1000 htltf_en: true,
1001 stbc_htltf2_en: true,
1002 ltf_merge_en: true,
1003 channel_filter_en: true,
1004 manu_scale: false,
1005 shift: 0,
1006 dump_ack_en: false,
1007 }
1008 }
1009
1010 #[cfg(esp32c6)]
1011 fn default() -> Self {
1012 Self {
1014 enable: 1,
1015 acquire_csi_legacy: 1,
1016 acquire_csi_ht20: 1,
1017 acquire_csi_ht40: 1,
1018 acquire_csi_su: 1,
1019 acquire_csi_mu: 1,
1020 acquire_csi_dcm: 1,
1021 acquire_csi_beamformed: 1,
1022 acquire_csi_he_stbc: 2,
1023 val_scale_cfg: 2,
1024 dump_ack_en: 1,
1025 reserved: 19,
1026 }
1027 }
1028}
1029
1030#[cfg(feature = "csi")]
1031impl From<CsiConfig> for wifi_csi_config_t {
1032 fn from(config: CsiConfig) -> Self {
1033 #[cfg(not(esp32c6))]
1034 {
1035 wifi_csi_config_t {
1036 lltf_en: config.lltf_en,
1037 htltf_en: config.htltf_en,
1038 stbc_htltf2_en: config.stbc_htltf2_en,
1039 ltf_merge_en: config.ltf_merge_en,
1040 channel_filter_en: config.channel_filter_en,
1041 manu_scale: config.manu_scale,
1042 shift: config.shift,
1043 dump_ack_en: config.dump_ack_en,
1044 }
1045 }
1046 #[cfg(esp32c6)]
1047 {
1048 wifi_csi_acquire_config_t {
1049 _bitfield_align_1: [0; 0],
1050 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
1051 config.enable,
1052 config.acquire_csi_legacy,
1053 config.acquire_csi_ht20,
1054 config.acquire_csi_ht40,
1055 config.acquire_csi_su,
1056 config.acquire_csi_mu,
1057 config.acquire_csi_dcm,
1058 config.acquire_csi_beamformed,
1059 config.acquire_csi_he_stbc,
1060 config.val_scale_cfg,
1061 config.dump_ack_en,
1062 config.reserved,
1063 ),
1064 }
1065 }
1066 }
1067}
1068
1069#[cfg(feature = "csi")]
1070impl CsiConfig {
1071 pub(crate) fn apply_config(&self) -> Result<(), WifiError> {
1073 let conf: wifi_csi_config_t = self.clone().into();
1074
1075 unsafe {
1076 esp_wifi_result!(esp_wifi_set_csi_config(&conf))?;
1077 }
1078 Ok(())
1079 }
1080
1081 pub(crate) fn set_receive_cb<C: CsiCallback>(&mut self, cb: C) -> Result<(), WifiError> {
1084 let cb = alloc::boxed::Box::new(cb);
1085 let cb_ptr = alloc::boxed::Box::into_raw(cb) as *mut crate::wifi::c_types::c_void;
1086
1087 unsafe {
1088 esp_wifi_result!(esp_wifi_set_csi_rx_cb(Some(csi_rx_cb::<C>), cb_ptr))?;
1089 }
1090 Ok(())
1091 }
1092
1093 pub(crate) fn set_csi(&self, enable: bool) -> Result<(), WifiError> {
1095 unsafe {
1097 esp_wifi_result!(esp_wifi_set_csi(enable))?;
1098 }
1099 Ok(())
1100 }
1101}
1102
1103const RX_QUEUE_SIZE: usize = crate::CONFIG.rx_queue_size;
1104const TX_QUEUE_SIZE: usize = crate::CONFIG.tx_queue_size;
1105
1106pub(crate) static DATA_QUEUE_RX_AP: Locked<VecDeque<EspWifiPacketBuffer>> =
1107 Locked::new(VecDeque::new());
1108
1109pub(crate) static DATA_QUEUE_RX_STA: Locked<VecDeque<EspWifiPacketBuffer>> =
1110 Locked::new(VecDeque::new());
1111
1112#[derive(Debug, Clone, Copy)]
1114#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1115#[non_exhaustive]
1116pub enum WifiError {
1117 NotInitialized,
1120
1121 InternalError(InternalWifiError),
1123
1124 Disconnected,
1126
1127 UnknownWifiMode,
1129
1130 Unsupported,
1132
1133 InvalidArguments,
1135}
1136
1137#[repr(i32)]
1139#[derive(Debug, FromPrimitive, EnumSetType)]
1140#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1141pub enum WifiEvent {
1142 WifiReady = 0,
1144 ScanDone,
1146 StaStart,
1148 StaStop,
1150 StaConnected,
1152 StaDisconnected,
1154 StaAuthmodeChange,
1156
1157 StaWpsErSuccess,
1159 StaWpsErFailed,
1161 StaWpsErTimeout,
1163 StaWpsErPin,
1165 StaWpsErPbcOverlap,
1167
1168 ApStart,
1170 ApStop,
1172 ApStaconnected,
1174 ApStadisconnected,
1176 ApProbereqrecved,
1178
1179 FtmReport,
1181
1182 StaBssRssiLow,
1184 ActionTxStatus,
1186 RocDone,
1188
1189 StaBeaconTimeout,
1191
1192 ConnectionlessModuleWakeIntervalStart,
1194
1195 ApWpsRgSuccess,
1197 ApWpsRgFailed,
1199 ApWpsRgTimeout,
1201 ApWpsRgPin,
1203 ApWpsRgPbcOverlap,
1205
1206 ItwtSetup,
1208 ItwtTeardown,
1210 ItwtProbe,
1212 ItwtSuspend,
1214 TwtWakeup,
1216 BtwtSetup,
1218 BtwtTeardown,
1220
1221 NanStarted,
1223 NanStopped,
1225 NanSvcMatch,
1227 NanReplied,
1229 NanReceive,
1231 NdpIndication,
1233 NdpConfirm,
1235 NdpTerminated,
1237 HomeChannelChange,
1239
1240 StaNeighborRep,
1242}
1243
1244#[repr(i32)]
1246#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
1247#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1248#[allow(clippy::enum_variant_names)] pub enum InternalWifiError {
1250 EspErrNoMem = 0x101,
1252
1253 EspErrInvalidArg = 0x102,
1255
1256 EspErrWifiNotInit = 0x3001,
1258
1259 EspErrWifiNotStarted = 0x3002,
1261
1262 EspErrWifiNotStopped = 0x3003,
1264
1265 EspErrWifiIf = 0x3004,
1267
1268 EspErrWifiMode = 0x3005,
1270
1271 EspErrWifiState = 0x3006,
1273
1274 EspErrWifiConn = 0x3007,
1276
1277 EspErrWifiNvs = 0x3008,
1279
1280 EspErrWifiMac = 0x3009,
1282
1283 EspErrWifiSsid = 0x300A,
1285
1286 EspErrWifiPassword = 0x300B,
1288
1289 EspErrWifiTimeout = 0x300C,
1291
1292 EspErrWifiWakeFail = 0x300D,
1294
1295 EspErrWifiWouldBlock = 0x300E,
1297
1298 EspErrWifiNotConnect = 0x300F,
1300
1301 EspErrWifiPost = 0x3012,
1303
1304 EspErrWifiInitState = 0x3013,
1306
1307 EspErrWifiStopState = 0x3014,
1309
1310 EspErrWifiNotAssoc = 0x3015,
1312
1313 EspErrWifiTxDisallow = 0x3016,
1315}
1316
1317pub fn sta_mac(mac: &mut [u8; 6]) {
1319 unsafe {
1320 read_mac(mac as *mut u8, 0);
1321 }
1322}
1323
1324pub fn ap_mac(mac: &mut [u8; 6]) {
1326 unsafe {
1327 read_mac(mac as *mut u8, 1);
1328 }
1329}
1330
1331pub(crate) fn wifi_init() -> Result<(), WifiError> {
1332 unsafe {
1333 internal::G_CONFIG.wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs;
1334 internal::G_CONFIG.feature_caps = internal::g_wifi_feature_caps;
1335
1336 #[cfg(coex)]
1337 esp_wifi_result!(coex_init())?;
1338
1339 esp_wifi_result!(esp_wifi_init_internal(addr_of!(internal::G_CONFIG)))?;
1340 esp_wifi_result!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
1341
1342 esp_wifi_result!(esp_supplicant_init())?;
1343
1344 esp_wifi_result!(esp_wifi_set_tx_done_cb(Some(esp_wifi_tx_done_cb)))?;
1345
1346 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1347 esp_interface_t_ESP_IF_WIFI_STA,
1348 Some(recv_cb_sta)
1349 ))?;
1350
1351 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1353 esp_interface_t_ESP_IF_WIFI_AP,
1354 Some(recv_cb_ap)
1355 ))?;
1356
1357 #[cfg(any(esp32, esp32s3))]
1358 {
1359 static mut NVS_STRUCT: [u32; 12] = [0; 12];
1360 chip_specific::g_misc_nvs = addr_of!(NVS_STRUCT) as u32;
1361 }
1362
1363 crate::flags::WIFI.store(true, Ordering::SeqCst);
1364
1365 Ok(())
1366 }
1367}
1368
1369#[cfg(coex)]
1370pub(crate) fn coex_initialize() -> i32 {
1371 debug!("call coex-initialize");
1372 unsafe {
1373 let res = crate::binary::include::esp_coex_adapter_register(
1374 core::ptr::addr_of_mut!(internal::G_COEX_ADAPTER_FUNCS).cast(),
1375 );
1376 if res != 0 {
1377 error!("Error: esp_coex_adapter_register {}", res);
1378 return res;
1379 }
1380 let res = crate::binary::include::coex_pre_init();
1381 if res != 0 {
1382 error!("Error: coex_pre_init {}", res);
1383 return res;
1384 }
1385 0
1386 }
1387}
1388
1389pub(crate) unsafe extern "C" fn coex_init() -> i32 {
1390 #[cfg(coex)]
1391 {
1392 debug!("coex-init");
1393 #[allow(clippy::needless_return)]
1394 return unsafe { crate::binary::include::coex_init() };
1395 }
1396
1397 #[cfg(not(coex))]
1398 0
1399}
1400
1401pub(crate) fn wifi_deinit() -> Result<(), crate::InitializationError> {
1402 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
1403 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1404 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1405 crate::flags::WIFI.store(false, Ordering::Release);
1406 Ok(())
1407}
1408
1409unsafe extern "C" fn recv_cb_sta(
1410 buffer: *mut c_types::c_void,
1411 len: u16,
1412 eb: *mut c_types::c_void,
1413) -> esp_err_t {
1414 let packet = EspWifiPacketBuffer { buffer, len, eb };
1415 match DATA_QUEUE_RX_STA.with(|queue| {
1422 if queue.len() < RX_QUEUE_SIZE {
1423 queue.push_back(packet);
1424 Ok(())
1425 } else {
1426 Err(packet)
1427 }
1428 }) {
1429 Ok(()) => {
1430 embassy::STA_RECEIVE_WAKER.wake();
1431 include::ESP_OK as esp_err_t
1432 }
1433 _ => {
1434 debug!("RX QUEUE FULL");
1435 include::ESP_ERR_NO_MEM as esp_err_t
1436 }
1437 }
1438}
1439
1440unsafe extern "C" fn recv_cb_ap(
1441 buffer: *mut c_types::c_void,
1442 len: u16,
1443 eb: *mut c_types::c_void,
1444) -> esp_err_t {
1445 let packet = EspWifiPacketBuffer { buffer, len, eb };
1446 match DATA_QUEUE_RX_AP.with(|queue| {
1453 if queue.len() < RX_QUEUE_SIZE {
1454 queue.push_back(packet);
1455 Ok(())
1456 } else {
1457 Err(packet)
1458 }
1459 }) {
1460 Ok(()) => {
1461 embassy::AP_RECEIVE_WAKER.wake();
1462 include::ESP_OK as esp_err_t
1463 }
1464 _ => {
1465 debug!("RX QUEUE FULL");
1466 include::ESP_ERR_NO_MEM as esp_err_t
1467 }
1468 }
1469}
1470
1471pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1472
1473fn decrement_inflight_counter() {
1474 unwrap!(
1475 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1476 Some(x.saturating_sub(1))
1477 })
1478 );
1479}
1480
1481#[ram]
1482unsafe extern "C" fn esp_wifi_tx_done_cb(
1483 _ifidx: u8,
1484 _data: *mut u8,
1485 _data_len: *mut u16,
1486 _tx_status: bool,
1487) {
1488 trace!("esp_wifi_tx_done_cb");
1489
1490 decrement_inflight_counter();
1491
1492 embassy::TRANSMIT_WAKER.wake();
1493}
1494
1495pub(crate) fn wifi_start() -> Result<(), WifiError> {
1496 unsafe {
1497 esp_wifi_result!(esp_wifi_start())?;
1498
1499 let mode = WifiMode::current()?;
1500
1501 if mode.is_ap() {
1503 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1504 wifi_interface_t_WIFI_IF_AP,
1505 crate::CONFIG.ap_beacon_timeout
1506 ))?;
1507 }
1508 if mode.is_sta() {
1509 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1510 wifi_interface_t_WIFI_IF_STA,
1511 crate::CONFIG.beacon_timeout
1512 ))?;
1513 };
1514 }
1515
1516 Ok(())
1517}
1518
1519#[derive(Clone, Copy, PartialEq, Eq)]
1528pub enum ScanTypeConfig {
1529 Active {
1539 min: Duration,
1541 max: Duration,
1543 },
1544 Passive(Duration),
1555}
1556
1557impl Default for ScanTypeConfig {
1558 fn default() -> Self {
1559 Self::Active {
1560 min: Duration::from_millis(10),
1561 max: Duration::from_millis(20),
1562 }
1563 }
1564}
1565
1566impl ScanTypeConfig {
1567 fn validate(&self) {
1568 if matches!(self, Self::Passive(dur) if *dur > Duration::from_millis(1500)) {
1569 warn!(
1570 "Passive scan duration longer than 1500ms may cause a station to disconnect from the AP"
1571 );
1572 }
1573 }
1574}
1575
1576#[derive(Clone, Copy, Default, PartialEq, Eq)]
1578pub struct ScanConfig<'a> {
1579 pub ssid: Option<&'a str>,
1584 pub bssid: Option<[u8; 6]>,
1589 pub channel: Option<u8>,
1594 pub show_hidden: bool,
1596 pub scan_type: ScanTypeConfig,
1598}
1599
1600pub(crate) fn wifi_start_scan(
1601 block: bool,
1602 ScanConfig {
1603 ssid,
1604 mut bssid,
1605 channel,
1606 show_hidden,
1607 scan_type,
1608 }: ScanConfig<'_>,
1609) -> i32 {
1610 scan_type.validate();
1611 let (scan_time, scan_type) = match scan_type {
1612 ScanTypeConfig::Active { min, max } => (
1613 wifi_scan_time_t {
1614 active: wifi_active_scan_time_t {
1615 min: min.as_millis() as u32,
1616 max: max.as_millis() as u32,
1617 },
1618 passive: 0,
1619 },
1620 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1621 ),
1622 ScanTypeConfig::Passive(dur) => (
1623 wifi_scan_time_t {
1624 active: wifi_active_scan_time_t { min: 0, max: 0 },
1625 passive: dur.as_millis() as u32,
1626 },
1627 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1628 ),
1629 };
1630
1631 let mut ssid_buf = ssid.map(|m| {
1632 let mut buf = alloc::vec::Vec::from_iter(m.bytes());
1633 buf.push(b'\0');
1634 buf
1635 });
1636
1637 let ssid = ssid_buf
1638 .as_mut()
1639 .map(|e| e.as_mut_ptr())
1640 .unwrap_or_else(core::ptr::null_mut);
1641 let bssid = bssid
1642 .as_mut()
1643 .map(|e| e.as_mut_ptr())
1644 .unwrap_or_else(core::ptr::null_mut);
1645
1646 let scan_config = wifi_scan_config_t {
1647 ssid,
1648 bssid,
1649 channel: channel.unwrap_or(0),
1650 show_hidden,
1651 scan_type,
1652 scan_time,
1653 home_chan_dwell_time: 0,
1654 channel_bitmap: wifi_scan_channel_bitmap_t {
1655 ghz_2_channels: 0,
1656 ghz_5_channels: 0,
1657 },
1658 };
1659
1660 unsafe { esp_wifi_scan_start(&scan_config, block) }
1661}
1662
1663mod private {
1664 use super::*;
1665
1666 #[derive(Debug)]
1667 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1668 pub struct EspWifiPacketBuffer {
1676 pub(crate) buffer: *mut c_types::c_void,
1677 pub(crate) len: u16,
1678 pub(crate) eb: *mut c_types::c_void,
1679 }
1680
1681 unsafe impl Send for EspWifiPacketBuffer {}
1682
1683 impl Drop for EspWifiPacketBuffer {
1684 fn drop(&mut self) {
1685 trace!("Dropping EspWifiPacketBuffer, freeing memory");
1686 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1687 }
1688 }
1689
1690 impl EspWifiPacketBuffer {
1691 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1692 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1693 }
1694 }
1695}
1696
1697#[derive(Debug, Clone, Copy)]
1699pub enum WifiDeviceMode {
1700 Sta,
1701 Ap,
1702}
1703
1704impl WifiDeviceMode {
1705 fn mac_address(&self) -> [u8; 6] {
1706 match self {
1707 WifiDeviceMode::Sta => {
1708 let mut mac = [0; 6];
1709 sta_mac(&mut mac);
1710 mac
1711 }
1712 WifiDeviceMode::Ap => {
1713 let mut mac = [0; 6];
1714 ap_mac(&mut mac);
1715 mac
1716 }
1717 }
1718 }
1719
1720 fn data_queue_rx(&self) -> &'static Locked<VecDeque<EspWifiPacketBuffer>> {
1721 match self {
1722 WifiDeviceMode::Sta => &DATA_QUEUE_RX_STA,
1723 WifiDeviceMode::Ap => &DATA_QUEUE_RX_AP,
1724 }
1725 }
1726
1727 fn can_send(&self) -> bool {
1728 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE
1729 }
1730
1731 fn increase_in_flight_counter(&self) {
1732 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1733 }
1734
1735 fn tx_token(&self) -> Option<WifiTxToken> {
1736 if !self.can_send() {
1737 crate::preempt::yield_task();
1738 }
1739
1740 if self.can_send() {
1741 Some(WifiTxToken { mode: *self })
1742 } else {
1743 None
1744 }
1745 }
1746
1747 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1748 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1749 if is_empty || !self.can_send() {
1750 crate::preempt::yield_task();
1751 }
1752
1753 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1754
1755 if !is_empty {
1756 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1757 } else {
1758 None
1759 }
1760 }
1761
1762 fn interface(&self) -> wifi_interface_t {
1763 match self {
1764 WifiDeviceMode::Sta => wifi_interface_t_WIFI_IF_STA,
1765 WifiDeviceMode::Ap => wifi_interface_t_WIFI_IF_AP,
1766 }
1767 }
1768
1769 fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) {
1770 embassy::TRANSMIT_WAKER.register(cx.waker())
1771 }
1772
1773 fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) {
1774 match self {
1775 WifiDeviceMode::Sta => embassy::STA_RECEIVE_WAKER.register(cx.waker()),
1776 WifiDeviceMode::Ap => embassy::AP_RECEIVE_WAKER.register(cx.waker()),
1777 }
1778 }
1779
1780 fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) {
1781 match self {
1782 WifiDeviceMode::Sta => embassy::STA_LINK_STATE_WAKER.register(cx.waker()),
1783 WifiDeviceMode::Ap => embassy::AP_LINK_STATE_WAKER.register(cx.waker()),
1784 }
1785 }
1786
1787 fn link_state(&self) -> embassy_net_driver::LinkState {
1788 match self {
1789 WifiDeviceMode::Sta => {
1790 if matches!(sta_state(), WifiState::StaConnected) {
1791 embassy_net_driver::LinkState::Up
1792 } else {
1793 embassy_net_driver::LinkState::Down
1794 }
1795 }
1796 WifiDeviceMode::Ap => {
1797 if matches!(ap_state(), WifiState::ApStarted) {
1798 embassy_net_driver::LinkState::Up
1799 } else {
1800 embassy_net_driver::LinkState::Down
1801 }
1802 }
1803 }
1804 }
1805}
1806
1807pub struct WifiDevice<'d> {
1809 _phantom: PhantomData<&'d ()>,
1810 mode: WifiDeviceMode,
1811}
1812
1813impl WifiDevice<'_> {
1814 pub fn mac_address(&self) -> [u8; 6] {
1816 self.mode.mac_address()
1817 }
1818
1819 #[cfg(not(feature = "smoltcp"))]
1822 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1823 self.mode.rx_token()
1824 }
1825
1826 #[cfg(not(feature = "smoltcp"))]
1829 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1830 self.mode.tx_token()
1831 }
1832}
1833
1834fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
1835 let str_len = record
1836 .ssid
1837 .iter()
1838 .position(|&c| c == 0)
1839 .unwrap_or(record.ssid.len());
1840 let ssid_ref = unsafe { core::str::from_utf8_unchecked(&record.ssid[..str_len]) };
1841
1842 let mut ssid = String::new();
1843 ssid.push_str(ssid_ref);
1844
1845 AccessPointInfo {
1846 ssid,
1847 bssid: record.bssid,
1848 channel: record.primary,
1849 secondary_channel: match record.second {
1850 include::wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
1851 include::wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
1852 include::wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
1853 _ => panic!(),
1854 },
1855 signal_strength: record.rssi,
1856 auth_method: Some(AuthMethod::from_raw(record.authmode)),
1857 }
1858}
1859
1860#[cfg(not(any(esp32c6)))]
1863#[derive(Debug, Clone, Copy)]
1864#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1865pub struct RxControlInfo {
1866 pub rssi: i32,
1868 pub rate: u32,
1871 pub sig_mode: u32,
1874 pub mcs: u32,
1877 pub cwb: u32,
1879 pub smoothing: u32,
1882 pub not_sounding: u32,
1885 pub aggregation: u32,
1887 pub stbc: u32,
1890 pub fec_coding: u32,
1893 pub sgi: u32,
1896 pub ampdu_cnt: u32,
1898 pub channel: u32,
1900 pub secondary_channel: u32,
1903 pub timestamp: u32,
1906 pub noise_floor: i32,
1908 pub ant: u32,
1911 pub sig_len: u32,
1913 pub rx_state: u32,
1915}
1916
1917#[cfg(esp32c6)]
1920#[derive(Debug, Clone, Copy)]
1921#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1922pub struct RxControlInfo {
1923 pub rssi: i32,
1925 pub rate: u32,
1928 pub sig_len: u32,
1930 pub rx_state: u32,
1933 pub dump_len: u32,
1935 pub he_sigb_len: u32,
1937 pub cur_single_mpdu: u32,
1939 pub cur_bb_format: u32,
1941 pub rx_channel_estimate_info_vld: u32,
1943 pub rx_channel_estimate_len: u32,
1945 pub second: u32,
1947 pub channel: u32,
1949 pub noise_floor: i32,
1951 pub is_group: u32,
1953 pub rxend_state: u32,
1955 pub rxmatch3: u32,
1957 pub rxmatch2: u32,
1959 pub rxmatch1: u32,
1961 pub rxmatch0: u32,
1963}
1964impl RxControlInfo {
1965 pub unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
1971 #[cfg(not(esp32c6))]
1972 let rx_control_info = unsafe {
1973 RxControlInfo {
1974 rssi: (*rx_cntl).rssi(),
1975 rate: (*rx_cntl).rate(),
1976 sig_mode: (*rx_cntl).sig_mode(),
1977 mcs: (*rx_cntl).mcs(),
1978 cwb: (*rx_cntl).cwb(),
1979 smoothing: (*rx_cntl).smoothing(),
1980 not_sounding: (*rx_cntl).not_sounding(),
1981 aggregation: (*rx_cntl).aggregation(),
1982 stbc: (*rx_cntl).stbc(),
1983 fec_coding: (*rx_cntl).fec_coding(),
1984 sgi: (*rx_cntl).sgi(),
1985 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
1986 channel: (*rx_cntl).channel(),
1987 secondary_channel: (*rx_cntl).secondary_channel(),
1988 timestamp: (*rx_cntl).timestamp(),
1989 noise_floor: (*rx_cntl).noise_floor(),
1990 ant: (*rx_cntl).ant(),
1991 sig_len: (*rx_cntl).sig_len(),
1992 rx_state: (*rx_cntl).rx_state(),
1993 }
1994 };
1995 #[cfg(esp32c6)]
1996 let rx_control_info = unsafe {
1997 RxControlInfo {
1998 rssi: (*rx_cntl).rssi(),
1999 rate: (*rx_cntl).rate(),
2000 sig_len: (*rx_cntl).sig_len(),
2001 rx_state: (*rx_cntl).rx_state(),
2002 dump_len: (*rx_cntl).dump_len(),
2003 he_sigb_len: (*rx_cntl).he_sigb_len(),
2004 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
2005 cur_bb_format: (*rx_cntl).cur_bb_format(),
2006 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
2007 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
2008 second: (*rx_cntl).second(),
2009 channel: (*rx_cntl).channel(),
2010 noise_floor: (*rx_cntl).noise_floor(),
2011 is_group: (*rx_cntl).is_group(),
2012 rxend_state: (*rx_cntl).rxend_state(),
2013 rxmatch3: (*rx_cntl).rxmatch3(),
2014 rxmatch2: (*rx_cntl).rxmatch2(),
2015 rxmatch1: (*rx_cntl).rxmatch1(),
2016 rxmatch0: (*rx_cntl).rxmatch0(),
2017 }
2018 };
2019 rx_control_info
2020 }
2021}
2022#[cfg(feature = "sniffer")]
2024pub struct PromiscuousPkt<'a> {
2025 pub rx_cntl: RxControlInfo,
2027 pub frame_type: wifi_promiscuous_pkt_type_t,
2029 pub len: usize,
2031 pub data: &'a [u8],
2033}
2034#[cfg(feature = "sniffer")]
2035impl PromiscuousPkt<'_> {
2036 pub(crate) unsafe fn from_raw(
2040 buf: *const wifi_promiscuous_pkt_t,
2041 frame_type: wifi_promiscuous_pkt_type_t,
2042 ) -> Self {
2043 let rx_cntl = unsafe { RxControlInfo::from_raw(&(*buf).rx_ctrl) };
2044 let len = rx_cntl.sig_len as usize;
2045 PromiscuousPkt {
2046 rx_cntl,
2047 frame_type,
2048 len,
2049 data: unsafe {
2050 core::slice::from_raw_parts(
2051 (buf as *const u8).add(core::mem::size_of::<wifi_pkt_rx_ctrl_t>()),
2052 len,
2053 )
2054 },
2055 }
2056 }
2057}
2058
2059#[cfg(feature = "sniffer")]
2060static SNIFFER_CB: Locked<Option<fn(PromiscuousPkt<'_>)>> = Locked::new(None);
2061
2062#[cfg(feature = "sniffer")]
2063unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) {
2064 unsafe {
2065 if let Some(sniffer_callback) = SNIFFER_CB.with(|callback| *callback) {
2066 let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type);
2067 sniffer_callback(promiscuous_pkt);
2068 }
2069 }
2070}
2071
2072#[cfg(feature = "sniffer")]
2073#[non_exhaustive]
2075pub struct Sniffer {}
2076
2077#[cfg(feature = "sniffer")]
2078impl Sniffer {
2079 pub(crate) fn new() -> Self {
2080 unwrap!(esp_wifi_result!(unsafe {
2083 esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb))
2084 }));
2085 Self {}
2086 }
2087 pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
2089 esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
2090 Ok(())
2091 }
2092 pub fn send_raw_frame(
2094 &mut self,
2095 use_sta_interface: bool,
2096 buffer: &[u8],
2097 use_internal_seq_num: bool,
2098 ) -> Result<(), WifiError> {
2099 esp_wifi_result!(unsafe {
2100 esp_wifi_80211_tx(
2101 if use_sta_interface {
2102 wifi_interface_t_WIFI_IF_STA
2103 } else {
2104 wifi_interface_t_WIFI_IF_AP
2105 } as wifi_interface_t,
2106 buffer.as_ptr() as *const _,
2107 buffer.len() as i32,
2108 use_internal_seq_num,
2109 )
2110 })
2111 }
2112 pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt<'_>)) {
2114 SNIFFER_CB.with(|callback| *callback = Some(cb));
2115 }
2116}
2117
2118#[cfg(feature = "smoltcp")]
2120impl Device for WifiDevice<'_> {
2121 type RxToken<'a>
2122 = WifiRxToken
2123 where
2124 Self: 'a;
2125 type TxToken<'a>
2126 = WifiTxToken
2127 where
2128 Self: 'a;
2129
2130 fn receive(
2131 &mut self,
2132 _instant: smoltcp::time::Instant,
2133 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2134 self.mode.rx_token()
2135 }
2136
2137 fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
2138 self.mode.tx_token()
2139 }
2140
2141 fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
2142 let mut caps = DeviceCapabilities::default();
2143 caps.max_transmission_unit = MTU;
2144 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2145 None
2146 } else {
2147 Some(crate::CONFIG.max_burst_size)
2148 };
2149 caps
2150 }
2151}
2152
2153#[doc(hidden)]
2154#[derive(Debug)]
2155pub struct WifiRxToken {
2156 mode: WifiDeviceMode,
2157}
2158
2159impl WifiRxToken {
2160 pub fn consume_token<R, F>(self, f: F) -> R
2163 where
2164 F: FnOnce(&mut [u8]) -> R,
2165 {
2166 let mut data = self.mode.data_queue_rx().with(|queue| {
2167 unwrap!(
2168 queue.pop_front(),
2169 "unreachable: transmit()/receive() ensures there is a packet to process"
2170 )
2171 });
2172
2173 let buffer = data.as_slice_mut();
2180 dump_packet_info(buffer);
2181
2182 f(buffer)
2183 }
2184}
2185
2186#[cfg(feature = "smoltcp")]
2187impl RxToken for WifiRxToken {
2188 fn consume<R, F>(self, f: F) -> R
2189 where
2190 F: FnOnce(&[u8]) -> R,
2191 {
2192 self.consume_token(|t| f(t))
2193 }
2194}
2195
2196#[doc(hidden)]
2197#[derive(Debug)]
2198pub struct WifiTxToken {
2199 mode: WifiDeviceMode,
2200}
2201
2202impl WifiTxToken {
2203 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
2206 where
2207 F: FnOnce(&mut [u8]) -> R,
2208 {
2209 self.mode.increase_in_flight_counter();
2210
2211 static mut BUFFER: [u8; MTU] = [0u8; MTU];
2215
2216 let buffer = unsafe { &mut BUFFER[..len] };
2217
2218 let res = f(buffer);
2219
2220 esp_wifi_send_data(self.mode.interface(), buffer);
2221
2222 res
2223 }
2224}
2225
2226#[cfg(feature = "smoltcp")]
2227impl TxToken for WifiTxToken {
2228 fn consume<R, F>(self, len: usize, f: F) -> R
2229 where
2230 F: FnOnce(&mut [u8]) -> R,
2231 {
2232 self.consume_token(len, f)
2233 }
2234}
2235
2236pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
2241 trace!("sending... {} bytes", data.len());
2242 dump_packet_info(data);
2243
2244 let len = data.len() as u16;
2245 let ptr = data.as_mut_ptr().cast();
2246
2247 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
2248
2249 if res != 0 {
2250 warn!("esp_wifi_internal_tx {}", res);
2251 decrement_inflight_counter();
2252 } else {
2253 trace!("esp_wifi_internal_tx ok");
2254 }
2255}
2256
2257fn apply_ap_config(config: &AccessPointConfiguration) -> Result<(), WifiError> {
2258 let mut cfg = wifi_config_t {
2259 ap: wifi_ap_config_t {
2260 ssid: [0; 32],
2261 password: [0; 64],
2262 ssid_len: 0,
2263 channel: config.channel,
2264 authmode: config.auth_method.to_raw(),
2265 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
2266 max_connection: config.max_connections as u8,
2267 beacon_interval: 100,
2268 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
2269 ftm_responder: false,
2270 pmf_cfg: wifi_pmf_config_t {
2271 capable: true,
2272 required: false,
2273 },
2274 sae_pwe_h2e: 0,
2275 csa_count: 3,
2276 dtim_period: 2,
2277 },
2278 };
2279
2280 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2281 return Err(WifiError::InternalError(
2282 InternalWifiError::EspErrInvalidArg,
2283 ));
2284 }
2285
2286 unsafe {
2287 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2288 cfg.ap.ssid_len = config.ssid.len() as u8;
2289 cfg.ap.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2290
2291 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
2292 }
2293}
2294
2295fn apply_sta_config(config: &ClientConfiguration) -> Result<(), WifiError> {
2296 let mut cfg = wifi_config_t {
2297 sta: wifi_sta_config_t {
2298 ssid: [0; 32],
2299 password: [0; 64],
2300 scan_method: crate::CONFIG.scan_method,
2301 bssid_set: config.bssid.is_some(),
2302 bssid: config.bssid.unwrap_or_default(),
2303 channel: config.channel.unwrap_or(0),
2304 listen_interval: crate::CONFIG.listen_interval,
2305 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2306 threshold: wifi_scan_threshold_t {
2307 rssi: -99,
2308 authmode: config.auth_method.to_raw(),
2309 },
2310 pmf_cfg: wifi_pmf_config_t {
2311 capable: true,
2312 required: false,
2313 },
2314 sae_pwe_h2e: 3,
2315 _bitfield_align_1: [0; 0],
2316 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2317 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2318 _bitfield_align_2: [0; 0],
2319 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2320 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2322 },
2323 };
2324
2325 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2326 return Err(WifiError::InternalError(
2327 InternalWifiError::EspErrInvalidArg,
2328 ));
2329 }
2330
2331 unsafe {
2332 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2333 cfg.sta.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2334
2335 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
2336 }
2337}
2338
2339fn apply_sta_eap_config(config: &EapClientConfiguration) -> Result<(), WifiError> {
2340 let mut cfg = wifi_config_t {
2341 sta: wifi_sta_config_t {
2342 ssid: [0; 32],
2343 password: [0; 64],
2344 scan_method: crate::CONFIG.scan_method,
2345 bssid_set: config.bssid.is_some(),
2346 bssid: config.bssid.unwrap_or_default(),
2347 channel: config.channel.unwrap_or(0),
2348 listen_interval: crate::CONFIG.listen_interval,
2349 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2350 threshold: wifi_scan_threshold_t {
2351 rssi: -99,
2352 authmode: config.auth_method.to_raw(),
2353 },
2354 pmf_cfg: wifi_pmf_config_t {
2355 capable: true,
2356 required: false,
2357 },
2358 sae_pwe_h2e: 3,
2359 _bitfield_align_1: [0; 0],
2360 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2361 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2362 _bitfield_align_2: [0; 0],
2363 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2364 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2366 },
2367 };
2368
2369 unsafe {
2370 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2371 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
2372
2373 if let Some(identity) = &config.identity {
2374 esp_wifi_result!(esp_eap_client_set_identity(
2375 identity.as_str().as_ptr(),
2376 identity.len() as i32
2377 ))?;
2378 } else {
2379 esp_eap_client_clear_identity();
2380 }
2381
2382 if let Some(username) = &config.username {
2383 esp_wifi_result!(esp_eap_client_set_username(
2384 username.as_str().as_ptr(),
2385 username.len() as i32
2386 ))?;
2387 } else {
2388 esp_eap_client_clear_username();
2389 }
2390
2391 if let Some(password) = &config.password {
2392 esp_wifi_result!(esp_eap_client_set_password(
2393 password.as_str().as_ptr(),
2394 password.len() as i32
2395 ))?;
2396 } else {
2397 esp_eap_client_clear_password();
2398 }
2399
2400 if let Some(new_password) = &config.new_password {
2401 esp_wifi_result!(esp_eap_client_set_new_password(
2402 new_password.as_str().as_ptr(),
2403 new_password.len() as i32
2404 ))?;
2405 } else {
2406 esp_eap_client_clear_new_password();
2407 }
2408
2409 if let Some(pac_file) = &config.pac_file {
2410 esp_wifi_result!(esp_eap_client_set_pac_file(
2411 pac_file.as_ptr(),
2412 pac_file.len() as i32
2413 ))?;
2414 }
2415
2416 if let Some(phase2_method) = &config.ttls_phase2_method {
2417 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
2418 phase2_method.to_raw()
2419 ))?;
2420 }
2421
2422 if let Some(ca_cert) = config.ca_cert {
2423 esp_wifi_result!(esp_eap_client_set_ca_cert(
2424 ca_cert.as_ptr(),
2425 ca_cert.len() as i32
2426 ))?;
2427 } else {
2428 esp_eap_client_clear_ca_cert();
2429 }
2430
2431 if let Some((cert, key, password)) = config.certificate_and_key {
2432 let (pwd, pwd_len) = if let Some(pwd) = password {
2433 (pwd.as_ptr(), pwd.len() as i32)
2434 } else {
2435 (core::ptr::null(), 0)
2436 };
2437
2438 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
2439 cert.as_ptr(),
2440 cert.len() as i32,
2441 key.as_ptr(),
2442 key.len() as i32,
2443 pwd,
2444 pwd_len,
2445 ))?;
2446 } else {
2447 esp_eap_client_clear_certificate_and_key();
2448 }
2449
2450 if let Some(cfg) = &config.eap_fast_config {
2451 let params = esp_eap_fast_config {
2452 fast_provisioning: cfg.fast_provisioning as i32,
2453 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
2454 fast_pac_format_binary: cfg.fast_pac_format_binary,
2455 };
2456 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
2457 }
2458
2459 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
2460
2461 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
2468
2469 Ok(())
2470 }
2471}
2472
2473fn dump_packet_info(_buffer: &mut [u8]) {
2474 #[cfg(dump_packets)]
2475 {
2476 info!("@WIFIFRAME {:?}", _buffer);
2477 }
2478}
2479
2480#[doc(hidden)]
2481#[macro_export]
2482macro_rules! esp_wifi_result {
2483 ($value:expr) => {{
2484 use num_traits::FromPrimitive;
2485 let result = $value;
2486 if result != esp_wifi_sys::include::ESP_OK as i32 {
2487 warn!("{} returned an error: {}", stringify!($value), result);
2488 Err(WifiError::InternalError(unwrap!(FromPrimitive::from_i32(
2489 result
2490 ))))
2491 } else {
2492 Ok::<(), WifiError>(())
2493 }
2494 }};
2495}
2496
2497pub(crate) mod embassy {
2498 use embassy_net_driver::{Capabilities, Driver, HardwareAddress, RxToken, TxToken};
2499 use esp_hal::asynch::AtomicWaker;
2500
2501 use super::*;
2502
2503 pub(crate) static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
2506
2507 pub(crate) static AP_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2508 pub(crate) static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2509
2510 pub(crate) static STA_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2511 pub(crate) static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2512
2513 impl RxToken for WifiRxToken {
2514 fn consume<R, F>(self, f: F) -> R
2515 where
2516 F: FnOnce(&mut [u8]) -> R,
2517 {
2518 self.consume_token(f)
2519 }
2520 }
2521
2522 impl TxToken for WifiTxToken {
2523 fn consume<R, F>(self, len: usize, f: F) -> R
2524 where
2525 F: FnOnce(&mut [u8]) -> R,
2526 {
2527 self.consume_token(len, f)
2528 }
2529 }
2530
2531 impl Driver for WifiDevice<'_> {
2532 type RxToken<'a>
2533 = WifiRxToken
2534 where
2535 Self: 'a;
2536 type TxToken<'a>
2537 = WifiTxToken
2538 where
2539 Self: 'a;
2540
2541 fn receive(
2542 &mut self,
2543 cx: &mut core::task::Context<'_>,
2544 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2545 self.mode.register_receive_waker(cx);
2546 self.mode.register_transmit_waker(cx);
2547 self.mode.rx_token()
2548 }
2549
2550 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
2551 self.mode.register_transmit_waker(cx);
2552 self.mode.tx_token()
2553 }
2554
2555 fn link_state(
2556 &mut self,
2557 cx: &mut core::task::Context<'_>,
2558 ) -> embassy_net_driver::LinkState {
2559 self.mode.register_link_state_waker(cx);
2560 self.mode.link_state()
2561 }
2562
2563 fn capabilities(&self) -> Capabilities {
2564 let mut caps = Capabilities::default();
2565 caps.max_transmission_unit = MTU;
2566 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2567 None
2568 } else {
2569 Some(crate::CONFIG.max_burst_size)
2570 };
2571 caps
2572 }
2573
2574 fn hardware_address(&self) -> HardwareAddress {
2575 HardwareAddress::Ethernet(self.mac_address())
2576 }
2577 }
2578}
2579
2580pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
2581 esp_wifi_result!(unsafe { esp_wifi_sys::include::esp_wifi_set_ps(ps.into()) })?;
2582 Ok(())
2583}
2584
2585struct FreeApListOnDrop;
2586impl FreeApListOnDrop {
2587 pub fn defuse(self) {
2588 core::mem::forget(self);
2589 }
2590}
2591
2592impl Drop for FreeApListOnDrop {
2593 fn drop(&mut self) {
2594 unsafe {
2595 include::esp_wifi_clear_ap_list();
2596 }
2597 }
2598}
2599
2600#[non_exhaustive]
2601pub struct Interfaces<'d> {
2602 pub sta: WifiDevice<'d>,
2603 pub ap: WifiDevice<'d>,
2604 #[cfg(feature = "esp-now")]
2605 pub esp_now: crate::esp_now::EspNow<'d>,
2606 #[cfg(feature = "sniffer")]
2607 pub sniffer: Sniffer,
2608}
2609
2610pub fn new<'d>(
2616 inited: &'d EspWifiController<'d>,
2617 _device: crate::hal::peripherals::WIFI<'d>,
2618) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> {
2619 if crate::is_interrupts_disabled() {
2620 return Err(WifiError::Unsupported);
2621 }
2622
2623 let mut controller = WifiController {
2624 _phantom: Default::default(),
2625 };
2626
2627 if !inited.wifi() {
2628 crate::wifi::wifi_init()?;
2629
2630 let mut cntry_code = [0u8; 3];
2631 cntry_code[..crate::CONFIG.country_code.len()]
2632 .copy_from_slice(crate::CONFIG.country_code.as_bytes());
2633 cntry_code[2] = crate::CONFIG.country_code_operating_class;
2634
2635 #[allow(clippy::useless_transmute)]
2636 unsafe {
2637 let country = wifi_country_t {
2638 #[allow(clippy::useless_transmute)]
2640 cc: core::mem::transmute::<[u8; 3], [core::ffi::c_char; 3]>(cntry_code),
2641 schan: 1,
2642 nchan: 13,
2643 max_tx_power: 20,
2644 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
2645 };
2646 esp_wifi_result!(esp_wifi_set_country(&country))?;
2647 }
2648
2649 controller.set_power_saving(PowerSaveMode::default())?;
2650 }
2651
2652 Ok((
2653 controller,
2654 Interfaces {
2655 sta: WifiDevice {
2656 _phantom: Default::default(),
2657 mode: WifiDeviceMode::Sta,
2658 },
2659 ap: WifiDevice {
2660 _phantom: Default::default(),
2661 mode: WifiDeviceMode::Ap,
2662 },
2663 #[cfg(feature = "esp-now")]
2664 esp_now: crate::esp_now::EspNow::new_internal(),
2665 #[cfg(feature = "sniffer")]
2666 sniffer: Sniffer::new(),
2667 },
2668 ))
2669}
2670
2671#[non_exhaustive]
2672pub struct WifiController<'d> {
2673 _phantom: PhantomData<&'d ()>,
2674}
2675
2676impl Drop for WifiController<'_> {
2677 fn drop(&mut self) {
2678 if let Err(e) = crate::wifi::wifi_deinit() {
2679 warn!("Failed to cleanly deinit wifi: {:?}", e);
2680 }
2681 }
2682}
2683
2684impl WifiController<'_> {
2685 #[cfg(feature = "csi")]
2687 pub fn set_csi(
2688 &mut self,
2689 mut csi: CsiConfig,
2690 cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send,
2691 ) -> Result<(), WifiError> {
2692 csi.apply_config()?;
2693 csi.set_receive_cb(cb)?;
2694 csi.set_csi(true)?;
2695
2696 Ok(())
2697 }
2698
2699 pub fn set_protocol(&mut self, protocols: EnumSet<Protocol>) -> Result<(), WifiError> {
2714 let protocol = protocols
2715 .into_iter()
2716 .map(|v| match v {
2717 Protocol::P802D11B => WIFI_PROTOCOL_11B,
2718 Protocol::P802D11BG => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G,
2719 Protocol::P802D11BGN => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
2720 Protocol::P802D11BGNLR => {
2721 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR
2722 }
2723 Protocol::P802D11LR => WIFI_PROTOCOL_LR,
2724 Protocol::P802D11BGNAX => {
2725 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX
2726 }
2727 })
2728 .fold(0, |combined, protocol| combined | protocol) as u8;
2729
2730 let mode = self.mode()?;
2731 if mode.is_sta() {
2732 esp_wifi_result!(unsafe {
2733 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_STA, protocol)
2734 })?;
2735 }
2736 if mode.is_ap() {
2737 esp_wifi_result!(unsafe {
2738 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_AP, protocol)
2739 })?;
2740 }
2741
2742 Ok(())
2743 }
2744
2745 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
2747 apply_power_saving(ps)
2748 }
2749
2750 pub fn scan_with_config_sync(
2752 &mut self,
2753 config: ScanConfig<'_>,
2754 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2755 self.scan_with_config_sync_max(config, usize::MAX)
2756 }
2757
2758 pub fn scan_with_config_sync_max(
2759 &mut self,
2760 config: ScanConfig<'_>,
2761 max: usize,
2762 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2763 esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?;
2764 let result = self.scan_results(max)?;
2765 Ok(result)
2766 }
2767
2768 fn scan_results(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2769 let mut scanned = alloc::vec::Vec::<AccessPointInfo>::new();
2770 let mut bss_total: u16 = max as u16;
2771
2772 let guard = FreeApListOnDrop;
2774
2775 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
2776
2777 guard.defuse();
2778
2779 let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
2780 for _ in 0..usize::min(bss_total as usize, max) {
2781 let record = unsafe { MaybeUninit::assume_init_mut(&mut record) };
2782 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_record(record))? };
2783 let ap_info = convert_ap_info(record);
2784 scanned.push(ap_info);
2785 }
2786
2787 unsafe { esp_wifi_result!(include::esp_wifi_clear_ap_list())? };
2788
2789 Ok(scanned)
2790 }
2791
2792 pub fn scan_n(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2794 self.scan_with_config_sync_max(Default::default(), max)
2795 }
2796
2797 pub fn start(&mut self) -> Result<(), WifiError> {
2799 crate::wifi::wifi_start()
2800 }
2801
2802 pub fn stop(&mut self) -> Result<(), WifiError> {
2804 self.stop_impl()
2805 }
2806
2807 pub fn connect(&mut self) -> Result<(), WifiError> {
2815 self.connect_impl()
2816 }
2817
2818 pub fn disconnect(&mut self) -> Result<(), WifiError> {
2820 self.disconnect_impl()
2821 }
2822
2823 pub fn rssi(&self) -> Result<i32, WifiError> {
2836 if self.mode()?.is_sta() {
2837 let mut rssi: i32 = 0;
2838 esp_wifi_result!(unsafe { esp_wifi_sta_get_rssi(&mut rssi) })?;
2840 Ok(rssi)
2841 } else {
2842 Err(WifiError::Unsupported)
2843 }
2844 }
2845
2846 pub fn capabilities(&self) -> Result<EnumSet<crate::wifi::Capability>, WifiError> {
2848 let caps =
2849 enumset::enum_set! { Capability::Client | Capability::AccessPoint | Capability::Mixed };
2850 Ok(caps)
2851 }
2852
2853 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), WifiError> {
2863 conf.validate()?;
2864
2865 let mode = match conf {
2866 Configuration::None => wifi_mode_t_WIFI_MODE_NULL,
2867 Configuration::Client(_) => wifi_mode_t_WIFI_MODE_STA,
2868 Configuration::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
2869 Configuration::Mixed(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
2870 Configuration::EapClient(_) => wifi_mode_t_WIFI_MODE_STA,
2871 };
2872
2873 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
2874
2875 match conf {
2876 Configuration::None => Ok::<(), WifiError>(()),
2877 Configuration::Client(config) => apply_sta_config(config),
2878 Configuration::AccessPoint(config) => apply_ap_config(config),
2879 Configuration::Mixed(sta_config, ap_config) => {
2880 apply_ap_config(ap_config).and_then(|()| apply_sta_config(sta_config))
2881 }
2882 Configuration::EapClient(config) => apply_sta_eap_config(config),
2883 }
2884 .inspect_err(|_| {
2885 unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL) };
2889 })?;
2890
2891 Ok(())
2892 }
2893
2894 pub fn set_mode(&mut self, mode: WifiMode) -> Result<(), WifiError> {
2898 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?;
2899 Ok(())
2900 }
2901
2902 fn stop_impl(&mut self) -> Result<(), WifiError> {
2903 esp_wifi_result!(unsafe { esp_wifi_stop() })
2904 }
2905
2906 fn connect_impl(&mut self) -> Result<(), WifiError> {
2907 esp_wifi_result!(unsafe { esp_wifi_connect() })
2908 }
2909
2910 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
2911 esp_wifi_result!(unsafe { esp_wifi_disconnect() })
2912 }
2913
2914 pub fn is_started(&self) -> Result<bool, WifiError> {
2919 if matches!(
2920 crate::wifi::sta_state(),
2921 WifiState::StaStarted | WifiState::StaConnected | WifiState::StaDisconnected
2922 ) {
2923 return Ok(true);
2924 }
2925 if matches!(crate::wifi::ap_state(), WifiState::ApStarted) {
2926 return Ok(true);
2927 }
2928 Ok(false)
2929 }
2930
2931 pub fn is_connected(&self) -> Result<bool, WifiError> {
2936 match crate::wifi::sta_state() {
2937 crate::wifi::WifiState::StaConnected => Ok(true),
2938 crate::wifi::WifiState::StaDisconnected => Err(WifiError::Disconnected),
2939 _ => Ok(false),
2941 }
2942 }
2943
2944 fn mode(&self) -> Result<WifiMode, WifiError> {
2945 WifiMode::current()
2946 }
2947
2948 pub async fn scan_n_async(
2950 &mut self,
2951 max: usize,
2952 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2953 self.scan_with_config_async_max(Default::default(), max)
2954 .await
2955 }
2956
2957 pub async fn scan_with_config_async(
2959 &mut self,
2960 config: ScanConfig<'_>,
2961 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2962 self.scan_with_config_async_max(config, usize::MAX).await
2963 }
2964
2965 async fn scan_with_config_async_max(
2966 &mut self,
2967 config: ScanConfig<'_>,
2968 max: usize,
2969 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2970 Self::clear_events(WifiEvent::ScanDone);
2971 esp_wifi_result!(wifi_start_scan(false, config))?;
2972
2973 let guard = FreeApListOnDrop;
2975 WifiEventFuture::new(WifiEvent::ScanDone).await;
2976
2977 guard.defuse();
2978
2979 let result = self.scan_results(max)?;
2980
2981 Ok(result)
2982 }
2983
2984 pub async fn start_async(&mut self) -> Result<(), WifiError> {
2986 let mut events = enumset::enum_set! {};
2987
2988 let mode = self.mode()?;
2989 if mode.is_ap() {
2990 events |= WifiEvent::ApStart;
2991 }
2992 if mode.is_sta() {
2993 events |= WifiEvent::StaStart;
2994 }
2995
2996 Self::clear_events(events);
2997
2998 wifi_start()?;
2999
3000 self.wait_for_all_events(events, false).await;
3001
3002 Ok(())
3003 }
3004
3005 pub async fn stop_async(&mut self) -> Result<(), WifiError> {
3007 let mut events = enumset::enum_set! {};
3008
3009 let mode = self.mode()?;
3010 if mode.is_ap() {
3011 events |= WifiEvent::ApStop;
3012 }
3013 if mode.is_sta() {
3014 events |= WifiEvent::StaStop;
3015 }
3016
3017 Self::clear_events(events);
3018
3019 crate::wifi::WifiController::stop_impl(self)?;
3020
3021 self.wait_for_all_events(events, false).await;
3022
3023 reset_ap_state();
3024 reset_sta_state();
3025
3026 Ok(())
3027 }
3028
3029 pub async fn connect_async(&mut self) -> Result<(), WifiError> {
3031 Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected);
3032
3033 let err = crate::wifi::WifiController::connect_impl(self).err();
3034
3035 if MultiWifiEventFuture::new(WifiEvent::StaConnected | WifiEvent::StaDisconnected)
3036 .await
3037 .contains(WifiEvent::StaDisconnected)
3038 {
3039 Err(err.unwrap_or(WifiError::Disconnected))
3040 } else {
3041 Ok(())
3042 }
3043 }
3044
3045 pub async fn disconnect_async(&mut self) -> Result<(), WifiError> {
3048 if !matches!(self.is_connected(), Ok(true)) {
3052 return Ok(());
3053 }
3054
3055 Self::clear_events(WifiEvent::StaDisconnected);
3056 crate::wifi::WifiController::disconnect_impl(self)?;
3057 WifiEventFuture::new(WifiEvent::StaDisconnected).await;
3058
3059 Ok(())
3060 }
3061
3062 fn clear_events(events: impl Into<EnumSet<WifiEvent>>) {
3063 WIFI_EVENTS.with(|evts| evts.get_mut().remove_all(events.into()));
3064 }
3065
3066 pub async fn wait_for_event(&mut self, event: WifiEvent) {
3068 Self::clear_events(event);
3069 WifiEventFuture::new(event).await
3070 }
3071
3072 pub async fn wait_for_events(
3075 &mut self,
3076 events: EnumSet<WifiEvent>,
3077 clear_pending: bool,
3078 ) -> EnumSet<WifiEvent> {
3079 if clear_pending {
3080 Self::clear_events(events);
3081 }
3082 MultiWifiEventFuture::new(events).await
3083 }
3084
3085 pub async fn wait_for_all_events(
3087 &mut self,
3088 mut events: EnumSet<WifiEvent>,
3089 clear_pending: bool,
3090 ) {
3091 if clear_pending {
3092 Self::clear_events(events);
3093 }
3094
3095 while !events.is_empty() {
3096 let fired = MultiWifiEventFuture::new(events).await;
3097 events -= fired;
3098 }
3099 }
3100}
3101
3102impl WifiEvent {
3103 pub(crate) fn waker(&self) -> &'static AtomicWaker {
3104 static WAKER: AtomicWaker = AtomicWaker::new();
3108 &WAKER
3109 }
3110}
3111
3112#[must_use = "futures do nothing unless you `.await` or poll them"]
3113pub(crate) struct WifiEventFuture {
3114 event: WifiEvent,
3115}
3116
3117impl WifiEventFuture {
3118 pub fn new(event: WifiEvent) -> Self {
3120 Self { event }
3121 }
3122}
3123
3124impl core::future::Future for WifiEventFuture {
3125 type Output = ();
3126
3127 fn poll(
3128 self: core::pin::Pin<&mut Self>,
3129 cx: &mut core::task::Context<'_>,
3130 ) -> Poll<Self::Output> {
3131 self.event.waker().register(cx.waker());
3132 if WIFI_EVENTS.with(|events| events.get_mut().remove(self.event)) {
3133 Poll::Ready(())
3134 } else {
3135 Poll::Pending
3136 }
3137 }
3138}
3139
3140#[must_use = "futures do nothing unless you `.await` or poll them"]
3141pub(crate) struct MultiWifiEventFuture {
3142 event: EnumSet<WifiEvent>,
3143}
3144
3145impl MultiWifiEventFuture {
3146 pub fn new(event: EnumSet<WifiEvent>) -> Self {
3148 Self { event }
3149 }
3150}
3151
3152impl core::future::Future for MultiWifiEventFuture {
3153 type Output = EnumSet<WifiEvent>;
3154
3155 fn poll(
3156 self: core::pin::Pin<&mut Self>,
3157 cx: &mut core::task::Context<'_>,
3158 ) -> Poll<Self::Output> {
3159 let output = WIFI_EVENTS.with(|events| {
3160 let events = events.get_mut();
3161 let active = events.intersection(self.event);
3162 events.remove_all(active);
3163 active
3164 });
3165 if output.is_empty() {
3166 for event in self.event.iter() {
3167 event.waker().register(cx.waker());
3168 }
3169
3170 Poll::Pending
3171 } else {
3172 Poll::Ready(output)
3173 }
3174 }
3175}