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 Ok(())
1364 }
1365}
1366
1367#[cfg(coex)]
1368pub(crate) fn coex_initialize() -> i32 {
1369 debug!("call coex-initialize");
1370 unsafe {
1371 let res = crate::binary::include::esp_coex_adapter_register(
1372 core::ptr::addr_of_mut!(internal::G_COEX_ADAPTER_FUNCS).cast(),
1373 );
1374 if res != 0 {
1375 error!("Error: esp_coex_adapter_register {}", res);
1376 return res;
1377 }
1378 let res = crate::binary::include::coex_pre_init();
1379 if res != 0 {
1380 error!("Error: coex_pre_init {}", res);
1381 return res;
1382 }
1383 0
1384 }
1385}
1386
1387pub(crate) unsafe extern "C" fn coex_init() -> i32 {
1388 #[cfg(coex)]
1389 {
1390 debug!("coex-init");
1391 #[allow(clippy::needless_return)]
1392 return unsafe { crate::binary::include::coex_init() };
1393 }
1394
1395 #[cfg(not(coex))]
1396 0
1397}
1398
1399fn wifi_deinit() -> Result<(), crate::InitializationError> {
1400 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
1401 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1402 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1403 Ok(())
1404}
1405
1406unsafe extern "C" fn recv_cb_sta(
1407 buffer: *mut c_types::c_void,
1408 len: u16,
1409 eb: *mut c_types::c_void,
1410) -> esp_err_t {
1411 let packet = EspWifiPacketBuffer { buffer, len, eb };
1412 match DATA_QUEUE_RX_STA.with(|queue| {
1419 if queue.len() < RX_QUEUE_SIZE {
1420 queue.push_back(packet);
1421 Ok(())
1422 } else {
1423 Err(packet)
1424 }
1425 }) {
1426 Ok(()) => {
1427 embassy::STA_RECEIVE_WAKER.wake();
1428 include::ESP_OK as esp_err_t
1429 }
1430 _ => {
1431 debug!("RX QUEUE FULL");
1432 include::ESP_ERR_NO_MEM as esp_err_t
1433 }
1434 }
1435}
1436
1437unsafe extern "C" fn recv_cb_ap(
1438 buffer: *mut c_types::c_void,
1439 len: u16,
1440 eb: *mut c_types::c_void,
1441) -> esp_err_t {
1442 let packet = EspWifiPacketBuffer { buffer, len, eb };
1443 match DATA_QUEUE_RX_AP.with(|queue| {
1450 if queue.len() < RX_QUEUE_SIZE {
1451 queue.push_back(packet);
1452 Ok(())
1453 } else {
1454 Err(packet)
1455 }
1456 }) {
1457 Ok(()) => {
1458 embassy::AP_RECEIVE_WAKER.wake();
1459 include::ESP_OK as esp_err_t
1460 }
1461 _ => {
1462 debug!("RX QUEUE FULL");
1463 include::ESP_ERR_NO_MEM as esp_err_t
1464 }
1465 }
1466}
1467
1468pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1469
1470fn decrement_inflight_counter() {
1471 unwrap!(
1472 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1473 Some(x.saturating_sub(1))
1474 })
1475 );
1476}
1477
1478#[ram]
1479unsafe extern "C" fn esp_wifi_tx_done_cb(
1480 _ifidx: u8,
1481 _data: *mut u8,
1482 _data_len: *mut u16,
1483 _tx_status: bool,
1484) {
1485 trace!("esp_wifi_tx_done_cb");
1486
1487 decrement_inflight_counter();
1488
1489 embassy::TRANSMIT_WAKER.wake();
1490}
1491
1492pub(crate) fn wifi_start() -> Result<(), WifiError> {
1493 unsafe {
1494 esp_wifi_result!(esp_wifi_start())?;
1495
1496 let mode = WifiMode::current()?;
1497
1498 if mode.is_ap() {
1500 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1501 wifi_interface_t_WIFI_IF_AP,
1502 crate::CONFIG.ap_beacon_timeout
1503 ))?;
1504 }
1505 if mode.is_sta() {
1506 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1507 wifi_interface_t_WIFI_IF_STA,
1508 crate::CONFIG.beacon_timeout
1509 ))?;
1510 };
1511 }
1512
1513 Ok(())
1514}
1515
1516#[derive(Clone, Copy, PartialEq, Eq)]
1525pub enum ScanTypeConfig {
1526 Active {
1536 min: Duration,
1538 max: Duration,
1540 },
1541 Passive(Duration),
1552}
1553
1554impl Default for ScanTypeConfig {
1555 fn default() -> Self {
1556 Self::Active {
1557 min: Duration::from_millis(10),
1558 max: Duration::from_millis(20),
1559 }
1560 }
1561}
1562
1563impl ScanTypeConfig {
1564 fn validate(&self) {
1565 if matches!(self, Self::Passive(dur) if *dur > Duration::from_millis(1500)) {
1566 warn!(
1567 "Passive scan duration longer than 1500ms may cause a station to disconnect from the AP"
1568 );
1569 }
1570 }
1571}
1572
1573#[derive(Clone, Copy, Default, PartialEq, Eq)]
1575pub struct ScanConfig<'a> {
1576 pub ssid: Option<&'a str>,
1581 pub bssid: Option<[u8; 6]>,
1586 pub channel: Option<u8>,
1591 pub show_hidden: bool,
1593 pub scan_type: ScanTypeConfig,
1595}
1596
1597pub(crate) fn wifi_start_scan(
1598 block: bool,
1599 ScanConfig {
1600 ssid,
1601 mut bssid,
1602 channel,
1603 show_hidden,
1604 scan_type,
1605 }: ScanConfig<'_>,
1606) -> i32 {
1607 scan_type.validate();
1608 let (scan_time, scan_type) = match scan_type {
1609 ScanTypeConfig::Active { min, max } => (
1610 wifi_scan_time_t {
1611 active: wifi_active_scan_time_t {
1612 min: min.as_millis() as u32,
1613 max: max.as_millis() as u32,
1614 },
1615 passive: 0,
1616 },
1617 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1618 ),
1619 ScanTypeConfig::Passive(dur) => (
1620 wifi_scan_time_t {
1621 active: wifi_active_scan_time_t { min: 0, max: 0 },
1622 passive: dur.as_millis() as u32,
1623 },
1624 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1625 ),
1626 };
1627
1628 let mut ssid_buf = ssid.map(|m| {
1629 let mut buf = alloc::vec::Vec::from_iter(m.bytes());
1630 buf.push(b'\0');
1631 buf
1632 });
1633
1634 let ssid = ssid_buf
1635 .as_mut()
1636 .map(|e| e.as_mut_ptr())
1637 .unwrap_or_else(core::ptr::null_mut);
1638 let bssid = bssid
1639 .as_mut()
1640 .map(|e| e.as_mut_ptr())
1641 .unwrap_or_else(core::ptr::null_mut);
1642
1643 let scan_config = wifi_scan_config_t {
1644 ssid,
1645 bssid,
1646 channel: channel.unwrap_or(0),
1647 show_hidden,
1648 scan_type,
1649 scan_time,
1650 home_chan_dwell_time: 0,
1651 channel_bitmap: wifi_scan_channel_bitmap_t {
1652 ghz_2_channels: 0,
1653 ghz_5_channels: 0,
1654 },
1655 };
1656
1657 unsafe { esp_wifi_scan_start(&scan_config, block) }
1658}
1659
1660mod private {
1661 use super::*;
1662
1663 #[derive(Debug)]
1664 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1665 pub struct EspWifiPacketBuffer {
1673 pub(crate) buffer: *mut c_types::c_void,
1674 pub(crate) len: u16,
1675 pub(crate) eb: *mut c_types::c_void,
1676 }
1677
1678 unsafe impl Send for EspWifiPacketBuffer {}
1679
1680 impl Drop for EspWifiPacketBuffer {
1681 fn drop(&mut self) {
1682 trace!("Dropping EspWifiPacketBuffer, freeing memory");
1683 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1684 }
1685 }
1686
1687 impl EspWifiPacketBuffer {
1688 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1689 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1690 }
1691 }
1692}
1693
1694#[derive(Debug, Clone, Copy)]
1696pub enum WifiDeviceMode {
1697 Sta,
1698 Ap,
1699}
1700
1701impl WifiDeviceMode {
1702 fn mac_address(&self) -> [u8; 6] {
1703 match self {
1704 WifiDeviceMode::Sta => {
1705 let mut mac = [0; 6];
1706 sta_mac(&mut mac);
1707 mac
1708 }
1709 WifiDeviceMode::Ap => {
1710 let mut mac = [0; 6];
1711 ap_mac(&mut mac);
1712 mac
1713 }
1714 }
1715 }
1716
1717 fn data_queue_rx(&self) -> &'static Locked<VecDeque<EspWifiPacketBuffer>> {
1718 match self {
1719 WifiDeviceMode::Sta => &DATA_QUEUE_RX_STA,
1720 WifiDeviceMode::Ap => &DATA_QUEUE_RX_AP,
1721 }
1722 }
1723
1724 fn can_send(&self) -> bool {
1725 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE
1726 }
1727
1728 fn increase_in_flight_counter(&self) {
1729 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1730 }
1731
1732 fn tx_token(&self) -> Option<WifiTxToken> {
1733 if !self.can_send() {
1734 crate::preempt::yield_task();
1735 }
1736
1737 if self.can_send() {
1738 Some(WifiTxToken { mode: *self })
1739 } else {
1740 None
1741 }
1742 }
1743
1744 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1745 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1746 if is_empty || !self.can_send() {
1747 crate::preempt::yield_task();
1748 }
1749
1750 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1751
1752 if !is_empty {
1753 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1754 } else {
1755 None
1756 }
1757 }
1758
1759 fn interface(&self) -> wifi_interface_t {
1760 match self {
1761 WifiDeviceMode::Sta => wifi_interface_t_WIFI_IF_STA,
1762 WifiDeviceMode::Ap => wifi_interface_t_WIFI_IF_AP,
1763 }
1764 }
1765
1766 fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) {
1767 embassy::TRANSMIT_WAKER.register(cx.waker())
1768 }
1769
1770 fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) {
1771 match self {
1772 WifiDeviceMode::Sta => embassy::STA_RECEIVE_WAKER.register(cx.waker()),
1773 WifiDeviceMode::Ap => embassy::AP_RECEIVE_WAKER.register(cx.waker()),
1774 }
1775 }
1776
1777 fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) {
1778 match self {
1779 WifiDeviceMode::Sta => embassy::STA_LINK_STATE_WAKER.register(cx.waker()),
1780 WifiDeviceMode::Ap => embassy::AP_LINK_STATE_WAKER.register(cx.waker()),
1781 }
1782 }
1783
1784 fn link_state(&self) -> embassy_net_driver::LinkState {
1785 match self {
1786 WifiDeviceMode::Sta => {
1787 if matches!(sta_state(), WifiState::StaConnected) {
1788 embassy_net_driver::LinkState::Up
1789 } else {
1790 embassy_net_driver::LinkState::Down
1791 }
1792 }
1793 WifiDeviceMode::Ap => {
1794 if matches!(ap_state(), WifiState::ApStarted) {
1795 embassy_net_driver::LinkState::Up
1796 } else {
1797 embassy_net_driver::LinkState::Down
1798 }
1799 }
1800 }
1801 }
1802}
1803
1804pub struct WifiDevice<'d> {
1806 _phantom: PhantomData<&'d ()>,
1807 mode: WifiDeviceMode,
1808}
1809
1810impl WifiDevice<'_> {
1811 pub fn mac_address(&self) -> [u8; 6] {
1813 self.mode.mac_address()
1814 }
1815
1816 #[cfg(not(feature = "smoltcp"))]
1819 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1820 self.mode.rx_token()
1821 }
1822
1823 #[cfg(not(feature = "smoltcp"))]
1826 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1827 self.mode.tx_token()
1828 }
1829}
1830
1831fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
1832 let str_len = record
1833 .ssid
1834 .iter()
1835 .position(|&c| c == 0)
1836 .unwrap_or(record.ssid.len());
1837 let ssid_ref = unsafe { core::str::from_utf8_unchecked(&record.ssid[..str_len]) };
1838
1839 let mut ssid = String::new();
1840 ssid.push_str(ssid_ref);
1841
1842 AccessPointInfo {
1843 ssid,
1844 bssid: record.bssid,
1845 channel: record.primary,
1846 secondary_channel: match record.second {
1847 include::wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
1848 include::wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
1849 include::wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
1850 _ => panic!(),
1851 },
1852 signal_strength: record.rssi,
1853 auth_method: Some(AuthMethod::from_raw(record.authmode)),
1854 }
1855}
1856
1857#[cfg(not(any(esp32c6)))]
1860#[derive(Debug, Clone, Copy)]
1861#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1862pub struct RxControlInfo {
1863 pub rssi: i32,
1865 pub rate: u32,
1868 pub sig_mode: u32,
1871 pub mcs: u32,
1874 pub cwb: u32,
1876 pub smoothing: u32,
1879 pub not_sounding: u32,
1882 pub aggregation: u32,
1884 pub stbc: u32,
1887 pub fec_coding: u32,
1890 pub sgi: u32,
1893 pub ampdu_cnt: u32,
1895 pub channel: u32,
1897 pub secondary_channel: u32,
1900 pub timestamp: u32,
1903 pub noise_floor: i32,
1905 pub ant: u32,
1908 pub sig_len: u32,
1910 pub rx_state: u32,
1912}
1913
1914#[cfg(esp32c6)]
1917#[derive(Debug, Clone, Copy)]
1918#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1919pub struct RxControlInfo {
1920 pub rssi: i32,
1922 pub rate: u32,
1925 pub sig_len: u32,
1927 pub rx_state: u32,
1930 pub dump_len: u32,
1932 pub he_sigb_len: u32,
1934 pub cur_single_mpdu: u32,
1936 pub cur_bb_format: u32,
1938 pub rx_channel_estimate_info_vld: u32,
1940 pub rx_channel_estimate_len: u32,
1942 pub second: u32,
1944 pub channel: u32,
1946 pub noise_floor: i32,
1948 pub is_group: u32,
1950 pub rxend_state: u32,
1952 pub rxmatch3: u32,
1954 pub rxmatch2: u32,
1956 pub rxmatch1: u32,
1958 pub rxmatch0: u32,
1960}
1961impl RxControlInfo {
1962 pub unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
1968 #[cfg(not(esp32c6))]
1969 let rx_control_info = unsafe {
1970 RxControlInfo {
1971 rssi: (*rx_cntl).rssi(),
1972 rate: (*rx_cntl).rate(),
1973 sig_mode: (*rx_cntl).sig_mode(),
1974 mcs: (*rx_cntl).mcs(),
1975 cwb: (*rx_cntl).cwb(),
1976 smoothing: (*rx_cntl).smoothing(),
1977 not_sounding: (*rx_cntl).not_sounding(),
1978 aggregation: (*rx_cntl).aggregation(),
1979 stbc: (*rx_cntl).stbc(),
1980 fec_coding: (*rx_cntl).fec_coding(),
1981 sgi: (*rx_cntl).sgi(),
1982 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
1983 channel: (*rx_cntl).channel(),
1984 secondary_channel: (*rx_cntl).secondary_channel(),
1985 timestamp: (*rx_cntl).timestamp(),
1986 noise_floor: (*rx_cntl).noise_floor(),
1987 ant: (*rx_cntl).ant(),
1988 sig_len: (*rx_cntl).sig_len(),
1989 rx_state: (*rx_cntl).rx_state(),
1990 }
1991 };
1992 #[cfg(esp32c6)]
1993 let rx_control_info = unsafe {
1994 RxControlInfo {
1995 rssi: (*rx_cntl).rssi(),
1996 rate: (*rx_cntl).rate(),
1997 sig_len: (*rx_cntl).sig_len(),
1998 rx_state: (*rx_cntl).rx_state(),
1999 dump_len: (*rx_cntl).dump_len(),
2000 he_sigb_len: (*rx_cntl).he_sigb_len(),
2001 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
2002 cur_bb_format: (*rx_cntl).cur_bb_format(),
2003 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
2004 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
2005 second: (*rx_cntl).second(),
2006 channel: (*rx_cntl).channel(),
2007 noise_floor: (*rx_cntl).noise_floor(),
2008 is_group: (*rx_cntl).is_group(),
2009 rxend_state: (*rx_cntl).rxend_state(),
2010 rxmatch3: (*rx_cntl).rxmatch3(),
2011 rxmatch2: (*rx_cntl).rxmatch2(),
2012 rxmatch1: (*rx_cntl).rxmatch1(),
2013 rxmatch0: (*rx_cntl).rxmatch0(),
2014 }
2015 };
2016 rx_control_info
2017 }
2018}
2019#[cfg(feature = "sniffer")]
2021pub struct PromiscuousPkt<'a> {
2022 pub rx_cntl: RxControlInfo,
2024 pub frame_type: wifi_promiscuous_pkt_type_t,
2026 pub len: usize,
2028 pub data: &'a [u8],
2030}
2031#[cfg(feature = "sniffer")]
2032impl PromiscuousPkt<'_> {
2033 pub(crate) unsafe fn from_raw(
2037 buf: *const wifi_promiscuous_pkt_t,
2038 frame_type: wifi_promiscuous_pkt_type_t,
2039 ) -> Self {
2040 let rx_cntl = unsafe { RxControlInfo::from_raw(&(*buf).rx_ctrl) };
2041 let len = rx_cntl.sig_len as usize;
2042 PromiscuousPkt {
2043 rx_cntl,
2044 frame_type,
2045 len,
2046 data: unsafe {
2047 core::slice::from_raw_parts(
2048 (buf as *const u8).add(core::mem::size_of::<wifi_pkt_rx_ctrl_t>()),
2049 len,
2050 )
2051 },
2052 }
2053 }
2054}
2055
2056#[cfg(feature = "sniffer")]
2057static SNIFFER_CB: Locked<Option<fn(PromiscuousPkt<'_>)>> = Locked::new(None);
2058
2059#[cfg(feature = "sniffer")]
2060unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) {
2061 unsafe {
2062 if let Some(sniffer_callback) = SNIFFER_CB.with(|callback| *callback) {
2063 let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type);
2064 sniffer_callback(promiscuous_pkt);
2065 }
2066 }
2067}
2068
2069#[cfg(feature = "sniffer")]
2070#[non_exhaustive]
2072pub struct Sniffer {}
2073
2074#[cfg(feature = "sniffer")]
2075impl Sniffer {
2076 pub(crate) fn new() -> Self {
2077 unwrap!(esp_wifi_result!(unsafe {
2080 esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb))
2081 }));
2082 Self {}
2083 }
2084 pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
2086 esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
2087 Ok(())
2088 }
2089 pub fn send_raw_frame(
2091 &mut self,
2092 use_sta_interface: bool,
2093 buffer: &[u8],
2094 use_internal_seq_num: bool,
2095 ) -> Result<(), WifiError> {
2096 esp_wifi_result!(unsafe {
2097 esp_wifi_80211_tx(
2098 if use_sta_interface {
2099 wifi_interface_t_WIFI_IF_STA
2100 } else {
2101 wifi_interface_t_WIFI_IF_AP
2102 } as wifi_interface_t,
2103 buffer.as_ptr() as *const _,
2104 buffer.len() as i32,
2105 use_internal_seq_num,
2106 )
2107 })
2108 }
2109 pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt<'_>)) {
2111 SNIFFER_CB.with(|callback| *callback = Some(cb));
2112 }
2113}
2114
2115#[cfg(feature = "smoltcp")]
2117impl Device for WifiDevice<'_> {
2118 type RxToken<'a>
2119 = WifiRxToken
2120 where
2121 Self: 'a;
2122 type TxToken<'a>
2123 = WifiTxToken
2124 where
2125 Self: 'a;
2126
2127 fn receive(
2128 &mut self,
2129 _instant: smoltcp::time::Instant,
2130 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2131 self.mode.rx_token()
2132 }
2133
2134 fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
2135 self.mode.tx_token()
2136 }
2137
2138 fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
2139 let mut caps = DeviceCapabilities::default();
2140 caps.max_transmission_unit = MTU;
2141 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2142 None
2143 } else {
2144 Some(crate::CONFIG.max_burst_size)
2145 };
2146 caps
2147 }
2148}
2149
2150#[doc(hidden)]
2151#[derive(Debug)]
2152pub struct WifiRxToken {
2153 mode: WifiDeviceMode,
2154}
2155
2156impl WifiRxToken {
2157 pub fn consume_token<R, F>(self, f: F) -> R
2160 where
2161 F: FnOnce(&mut [u8]) -> R,
2162 {
2163 let mut data = self.mode.data_queue_rx().with(|queue| {
2164 unwrap!(
2165 queue.pop_front(),
2166 "unreachable: transmit()/receive() ensures there is a packet to process"
2167 )
2168 });
2169
2170 let buffer = data.as_slice_mut();
2177 dump_packet_info(buffer);
2178
2179 f(buffer)
2180 }
2181}
2182
2183#[cfg(feature = "smoltcp")]
2184impl RxToken for WifiRxToken {
2185 fn consume<R, F>(self, f: F) -> R
2186 where
2187 F: FnOnce(&[u8]) -> R,
2188 {
2189 self.consume_token(|t| f(t))
2190 }
2191}
2192
2193#[doc(hidden)]
2194#[derive(Debug)]
2195pub struct WifiTxToken {
2196 mode: WifiDeviceMode,
2197}
2198
2199impl WifiTxToken {
2200 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
2203 where
2204 F: FnOnce(&mut [u8]) -> R,
2205 {
2206 self.mode.increase_in_flight_counter();
2207
2208 static mut BUFFER: [u8; MTU] = [0u8; MTU];
2212
2213 let buffer = unsafe { &mut BUFFER[..len] };
2214
2215 let res = f(buffer);
2216
2217 esp_wifi_send_data(self.mode.interface(), buffer);
2218
2219 res
2220 }
2221}
2222
2223#[cfg(feature = "smoltcp")]
2224impl TxToken for WifiTxToken {
2225 fn consume<R, F>(self, len: usize, f: F) -> R
2226 where
2227 F: FnOnce(&mut [u8]) -> R,
2228 {
2229 self.consume_token(len, f)
2230 }
2231}
2232
2233pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
2238 trace!("sending... {} bytes", data.len());
2239 dump_packet_info(data);
2240
2241 let len = data.len() as u16;
2242 let ptr = data.as_mut_ptr().cast();
2243
2244 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
2245
2246 if res != 0 {
2247 warn!("esp_wifi_internal_tx {}", res);
2248 decrement_inflight_counter();
2249 } else {
2250 trace!("esp_wifi_internal_tx ok");
2251 }
2252}
2253
2254fn apply_ap_config(config: &AccessPointConfiguration) -> Result<(), WifiError> {
2255 let mut cfg = wifi_config_t {
2256 ap: wifi_ap_config_t {
2257 ssid: [0; 32],
2258 password: [0; 64],
2259 ssid_len: 0,
2260 channel: config.channel,
2261 authmode: config.auth_method.to_raw(),
2262 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
2263 max_connection: config.max_connections as u8,
2264 beacon_interval: 100,
2265 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
2266 ftm_responder: false,
2267 pmf_cfg: wifi_pmf_config_t {
2268 capable: true,
2269 required: false,
2270 },
2271 sae_pwe_h2e: 0,
2272 csa_count: 3,
2273 dtim_period: 2,
2274 },
2275 };
2276
2277 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2278 return Err(WifiError::InternalError(
2279 InternalWifiError::EspErrInvalidArg,
2280 ));
2281 }
2282
2283 unsafe {
2284 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2285 cfg.ap.ssid_len = config.ssid.len() as u8;
2286 cfg.ap.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2287
2288 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
2289 }
2290}
2291
2292fn apply_sta_config(config: &ClientConfiguration) -> Result<(), WifiError> {
2293 let mut cfg = wifi_config_t {
2294 sta: wifi_sta_config_t {
2295 ssid: [0; 32],
2296 password: [0; 64],
2297 scan_method: crate::CONFIG.scan_method,
2298 bssid_set: config.bssid.is_some(),
2299 bssid: config.bssid.unwrap_or_default(),
2300 channel: config.channel.unwrap_or(0),
2301 listen_interval: crate::CONFIG.listen_interval,
2302 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2303 threshold: wifi_scan_threshold_t {
2304 rssi: -99,
2305 authmode: config.auth_method.to_raw(),
2306 },
2307 pmf_cfg: wifi_pmf_config_t {
2308 capable: true,
2309 required: false,
2310 },
2311 sae_pwe_h2e: 3,
2312 _bitfield_align_1: [0; 0],
2313 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2314 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2315 _bitfield_align_2: [0; 0],
2316 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2317 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2319 },
2320 };
2321
2322 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2323 return Err(WifiError::InternalError(
2324 InternalWifiError::EspErrInvalidArg,
2325 ));
2326 }
2327
2328 unsafe {
2329 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2330 cfg.sta.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2331
2332 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
2333 }
2334}
2335
2336fn apply_sta_eap_config(config: &EapClientConfiguration) -> Result<(), WifiError> {
2337 let mut cfg = wifi_config_t {
2338 sta: wifi_sta_config_t {
2339 ssid: [0; 32],
2340 password: [0; 64],
2341 scan_method: crate::CONFIG.scan_method,
2342 bssid_set: config.bssid.is_some(),
2343 bssid: config.bssid.unwrap_or_default(),
2344 channel: config.channel.unwrap_or(0),
2345 listen_interval: crate::CONFIG.listen_interval,
2346 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2347 threshold: wifi_scan_threshold_t {
2348 rssi: -99,
2349 authmode: config.auth_method.to_raw(),
2350 },
2351 pmf_cfg: wifi_pmf_config_t {
2352 capable: true,
2353 required: false,
2354 },
2355 sae_pwe_h2e: 3,
2356 _bitfield_align_1: [0; 0],
2357 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2358 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2359 _bitfield_align_2: [0; 0],
2360 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2361 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2363 },
2364 };
2365
2366 unsafe {
2367 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2368 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
2369
2370 if let Some(identity) = &config.identity {
2371 esp_wifi_result!(esp_eap_client_set_identity(
2372 identity.as_str().as_ptr(),
2373 identity.len() as i32
2374 ))?;
2375 } else {
2376 esp_eap_client_clear_identity();
2377 }
2378
2379 if let Some(username) = &config.username {
2380 esp_wifi_result!(esp_eap_client_set_username(
2381 username.as_str().as_ptr(),
2382 username.len() as i32
2383 ))?;
2384 } else {
2385 esp_eap_client_clear_username();
2386 }
2387
2388 if let Some(password) = &config.password {
2389 esp_wifi_result!(esp_eap_client_set_password(
2390 password.as_str().as_ptr(),
2391 password.len() as i32
2392 ))?;
2393 } else {
2394 esp_eap_client_clear_password();
2395 }
2396
2397 if let Some(new_password) = &config.new_password {
2398 esp_wifi_result!(esp_eap_client_set_new_password(
2399 new_password.as_str().as_ptr(),
2400 new_password.len() as i32
2401 ))?;
2402 } else {
2403 esp_eap_client_clear_new_password();
2404 }
2405
2406 if let Some(pac_file) = &config.pac_file {
2407 esp_wifi_result!(esp_eap_client_set_pac_file(
2408 pac_file.as_ptr(),
2409 pac_file.len() as i32
2410 ))?;
2411 }
2412
2413 if let Some(phase2_method) = &config.ttls_phase2_method {
2414 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
2415 phase2_method.to_raw()
2416 ))?;
2417 }
2418
2419 if let Some(ca_cert) = config.ca_cert {
2420 esp_wifi_result!(esp_eap_client_set_ca_cert(
2421 ca_cert.as_ptr(),
2422 ca_cert.len() as i32
2423 ))?;
2424 } else {
2425 esp_eap_client_clear_ca_cert();
2426 }
2427
2428 if let Some((cert, key, password)) = config.certificate_and_key {
2429 let (pwd, pwd_len) = if let Some(pwd) = password {
2430 (pwd.as_ptr(), pwd.len() as i32)
2431 } else {
2432 (core::ptr::null(), 0)
2433 };
2434
2435 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
2436 cert.as_ptr(),
2437 cert.len() as i32,
2438 key.as_ptr(),
2439 key.len() as i32,
2440 pwd,
2441 pwd_len,
2442 ))?;
2443 } else {
2444 esp_eap_client_clear_certificate_and_key();
2445 }
2446
2447 if let Some(cfg) = &config.eap_fast_config {
2448 let params = esp_eap_fast_config {
2449 fast_provisioning: cfg.fast_provisioning as i32,
2450 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
2451 fast_pac_format_binary: cfg.fast_pac_format_binary,
2452 };
2453 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
2454 }
2455
2456 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
2457
2458 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
2465
2466 Ok(())
2467 }
2468}
2469
2470fn dump_packet_info(_buffer: &mut [u8]) {
2471 #[cfg(dump_packets)]
2472 {
2473 info!("@WIFIFRAME {:?}", _buffer);
2474 }
2475}
2476
2477#[doc(hidden)]
2478#[macro_export]
2479macro_rules! esp_wifi_result {
2480 ($value:expr) => {{
2481 use num_traits::FromPrimitive;
2482 let result = $value;
2483 if result != esp_wifi_sys::include::ESP_OK as i32 {
2484 warn!("{} returned an error: {}", stringify!($value), result);
2485 Err(WifiError::InternalError(unwrap!(FromPrimitive::from_i32(
2486 result
2487 ))))
2488 } else {
2489 Ok::<(), WifiError>(())
2490 }
2491 }};
2492}
2493
2494pub(crate) mod embassy {
2495 use embassy_net_driver::{Capabilities, Driver, HardwareAddress, RxToken, TxToken};
2496 use esp_hal::asynch::AtomicWaker;
2497
2498 use super::*;
2499
2500 pub(crate) static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
2503
2504 pub(crate) static AP_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2505 pub(crate) static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2506
2507 pub(crate) static STA_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2508 pub(crate) static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2509
2510 impl RxToken for WifiRxToken {
2511 fn consume<R, F>(self, f: F) -> R
2512 where
2513 F: FnOnce(&mut [u8]) -> R,
2514 {
2515 self.consume_token(f)
2516 }
2517 }
2518
2519 impl TxToken for WifiTxToken {
2520 fn consume<R, F>(self, len: usize, f: F) -> R
2521 where
2522 F: FnOnce(&mut [u8]) -> R,
2523 {
2524 self.consume_token(len, f)
2525 }
2526 }
2527
2528 impl Driver for WifiDevice<'_> {
2529 type RxToken<'a>
2530 = WifiRxToken
2531 where
2532 Self: 'a;
2533 type TxToken<'a>
2534 = WifiTxToken
2535 where
2536 Self: 'a;
2537
2538 fn receive(
2539 &mut self,
2540 cx: &mut core::task::Context<'_>,
2541 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2542 self.mode.register_receive_waker(cx);
2543 self.mode.register_transmit_waker(cx);
2544 self.mode.rx_token()
2545 }
2546
2547 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
2548 self.mode.register_transmit_waker(cx);
2549 self.mode.tx_token()
2550 }
2551
2552 fn link_state(
2553 &mut self,
2554 cx: &mut core::task::Context<'_>,
2555 ) -> embassy_net_driver::LinkState {
2556 self.mode.register_link_state_waker(cx);
2557 self.mode.link_state()
2558 }
2559
2560 fn capabilities(&self) -> Capabilities {
2561 let mut caps = Capabilities::default();
2562 caps.max_transmission_unit = MTU;
2563 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2564 None
2565 } else {
2566 Some(crate::CONFIG.max_burst_size)
2567 };
2568 caps
2569 }
2570
2571 fn hardware_address(&self) -> HardwareAddress {
2572 HardwareAddress::Ethernet(self.mac_address())
2573 }
2574 }
2575}
2576
2577pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
2578 esp_wifi_result!(unsafe { esp_wifi_sys::include::esp_wifi_set_ps(ps.into()) })?;
2579 Ok(())
2580}
2581
2582struct FreeApListOnDrop;
2583impl FreeApListOnDrop {
2584 pub fn defuse(self) {
2585 core::mem::forget(self);
2586 }
2587}
2588
2589impl Drop for FreeApListOnDrop {
2590 fn drop(&mut self) {
2591 unsafe {
2592 include::esp_wifi_clear_ap_list();
2593 }
2594 }
2595}
2596
2597#[non_exhaustive]
2598pub struct Interfaces<'d> {
2599 pub sta: WifiDevice<'d>,
2600 pub ap: WifiDevice<'d>,
2601 #[cfg(feature = "esp-now")]
2602 pub esp_now: crate::esp_now::EspNow<'d>,
2603 #[cfg(feature = "sniffer")]
2604 pub sniffer: Sniffer,
2605}
2606
2607pub fn new<'d>(
2614 _inited: &'d EspWifiController<'d>,
2615 _device: crate::hal::peripherals::WIFI<'d>,
2616) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> {
2617 if crate::is_interrupts_disabled() {
2618 return Err(WifiError::Unsupported);
2619 }
2620
2621 let mut controller = WifiController {
2622 _phantom: Default::default(),
2623 };
2624
2625 crate::wifi::wifi_init()?;
2626
2627 let mut cntry_code = [0u8; 3];
2628 cntry_code[..crate::CONFIG.country_code.len()]
2629 .copy_from_slice(crate::CONFIG.country_code.as_bytes());
2630 cntry_code[2] = crate::CONFIG.country_code_operating_class;
2631
2632 unsafe {
2633 let country = wifi_country_t {
2634 cc: cntry_code,
2635 schan: 1,
2636 nchan: 13,
2637 max_tx_power: 20,
2638 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
2639 };
2640 esp_wifi_result!(esp_wifi_set_country(&country))?;
2641 }
2642
2643 controller.set_power_saving(PowerSaveMode::default())?;
2644
2645 Ok((
2646 controller,
2647 Interfaces {
2648 sta: WifiDevice {
2649 _phantom: Default::default(),
2650 mode: WifiDeviceMode::Sta,
2651 },
2652 ap: WifiDevice {
2653 _phantom: Default::default(),
2654 mode: WifiDeviceMode::Ap,
2655 },
2656 #[cfg(feature = "esp-now")]
2657 esp_now: crate::esp_now::EspNow::new_internal(),
2658 #[cfg(feature = "sniffer")]
2659 sniffer: Sniffer::new(),
2660 },
2661 ))
2662}
2663
2664#[non_exhaustive]
2665pub struct WifiController<'d> {
2666 _phantom: PhantomData<&'d ()>,
2667}
2668
2669impl Drop for WifiController<'_> {
2670 fn drop(&mut self) {
2671 if let Err(e) = crate::wifi::wifi_deinit() {
2672 warn!("Failed to cleanly deinit wifi: {:?}", e);
2673 }
2674 }
2675}
2676
2677impl WifiController<'_> {
2678 #[cfg(feature = "csi")]
2680 pub fn set_csi(
2681 &mut self,
2682 mut csi: CsiConfig,
2683 cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send,
2684 ) -> Result<(), WifiError> {
2685 csi.apply_config()?;
2686 csi.set_receive_cb(cb)?;
2687 csi.set_csi(true)?;
2688
2689 Ok(())
2690 }
2691
2692 pub fn set_protocol(&mut self, protocols: EnumSet<Protocol>) -> Result<(), WifiError> {
2707 let protocol = protocols
2708 .into_iter()
2709 .map(|v| match v {
2710 Protocol::P802D11B => WIFI_PROTOCOL_11B,
2711 Protocol::P802D11BG => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G,
2712 Protocol::P802D11BGN => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
2713 Protocol::P802D11BGNLR => {
2714 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR
2715 }
2716 Protocol::P802D11LR => WIFI_PROTOCOL_LR,
2717 Protocol::P802D11BGNAX => {
2718 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX
2719 }
2720 })
2721 .fold(0, |combined, protocol| combined | protocol) as u8;
2722
2723 let mode = self.mode()?;
2724 if mode.is_sta() {
2725 esp_wifi_result!(unsafe {
2726 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_STA, protocol)
2727 })?;
2728 }
2729 if mode.is_ap() {
2730 esp_wifi_result!(unsafe {
2731 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_AP, protocol)
2732 })?;
2733 }
2734
2735 Ok(())
2736 }
2737
2738 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
2740 apply_power_saving(ps)
2741 }
2742
2743 pub fn scan_with_config_sync(
2745 &mut self,
2746 config: ScanConfig<'_>,
2747 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2748 self.scan_with_config_sync_max(config, usize::MAX)
2749 }
2750
2751 pub fn scan_with_config_sync_max(
2752 &mut self,
2753 config: ScanConfig<'_>,
2754 max: usize,
2755 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2756 esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?;
2757 let result = self.scan_results(max)?;
2758 Ok(result)
2759 }
2760
2761 fn scan_results(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2762 let mut scanned = alloc::vec::Vec::<AccessPointInfo>::new();
2763 let mut bss_total: u16 = max as u16;
2764
2765 let guard = FreeApListOnDrop;
2767
2768 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
2769
2770 guard.defuse();
2771
2772 let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
2773 for _ in 0..usize::min(bss_total as usize, max) {
2774 let record = unsafe { MaybeUninit::assume_init_mut(&mut record) };
2775 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_record(record))? };
2776 let ap_info = convert_ap_info(record);
2777 scanned.push(ap_info);
2778 }
2779
2780 unsafe { esp_wifi_result!(include::esp_wifi_clear_ap_list())? };
2781
2782 Ok(scanned)
2783 }
2784
2785 pub fn scan_n(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2787 self.scan_with_config_sync_max(Default::default(), max)
2788 }
2789
2790 pub fn start(&mut self) -> Result<(), WifiError> {
2792 crate::wifi::wifi_start()
2793 }
2794
2795 pub fn stop(&mut self) -> Result<(), WifiError> {
2797 self.stop_impl()
2798 }
2799
2800 pub fn connect(&mut self) -> Result<(), WifiError> {
2807 self.connect_impl()
2808 }
2809
2810 pub fn disconnect(&mut self) -> Result<(), WifiError> {
2812 self.disconnect_impl()
2813 }
2814
2815 pub fn rssi(&self) -> Result<i32, WifiError> {
2828 if self.mode()?.is_sta() {
2829 let mut rssi: i32 = 0;
2830 esp_wifi_result!(unsafe { esp_wifi_sta_get_rssi(&mut rssi) })?;
2832 Ok(rssi)
2833 } else {
2834 Err(WifiError::Unsupported)
2835 }
2836 }
2837
2838 pub fn capabilities(&self) -> Result<EnumSet<crate::wifi::Capability>, WifiError> {
2840 let caps =
2841 enumset::enum_set! { Capability::Client | Capability::AccessPoint | Capability::Mixed };
2842 Ok(caps)
2843 }
2844
2845 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), WifiError> {
2855 conf.validate()?;
2856
2857 let mode = match conf {
2858 Configuration::None => wifi_mode_t_WIFI_MODE_NULL,
2859 Configuration::Client(_) => wifi_mode_t_WIFI_MODE_STA,
2860 Configuration::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
2861 Configuration::Mixed(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
2862 Configuration::EapClient(_) => wifi_mode_t_WIFI_MODE_STA,
2863 };
2864
2865 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
2866
2867 match conf {
2868 Configuration::None => Ok::<(), WifiError>(()),
2869 Configuration::Client(config) => apply_sta_config(config),
2870 Configuration::AccessPoint(config) => apply_ap_config(config),
2871 Configuration::Mixed(sta_config, ap_config) => {
2872 apply_ap_config(ap_config).and_then(|()| apply_sta_config(sta_config))
2873 }
2874 Configuration::EapClient(config) => apply_sta_eap_config(config),
2875 }
2876 .inspect_err(|_| {
2877 unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL) };
2881 })?;
2882
2883 Ok(())
2884 }
2885
2886 pub fn set_mode(&mut self, mode: WifiMode) -> Result<(), WifiError> {
2890 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?;
2891 Ok(())
2892 }
2893
2894 fn stop_impl(&mut self) -> Result<(), WifiError> {
2895 esp_wifi_result!(unsafe { esp_wifi_stop() })
2896 }
2897
2898 fn connect_impl(&mut self) -> Result<(), WifiError> {
2899 esp_wifi_result!(unsafe { esp_wifi_connect() })
2900 }
2901
2902 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
2903 esp_wifi_result!(unsafe { esp_wifi_disconnect() })
2904 }
2905
2906 pub fn is_started(&self) -> Result<bool, WifiError> {
2911 if matches!(
2912 crate::wifi::sta_state(),
2913 WifiState::StaStarted | WifiState::StaConnected | WifiState::StaDisconnected
2914 ) {
2915 return Ok(true);
2916 }
2917 if matches!(crate::wifi::ap_state(), WifiState::ApStarted) {
2918 return Ok(true);
2919 }
2920 Ok(false)
2921 }
2922
2923 pub fn is_connected(&self) -> Result<bool, WifiError> {
2928 match crate::wifi::sta_state() {
2929 crate::wifi::WifiState::StaConnected => Ok(true),
2930 crate::wifi::WifiState::StaDisconnected => Err(WifiError::Disconnected),
2931 _ => Ok(false),
2933 }
2934 }
2935
2936 fn mode(&self) -> Result<WifiMode, WifiError> {
2937 WifiMode::current()
2938 }
2939
2940 pub async fn scan_n_async(
2942 &mut self,
2943 max: usize,
2944 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2945 self.scan_with_config_async_max(Default::default(), max)
2946 .await
2947 }
2948
2949 pub async fn scan_with_config_async(
2951 &mut self,
2952 config: ScanConfig<'_>,
2953 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2954 self.scan_with_config_async_max(config, usize::MAX).await
2955 }
2956
2957 async fn scan_with_config_async_max(
2958 &mut self,
2959 config: ScanConfig<'_>,
2960 max: usize,
2961 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2962 Self::clear_events(WifiEvent::ScanDone);
2963 esp_wifi_result!(wifi_start_scan(false, config))?;
2964
2965 let guard = FreeApListOnDrop;
2967 WifiEventFuture::new(WifiEvent::ScanDone).await;
2968
2969 guard.defuse();
2970
2971 let result = self.scan_results(max)?;
2972
2973 Ok(result)
2974 }
2975
2976 pub async fn start_async(&mut self) -> Result<(), WifiError> {
2978 let mut events = enumset::enum_set! {};
2979
2980 let mode = self.mode()?;
2981 if mode.is_ap() {
2982 events |= WifiEvent::ApStart;
2983 }
2984 if mode.is_sta() {
2985 events |= WifiEvent::StaStart;
2986 }
2987
2988 Self::clear_events(events);
2989
2990 wifi_start()?;
2991
2992 self.wait_for_all_events(events, false).await;
2993
2994 Ok(())
2995 }
2996
2997 pub async fn stop_async(&mut self) -> Result<(), WifiError> {
2999 let mut events = enumset::enum_set! {};
3000
3001 let mode = self.mode()?;
3002 if mode.is_ap() {
3003 events |= WifiEvent::ApStop;
3004 }
3005 if mode.is_sta() {
3006 events |= WifiEvent::StaStop;
3007 }
3008
3009 Self::clear_events(events);
3010
3011 crate::wifi::WifiController::stop_impl(self)?;
3012
3013 self.wait_for_all_events(events, false).await;
3014
3015 reset_ap_state();
3016 reset_sta_state();
3017
3018 Ok(())
3019 }
3020
3021 pub async fn connect_async(&mut self) -> Result<(), WifiError> {
3023 Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected);
3024
3025 let err = crate::wifi::WifiController::connect_impl(self).err();
3026
3027 if MultiWifiEventFuture::new(WifiEvent::StaConnected | WifiEvent::StaDisconnected)
3028 .await
3029 .contains(WifiEvent::StaDisconnected)
3030 {
3031 Err(err.unwrap_or(WifiError::Disconnected))
3032 } else {
3033 Ok(())
3034 }
3035 }
3036
3037 pub async fn disconnect_async(&mut self) -> Result<(), WifiError> {
3040 if !matches!(self.is_connected(), Ok(true)) {
3044 return Ok(());
3045 }
3046
3047 Self::clear_events(WifiEvent::StaDisconnected);
3048 crate::wifi::WifiController::disconnect_impl(self)?;
3049 WifiEventFuture::new(WifiEvent::StaDisconnected).await;
3050
3051 Ok(())
3052 }
3053
3054 fn clear_events(events: impl Into<EnumSet<WifiEvent>>) {
3055 WIFI_EVENTS.with(|evts| evts.get_mut().remove_all(events.into()));
3056 }
3057
3058 pub async fn wait_for_event(&mut self, event: WifiEvent) {
3060 Self::clear_events(event);
3061 WifiEventFuture::new(event).await
3062 }
3063
3064 pub async fn wait_for_events(
3067 &mut self,
3068 events: EnumSet<WifiEvent>,
3069 clear_pending: bool,
3070 ) -> EnumSet<WifiEvent> {
3071 if clear_pending {
3072 Self::clear_events(events);
3073 }
3074 MultiWifiEventFuture::new(events).await
3075 }
3076
3077 pub async fn wait_for_all_events(
3079 &mut self,
3080 mut events: EnumSet<WifiEvent>,
3081 clear_pending: bool,
3082 ) {
3083 if clear_pending {
3084 Self::clear_events(events);
3085 }
3086
3087 while !events.is_empty() {
3088 let fired = MultiWifiEventFuture::new(events).await;
3089 events -= fired;
3090 }
3091 }
3092}
3093
3094impl WifiEvent {
3095 pub(crate) fn waker(&self) -> &'static AtomicWaker {
3096 static WAKER: AtomicWaker = AtomicWaker::new();
3100 &WAKER
3101 }
3102}
3103
3104#[must_use = "futures do nothing unless you `.await` or poll them"]
3105pub(crate) struct WifiEventFuture {
3106 event: WifiEvent,
3107}
3108
3109impl WifiEventFuture {
3110 pub fn new(event: WifiEvent) -> Self {
3112 Self { event }
3113 }
3114}
3115
3116impl core::future::Future for WifiEventFuture {
3117 type Output = ();
3118
3119 fn poll(
3120 self: core::pin::Pin<&mut Self>,
3121 cx: &mut core::task::Context<'_>,
3122 ) -> Poll<Self::Output> {
3123 self.event.waker().register(cx.waker());
3124 if WIFI_EVENTS.with(|events| events.get_mut().remove(self.event)) {
3125 Poll::Ready(())
3126 } else {
3127 Poll::Pending
3128 }
3129 }
3130}
3131
3132#[must_use = "futures do nothing unless you `.await` or poll them"]
3133pub(crate) struct MultiWifiEventFuture {
3134 event: EnumSet<WifiEvent>,
3135}
3136
3137impl MultiWifiEventFuture {
3138 pub fn new(event: EnumSet<WifiEvent>) -> Self {
3140 Self { event }
3141 }
3142}
3143
3144impl core::future::Future for MultiWifiEventFuture {
3145 type Output = EnumSet<WifiEvent>;
3146
3147 fn poll(
3148 self: core::pin::Pin<&mut Self>,
3149 cx: &mut core::task::Context<'_>,
3150 ) -> Poll<Self::Output> {
3151 let output = WIFI_EVENTS.with(|events| {
3152 let events = events.get_mut();
3153 let active = events.intersection(self.event);
3154 events.remove_all(active);
3155 active
3156 });
3157 if output.is_empty() {
3158 for event in self.event.iter() {
3159 event.waker().register(cx.waker());
3160 }
3161
3162 Poll::Pending
3163 } else {
3164 Poll::Ready(output)
3165 }
3166 }
3167}