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_start,
111 esp_wifi_stop,
112 g_wifi_default_wpa_crypto_funcs,
113 wifi_active_scan_time_t,
114 wifi_ap_config_t,
115 wifi_auth_mode_t,
116 wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
117 wifi_config_t,
118 wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
119 wifi_country_t,
120 wifi_interface_t,
121 wifi_interface_t_WIFI_IF_AP,
122 wifi_interface_t_WIFI_IF_STA,
123 wifi_mode_t,
124 wifi_mode_t_WIFI_MODE_AP,
125 wifi_mode_t_WIFI_MODE_APSTA,
126 wifi_mode_t_WIFI_MODE_NULL,
127 wifi_mode_t_WIFI_MODE_STA,
128 wifi_pmf_config_t,
129 wifi_scan_config_t,
130 wifi_scan_threshold_t,
131 wifi_scan_time_t,
132 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
133 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
134 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
135 wifi_sta_config_t,
136 },
137};
138
139#[derive(EnumSetType, Debug, PartialOrd)]
141#[cfg_attr(feature = "defmt", derive(defmt::Format))]
142#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
143#[derive(Default)]
144#[allow(clippy::upper_case_acronyms)] pub enum AuthMethod {
146 None,
148
149 WEP,
151
152 WPA,
154
155 #[default]
157 WPA2Personal,
158
159 WPAWPA2Personal,
161
162 WPA2Enterprise,
164
165 WPA3Personal,
167
168 WPA2WPA3Personal,
170
171 WAPIPersonal,
173}
174
175#[derive(EnumSetType, Debug, PartialOrd)]
177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
178#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
179#[derive(Default)]
180pub enum Protocol {
181 P802D11B,
183
184 P802D11BG,
186
187 #[default]
189 P802D11BGN,
190
191 P802D11BGNLR,
193
194 P802D11LR,
196
197 P802D11BGNAX,
199}
200
201#[derive(EnumSetType, Debug, PartialOrd)]
203#[cfg_attr(feature = "defmt", derive(defmt::Format))]
204#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
205#[derive(Default)]
206pub enum SecondaryChannel {
207 #[default]
210 None,
211
212 Above,
214
215 Below,
217}
218
219#[derive(Clone, Debug, Default, PartialEq, Eq)]
221#[cfg_attr(feature = "defmt", derive(defmt::Format))]
222#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
223pub struct AccessPointInfo {
224 #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
227 pub ssid: String,
228
229 pub bssid: [u8; 6],
231
232 pub channel: u8,
234
235 pub secondary_channel: SecondaryChannel,
237
238 pub signal_strength: i8,
240
241 pub auth_method: Option<AuthMethod>,
243}
244
245#[derive(Clone, PartialEq, Eq)]
247#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
248pub struct AccessPointConfiguration {
249 pub ssid: String,
251
252 pub ssid_hidden: bool,
254
255 pub channel: u8,
257
258 pub secondary_channel: Option<u8>,
260
261 pub protocols: EnumSet<Protocol>,
263
264 pub auth_method: AuthMethod,
266
267 pub password: String,
269
270 pub max_connections: u16,
272}
273
274impl AccessPointConfiguration {
275 fn validate(&self) -> Result<(), WifiError> {
276 if self.ssid.len() > 32 {
277 return Err(WifiError::InvalidArguments);
278 }
279
280 if self.password.len() > 64 {
281 return Err(WifiError::InvalidArguments);
282 }
283
284 Ok(())
285 }
286}
287
288impl Default for AccessPointConfiguration {
289 fn default() -> Self {
290 Self {
291 ssid: String::from("iot-device"),
292 ssid_hidden: false,
293 channel: 1,
294 secondary_channel: None,
295 protocols: (Protocol::P802D11B | Protocol::P802D11BG | Protocol::P802D11BGN),
296 auth_method: AuthMethod::None,
297 password: String::new(),
298 max_connections: 255,
299 }
300 }
301}
302
303impl core::fmt::Debug for AccessPointConfiguration {
304 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
305 f.debug_struct("AccessPointConfiguration")
306 .field("ssid", &self.ssid)
307 .field("ssid_hidden", &self.ssid_hidden)
308 .field("channel", &self.channel)
309 .field("secondary_channel", &self.secondary_channel)
310 .field("protocols", &self.protocols)
311 .field("auth_method", &self.auth_method)
312 .field("password", &"**REDACTED**")
313 .field("max_connections", &self.max_connections)
314 .finish()
315 }
316}
317
318#[cfg(feature = "defmt")]
319impl defmt::Format for AccessPointConfiguration {
320 fn format(&self, fmt: defmt::Formatter<'_>) {
321 #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Default)]
322 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
323 pub struct ProtocolSet(EnumSet<Protocol>);
324
325 #[cfg(feature = "defmt")]
326 impl defmt::Format for ProtocolSet {
327 fn format(&self, fmt: defmt::Formatter<'_>) {
328 for (i, p) in self.0.into_iter().enumerate() {
329 if i > 0 {
330 defmt::write!(fmt, " ");
331 }
332 defmt::write!(fmt, "{}", p);
333 }
334 }
335 }
336
337 let protocol_set = ProtocolSet(self.protocols);
338
339 defmt::write!(
340 fmt,
341 "AccessPointConfiguration {{\
342 ssid: {}, \
343 ssid_hidden: {}, \
344 channel: {}, \
345 secondary_channel: {}, \
346 protocols: {}, \
347 auth_method: {}, \
348 password: **REDACTED**, \
349 max_connections: {}, \
350 }}",
351 self.ssid.as_str(),
352 self.ssid_hidden,
353 self.channel,
354 self.secondary_channel,
355 protocol_set,
356 self.auth_method,
357 self.max_connections
358 );
359 }
360}
361
362#[derive(Clone, PartialEq, Eq, Default)]
364#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
365pub struct ClientConfiguration {
366 pub ssid: String,
368
369 pub bssid: Option<[u8; 6]>,
371
372 pub auth_method: AuthMethod,
375
376 pub password: String,
378
379 pub channel: Option<u8>,
381}
382
383impl ClientConfiguration {
384 fn validate(&self) -> Result<(), WifiError> {
385 if self.ssid.len() > 32 {
386 return Err(WifiError::InvalidArguments);
387 }
388
389 if self.password.len() > 64 {
390 return Err(WifiError::InvalidArguments);
391 }
392
393 Ok(())
394 }
395}
396
397impl core::fmt::Debug for ClientConfiguration {
398 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
399 f.debug_struct("ClientConfiguration")
400 .field("ssid", &self.ssid)
401 .field("bssid", &self.bssid)
402 .field("auth_method", &self.auth_method)
403 .field("password", &"**REDACTED**")
404 .field("channel", &self.channel)
405 .finish()
406 }
407}
408
409#[cfg(feature = "defmt")]
410impl defmt::Format for ClientConfiguration {
411 fn format(&self, fmt: defmt::Formatter<'_>) {
412 defmt::write!(
413 fmt,
414 "ClientConfiguration {{\
415 ssid: {}, \
416 bssid: {:?}, \
417 auth_method: {:?}, \
418 password: **REDACTED**, \
419 channel: {:?}, \
420 }}",
421 self.ssid.as_str(),
422 self.bssid,
423 self.auth_method,
424 self.channel
425 )
426 }
427}
428
429#[derive(Clone, Debug, PartialEq, Eq)]
431#[cfg_attr(feature = "defmt", derive(defmt::Format))]
432#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
433pub struct EapFastConfig {
434 pub fast_provisioning: u8,
436 pub fast_max_pac_list_len: u8,
438 pub fast_pac_format_binary: bool,
440}
441
442#[derive(Debug, Clone, PartialEq, Eq)]
444#[cfg_attr(feature = "defmt", derive(defmt::Format))]
445#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
446pub enum TtlsPhase2Method {
447 Eap,
449
450 Mschapv2,
452
453 Mschap,
455
456 Pap,
458
459 Chap,
461}
462
463impl TtlsPhase2Method {
464 fn to_raw(&self) -> u32 {
466 match self {
467 TtlsPhase2Method::Eap => {
468 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_EAP
469 }
470 TtlsPhase2Method::Mschapv2 => {
471 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAPV2
472 }
473 TtlsPhase2Method::Mschap => {
474 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAP
475 }
476 TtlsPhase2Method::Pap => {
477 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_PAP
478 }
479 TtlsPhase2Method::Chap => {
480 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_CHAP
481 }
482 }
483 }
484}
485
486#[derive(Clone, PartialEq, Eq)]
488#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
489pub struct EapClientConfiguration {
490 pub ssid: String,
492
493 pub bssid: Option<[u8; 6]>,
495
496 pub auth_method: AuthMethod,
499
500 pub identity: Option<String>,
502
503 pub username: Option<String>,
506
507 pub password: Option<String>,
509
510 pub new_password: Option<String>,
513
514 pub eap_fast_config: Option<EapFastConfig>,
516
517 pub pac_file: Option<&'static [u8]>,
519
520 pub time_check: bool,
523
524 pub ca_cert: Option<&'static [u8]>,
527
528 #[allow(clippy::type_complexity)]
531 pub certificate_and_key: Option<(&'static [u8], &'static [u8], Option<&'static [u8]>)>,
532
533 pub ttls_phase2_method: Option<TtlsPhase2Method>,
535
536 pub channel: Option<u8>,
538}
539
540impl EapClientConfiguration {
541 fn validate(&self) -> Result<(), WifiError> {
542 if self.ssid.len() > 32 {
543 return Err(WifiError::InvalidArguments);
544 }
545
546 if self.identity.as_ref().unwrap_or(&String::new()).len() > 128 {
547 return Err(WifiError::InvalidArguments);
548 }
549
550 if self.username.as_ref().unwrap_or(&String::new()).len() > 128 {
551 return Err(WifiError::InvalidArguments);
552 }
553
554 if self.password.as_ref().unwrap_or(&String::new()).len() > 64 {
555 return Err(WifiError::InvalidArguments);
556 }
557
558 if self.new_password.as_ref().unwrap_or(&String::new()).len() > 64 {
559 return Err(WifiError::InvalidArguments);
560 }
561
562 Ok(())
563 }
564}
565
566impl Debug for EapClientConfiguration {
567 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
568 f.debug_struct("EapClientConfiguration")
569 .field("ssid", &self.ssid)
570 .field("bssid", &self.bssid)
571 .field("auth_method", &self.auth_method)
572 .field("channel", &self.channel)
573 .field("identity", &self.identity)
574 .field("username", &self.username)
575 .field("password", &"**REDACTED**")
576 .field("new_password", &"**REDACTED**")
577 .field("eap_fast_config", &self.eap_fast_config)
578 .field("time_check", &self.time_check)
579 .field("pac_file set", &self.pac_file.is_some())
580 .field("ca_cert set", &self.ca_cert.is_some())
581 .field("certificate_and_key set", &"**REDACTED**")
582 .field("ttls_phase2_method", &self.ttls_phase2_method)
583 .finish()
584 }
585}
586
587#[cfg(feature = "defmt")]
588impl defmt::Format for EapClientConfiguration {
589 fn format(&self, fmt: defmt::Formatter<'_>) {
590 defmt::write!(
591 fmt,
592 "EapClientConfiguration {{\
593 ssid: {}, \
594 bssid: {:?}, \
595 auth_method: {:?}, \
596 channel: {:?}, \
597 identity: {:?}, \
598 username: {:?}, \
599 password: **REDACTED**, \
600 new_password: **REDACTED**, \
601 eap_fast_config: {:?}, \
602 time_check: {}, \
603 pac_file: {}, \
604 ca_cert: {}, \
605 certificate_and_key: **REDACTED**, \
606 ttls_phase2_method: {:?}, \
607 }}",
608 self.ssid.as_str(),
609 self.bssid,
610 self.auth_method,
611 self.channel,
612 &self.identity.as_ref().map_or("", |v| v.as_str()),
613 &self.username.as_ref().map_or("", |v| v.as_str()),
614 self.eap_fast_config,
615 self.time_check,
616 self.pac_file,
617 self.ca_cert,
618 self.ttls_phase2_method,
619 )
620 }
621}
622
623impl Default for EapClientConfiguration {
624 fn default() -> Self {
625 EapClientConfiguration {
626 ssid: String::new(),
627 bssid: None,
628 auth_method: AuthMethod::WPA2Enterprise,
629 identity: None,
630 username: None,
631 password: None,
632 channel: None,
633 eap_fast_config: None,
634 time_check: false,
635 new_password: None,
636 pac_file: None,
637 ca_cert: None,
638 certificate_and_key: None,
639 ttls_phase2_method: None,
640 }
641 }
642}
643
644#[derive(EnumSetType, Debug, PartialOrd)]
646#[cfg_attr(feature = "defmt", derive(defmt::Format))]
647#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
648pub enum Capability {
649 Client,
651
652 AccessPoint,
655
656 Mixed,
659}
660
661#[derive(Clone, Debug, PartialEq, Eq, Default)]
663#[cfg_attr(feature = "defmt", derive(defmt::Format))]
664#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
665#[allow(clippy::large_enum_variant)]
666pub enum Configuration {
667 #[default]
669 None,
670
671 Client(ClientConfiguration),
673
674 AccessPoint(AccessPointConfiguration),
676
677 Mixed(ClientConfiguration, AccessPointConfiguration),
679
680 #[cfg_attr(feature = "serde", serde(skip))]
682 EapClient(EapClientConfiguration),
683}
684
685impl Configuration {
686 fn validate(&self) -> Result<(), WifiError> {
687 match self {
688 Configuration::None => Ok(()),
689 Configuration::Client(client_configuration) => client_configuration.validate(),
690 Configuration::AccessPoint(access_point_configuration) => {
691 access_point_configuration.validate()
692 }
693 Configuration::Mixed(client_configuration, access_point_configuration) => {
694 client_configuration.validate()?;
695 access_point_configuration.validate()
696 }
697 Configuration::EapClient(eap_client_configuration) => {
698 eap_client_configuration.validate()
699 }
700 }
701 }
702
703 pub fn as_client_conf_ref(&self) -> Option<&ClientConfiguration> {
705 match self {
706 Self::Client(client_conf) | Self::Mixed(client_conf, _) => Some(client_conf),
707 _ => None,
708 }
709 }
710
711 pub fn as_ap_conf_ref(&self) -> Option<&AccessPointConfiguration> {
713 match self {
714 Self::AccessPoint(ap_conf) | Self::Mixed(_, ap_conf) => Some(ap_conf),
715 _ => None,
716 }
717 }
718
719 pub fn as_client_conf_mut(&mut self) -> &mut ClientConfiguration {
722 match self {
723 Self::Client(client_conf) => client_conf,
724 Self::Mixed(_, _) => {
725 let prev = mem::replace(self, Self::None);
726 match prev {
727 Self::Mixed(client_conf, _) => {
728 *self = Self::Client(client_conf);
729 self.as_client_conf_mut()
730 }
731 _ => unreachable!(),
732 }
733 }
734 _ => {
735 *self = Self::Client(Default::default());
736 self.as_client_conf_mut()
737 }
738 }
739 }
740
741 pub fn as_ap_conf_mut(&mut self) -> &mut AccessPointConfiguration {
744 match self {
745 Self::AccessPoint(ap_conf) => ap_conf,
746 Self::Mixed(_, _) => {
747 let prev = mem::replace(self, Self::None);
748 match prev {
749 Self::Mixed(_, ap_conf) => {
750 *self = Self::AccessPoint(ap_conf);
751 self.as_ap_conf_mut()
752 }
753 _ => unreachable!(),
754 }
755 }
756 _ => {
757 *self = Self::AccessPoint(Default::default());
758 self.as_ap_conf_mut()
759 }
760 }
761 }
762
763 pub fn as_mixed_conf_mut(
766 &mut self,
767 ) -> (&mut ClientConfiguration, &mut AccessPointConfiguration) {
768 match self {
769 Self::Mixed(client_conf, ap_conf) => (client_conf, ap_conf),
770 Self::AccessPoint(_) => {
771 let prev = mem::replace(self, Self::None);
772 match prev {
773 Self::AccessPoint(ap_conf) => {
774 *self = Self::Mixed(Default::default(), ap_conf);
775 self.as_mixed_conf_mut()
776 }
777 _ => unreachable!(),
778 }
779 }
780 Self::Client(_) => {
781 let prev = mem::replace(self, Self::None);
782 match prev {
783 Self::Client(client_conf) => {
784 *self = Self::Mixed(client_conf, Default::default());
785 self.as_mixed_conf_mut()
786 }
787 _ => unreachable!(),
788 }
789 }
790 _ => {
791 *self = Self::Mixed(Default::default(), Default::default());
792 self.as_mixed_conf_mut()
793 }
794 }
795 }
796}
797
798trait AuthMethodExt {
799 fn to_raw(&self) -> wifi_auth_mode_t;
800 fn from_raw(raw: wifi_auth_mode_t) -> Self;
801}
802
803impl AuthMethodExt for AuthMethod {
804 fn to_raw(&self) -> wifi_auth_mode_t {
805 match self {
806 AuthMethod::None => include::wifi_auth_mode_t_WIFI_AUTH_OPEN,
807 AuthMethod::WEP => include::wifi_auth_mode_t_WIFI_AUTH_WEP,
808 AuthMethod::WPA => include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
809 AuthMethod::WPA2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
810 AuthMethod::WPAWPA2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK,
811 AuthMethod::WPA2Enterprise => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE,
812 AuthMethod::WPA3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
813 AuthMethod::WPA2WPA3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK,
814 AuthMethod::WAPIPersonal => include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
815 }
816 }
817
818 fn from_raw(raw: wifi_auth_mode_t) -> Self {
819 match raw {
820 include::wifi_auth_mode_t_WIFI_AUTH_OPEN => AuthMethod::None,
821 include::wifi_auth_mode_t_WIFI_AUTH_WEP => AuthMethod::WEP,
822 include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => AuthMethod::WPA,
823 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => AuthMethod::WPA2Personal,
824 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => AuthMethod::WPAWPA2Personal,
825 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => AuthMethod::WPA2Enterprise,
826 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => AuthMethod::WPA3Personal,
827 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => AuthMethod::WPA2WPA3Personal,
828 include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => AuthMethod::WAPIPersonal,
829 _ => unreachable!(),
830 }
831 }
832}
833
834#[derive(Debug, Clone, Copy, PartialEq)]
836#[cfg_attr(feature = "defmt", derive(defmt::Format))]
837#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
838pub enum WifiMode {
839 Sta,
841 Ap,
843 ApSta,
845}
846
847impl WifiMode {
848 pub(crate) fn current() -> Result<Self, WifiError> {
849 let mut mode = wifi_mode_t_WIFI_MODE_NULL;
850 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?;
851
852 Self::try_from(mode)
853 }
854
855 pub fn is_sta(&self) -> bool {
857 match self {
858 Self::Sta | Self::ApSta => true,
859 Self::Ap => false,
860 }
861 }
862
863 pub fn is_ap(&self) -> bool {
865 match self {
866 Self::Sta => false,
867 Self::Ap | Self::ApSta => true,
868 }
869 }
870}
871
872impl TryFrom<&Configuration> for WifiMode {
873 type Error = WifiError;
874
875 fn try_from(config: &Configuration) -> Result<Self, Self::Error> {
877 let mode = match config {
878 Configuration::None => return Err(WifiError::UnknownWifiMode),
879 Configuration::AccessPoint(_) => Self::Ap,
880 Configuration::Client(_) => Self::Sta,
881 Configuration::Mixed(_, _) => Self::ApSta,
882 Configuration::EapClient(_) => Self::Sta,
883 };
884
885 Ok(mode)
886 }
887}
888
889impl TryFrom<wifi_mode_t> for WifiMode {
890 type Error = WifiError;
891
892 fn try_from(value: wifi_mode_t) -> Result<Self, Self::Error> {
894 #[allow(non_upper_case_globals)]
895 match value {
896 include::wifi_mode_t_WIFI_MODE_STA => Ok(Self::Sta),
897 include::wifi_mode_t_WIFI_MODE_AP => Ok(Self::Ap),
898 include::wifi_mode_t_WIFI_MODE_APSTA => Ok(Self::ApSta),
899 _ => Err(WifiError::UnknownWifiMode),
900 }
901 }
902}
903
904impl From<WifiMode> for wifi_mode_t {
905 fn from(val: WifiMode) -> Self {
906 #[allow(non_upper_case_globals)]
907 match val {
908 WifiMode::Sta => wifi_mode_t_WIFI_MODE_STA,
909 WifiMode::Ap => wifi_mode_t_WIFI_MODE_AP,
910 WifiMode::ApSta => wifi_mode_t_WIFI_MODE_APSTA,
911 }
912 }
913}
914
915#[cfg(feature = "csi")]
916pub(crate) trait CsiCallback: FnMut(crate::binary::include::wifi_csi_info_t) {}
917
918#[cfg(feature = "csi")]
919impl<T> CsiCallback for T where T: FnMut(crate::binary::include::wifi_csi_info_t) {}
920
921#[cfg(feature = "csi")]
922unsafe extern "C" fn csi_rx_cb<C: CsiCallback>(
923 ctx: *mut crate::wifi::c_types::c_void,
924 data: *mut crate::binary::include::wifi_csi_info_t,
925) {
926 unsafe {
927 let csi_callback = &mut *(ctx as *mut C);
928 csi_callback(*data);
929 }
930}
931
932#[derive(Clone, PartialEq, Eq)]
933#[cfg(all(not(esp32c6), feature = "csi"))]
936pub struct CsiConfig {
937 pub lltf_en: bool,
939 pub htltf_en: bool,
941 pub stbc_htltf2_en: bool,
944 pub ltf_merge_en: bool,
947 pub channel_filter_en: bool,
950 pub manu_scale: bool,
954 pub shift: u8,
957 pub dump_ack_en: bool,
959}
960
961#[derive(Clone, PartialEq, Eq)]
962#[cfg(all(esp32c6, feature = "csi"))]
963pub struct CsiConfig {
965 pub enable: u32,
967 pub acquire_csi_legacy: u32,
969 pub acquire_csi_ht20: u32,
971 pub acquire_csi_ht40: u32,
973 pub acquire_csi_su: u32,
975 pub acquire_csi_mu: u32,
977 pub acquire_csi_dcm: u32,
979 pub acquire_csi_beamformed: u32,
981 pub acquire_csi_he_stbc: u32,
985 pub val_scale_cfg: u32,
987 pub dump_ack_en: u32,
989 pub reserved: u32,
991}
992
993#[cfg(feature = "csi")]
994impl Default for CsiConfig {
995 #[cfg(not(esp32c6))]
996 fn default() -> Self {
997 Self {
998 lltf_en: true,
999 htltf_en: true,
1000 stbc_htltf2_en: true,
1001 ltf_merge_en: true,
1002 channel_filter_en: true,
1003 manu_scale: false,
1004 shift: 0,
1005 dump_ack_en: false,
1006 }
1007 }
1008
1009 #[cfg(esp32c6)]
1010 fn default() -> Self {
1011 Self {
1013 enable: 1,
1014 acquire_csi_legacy: 1,
1015 acquire_csi_ht20: 1,
1016 acquire_csi_ht40: 1,
1017 acquire_csi_su: 1,
1018 acquire_csi_mu: 1,
1019 acquire_csi_dcm: 1,
1020 acquire_csi_beamformed: 1,
1021 acquire_csi_he_stbc: 2,
1022 val_scale_cfg: 2,
1023 dump_ack_en: 1,
1024 reserved: 19,
1025 }
1026 }
1027}
1028
1029#[cfg(feature = "csi")]
1030impl From<CsiConfig> for wifi_csi_config_t {
1031 fn from(config: CsiConfig) -> Self {
1032 #[cfg(not(esp32c6))]
1033 {
1034 wifi_csi_config_t {
1035 lltf_en: config.lltf_en,
1036 htltf_en: config.htltf_en,
1037 stbc_htltf2_en: config.stbc_htltf2_en,
1038 ltf_merge_en: config.ltf_merge_en,
1039 channel_filter_en: config.channel_filter_en,
1040 manu_scale: config.manu_scale,
1041 shift: config.shift,
1042 dump_ack_en: config.dump_ack_en,
1043 }
1044 }
1045 #[cfg(esp32c6)]
1046 {
1047 wifi_csi_acquire_config_t {
1048 _bitfield_align_1: [0; 0],
1049 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
1050 config.enable,
1051 config.acquire_csi_legacy,
1052 config.acquire_csi_ht20,
1053 config.acquire_csi_ht40,
1054 config.acquire_csi_su,
1055 config.acquire_csi_mu,
1056 config.acquire_csi_dcm,
1057 config.acquire_csi_beamformed,
1058 config.acquire_csi_he_stbc,
1059 config.val_scale_cfg,
1060 config.dump_ack_en,
1061 config.reserved,
1062 ),
1063 }
1064 }
1065 }
1066}
1067
1068#[cfg(feature = "csi")]
1069impl CsiConfig {
1070 pub(crate) fn apply_config(&self) -> Result<(), WifiError> {
1072 let conf: wifi_csi_config_t = self.clone().into();
1073
1074 unsafe {
1075 esp_wifi_result!(esp_wifi_set_csi_config(&conf))?;
1076 }
1077 Ok(())
1078 }
1079
1080 pub(crate) fn set_receive_cb<C: CsiCallback>(&mut self, cb: C) -> Result<(), WifiError> {
1083 let cb = alloc::boxed::Box::new(cb);
1084 let cb_ptr = alloc::boxed::Box::into_raw(cb) as *mut crate::wifi::c_types::c_void;
1085
1086 unsafe {
1087 esp_wifi_result!(esp_wifi_set_csi_rx_cb(Some(csi_rx_cb::<C>), cb_ptr))?;
1088 }
1089 Ok(())
1090 }
1091
1092 pub(crate) fn set_csi(&self, enable: bool) -> Result<(), WifiError> {
1094 unsafe {
1096 esp_wifi_result!(esp_wifi_set_csi(enable))?;
1097 }
1098 Ok(())
1099 }
1100}
1101
1102const RX_QUEUE_SIZE: usize = crate::CONFIG.rx_queue_size;
1103const TX_QUEUE_SIZE: usize = crate::CONFIG.tx_queue_size;
1104
1105pub(crate) static DATA_QUEUE_RX_AP: Locked<VecDeque<EspWifiPacketBuffer>> =
1106 Locked::new(VecDeque::new());
1107
1108pub(crate) static DATA_QUEUE_RX_STA: Locked<VecDeque<EspWifiPacketBuffer>> =
1109 Locked::new(VecDeque::new());
1110
1111#[derive(Debug, Clone, Copy)]
1113#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1114#[non_exhaustive]
1115pub enum WifiError {
1116 NotInitialized,
1119
1120 InternalError(InternalWifiError),
1122
1123 Disconnected,
1125
1126 UnknownWifiMode,
1128
1129 Unsupported,
1131
1132 InvalidArguments,
1134}
1135
1136#[repr(i32)]
1138#[derive(Debug, FromPrimitive, EnumSetType)]
1139#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1140pub enum WifiEvent {
1141 WifiReady = 0,
1143 ScanDone,
1145 StaStart,
1147 StaStop,
1149 StaConnected,
1151 StaDisconnected,
1153 StaAuthmodeChange,
1155
1156 StaWpsErSuccess,
1158 StaWpsErFailed,
1160 StaWpsErTimeout,
1162 StaWpsErPin,
1164 StaWpsErPbcOverlap,
1166
1167 ApStart,
1169 ApStop,
1171 ApStaconnected,
1173 ApStadisconnected,
1175 ApProbereqrecved,
1177
1178 FtmReport,
1180
1181 StaBssRssiLow,
1183 ActionTxStatus,
1185 RocDone,
1187
1188 StaBeaconTimeout,
1190
1191 ConnectionlessModuleWakeIntervalStart,
1193
1194 ApWpsRgSuccess,
1196 ApWpsRgFailed,
1198 ApWpsRgTimeout,
1200 ApWpsRgPin,
1202 ApWpsRgPbcOverlap,
1204
1205 ItwtSetup,
1207 ItwtTeardown,
1209 ItwtProbe,
1211 ItwtSuspend,
1213 TwtWakeup,
1215 BtwtSetup,
1217 BtwtTeardown,
1219
1220 NanStarted,
1222 NanStopped,
1224 NanSvcMatch,
1226 NanReplied,
1228 NanReceive,
1230 NdpIndication,
1232 NdpConfirm,
1234 NdpTerminated,
1236 HomeChannelChange,
1238
1239 StaNeighborRep,
1241}
1242
1243#[repr(i32)]
1245#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
1246#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1247#[allow(clippy::enum_variant_names)] pub enum InternalWifiError {
1249 EspErrNoMem = 0x101,
1251
1252 EspErrInvalidArg = 0x102,
1254
1255 EspErrWifiNotInit = 0x3001,
1257
1258 EspErrWifiNotStarted = 0x3002,
1260
1261 EspErrWifiNotStopped = 0x3003,
1263
1264 EspErrWifiIf = 0x3004,
1266
1267 EspErrWifiMode = 0x3005,
1269
1270 EspErrWifiState = 0x3006,
1272
1273 EspErrWifiConn = 0x3007,
1275
1276 EspErrWifiNvs = 0x3008,
1278
1279 EspErrWifiMac = 0x3009,
1281
1282 EspErrWifiSsid = 0x300A,
1284
1285 EspErrWifiPassword = 0x300B,
1287
1288 EspErrWifiTimeout = 0x300C,
1290
1291 EspErrWifiWakeFail = 0x300D,
1293
1294 EspErrWifiWouldBlock = 0x300E,
1296
1297 EspErrWifiNotConnect = 0x300F,
1299
1300 EspErrWifiPost = 0x3012,
1302
1303 EspErrWifiInitState = 0x3013,
1305
1306 EspErrWifiStopState = 0x3014,
1308
1309 EspErrWifiNotAssoc = 0x3015,
1311
1312 EspErrWifiTxDisallow = 0x3016,
1314}
1315
1316pub fn sta_mac(mac: &mut [u8; 6]) {
1318 unsafe {
1319 read_mac(mac as *mut u8, 0);
1320 }
1321}
1322
1323pub fn ap_mac(mac: &mut [u8; 6]) {
1325 unsafe {
1326 read_mac(mac as *mut u8, 1);
1327 }
1328}
1329
1330pub(crate) fn wifi_init() -> Result<(), WifiError> {
1331 unsafe {
1332 internal::G_CONFIG.wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs;
1333 internal::G_CONFIG.feature_caps = internal::g_wifi_feature_caps;
1334
1335 #[cfg(coex)]
1336 esp_wifi_result!(coex_init())?;
1337
1338 esp_wifi_result!(esp_wifi_init_internal(addr_of!(internal::G_CONFIG)))?;
1339 esp_wifi_result!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
1340
1341 esp_wifi_result!(esp_supplicant_init())?;
1342
1343 esp_wifi_result!(esp_wifi_set_tx_done_cb(Some(esp_wifi_tx_done_cb)))?;
1344
1345 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1346 esp_interface_t_ESP_IF_WIFI_STA,
1347 Some(recv_cb_sta)
1348 ))?;
1349
1350 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1352 esp_interface_t_ESP_IF_WIFI_AP,
1353 Some(recv_cb_ap)
1354 ))?;
1355
1356 #[cfg(any(esp32, esp32s3))]
1357 {
1358 static mut NVS_STRUCT: [u32; 12] = [0; 12];
1359 chip_specific::g_misc_nvs = addr_of!(NVS_STRUCT) as u32;
1360 }
1361
1362 crate::flags::WIFI.store(true, Ordering::SeqCst);
1363
1364 Ok(())
1365 }
1366}
1367
1368#[cfg(coex)]
1369pub(crate) fn coex_initialize() -> i32 {
1370 debug!("call coex-initialize");
1371 unsafe {
1372 let res = crate::binary::include::esp_coex_adapter_register(
1373 core::ptr::addr_of_mut!(internal::G_COEX_ADAPTER_FUNCS).cast(),
1374 );
1375 if res != 0 {
1376 error!("Error: esp_coex_adapter_register {}", res);
1377 return res;
1378 }
1379 let res = crate::binary::include::coex_pre_init();
1380 if res != 0 {
1381 error!("Error: coex_pre_init {}", res);
1382 return res;
1383 }
1384 0
1385 }
1386}
1387
1388pub(crate) unsafe extern "C" fn coex_init() -> i32 {
1389 #[cfg(coex)]
1390 {
1391 debug!("coex-init");
1392 #[allow(clippy::needless_return)]
1393 return unsafe { crate::binary::include::coex_init() };
1394 }
1395
1396 #[cfg(not(coex))]
1397 0
1398}
1399
1400pub(crate) fn wifi_deinit() -> Result<(), crate::InitializationError> {
1401 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
1402 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1403 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1404 crate::flags::WIFI.store(false, Ordering::Release);
1405 Ok(())
1406}
1407
1408unsafe extern "C" fn recv_cb_sta(
1409 buffer: *mut c_types::c_void,
1410 len: u16,
1411 eb: *mut c_types::c_void,
1412) -> esp_err_t {
1413 let packet = EspWifiPacketBuffer { buffer, len, eb };
1414 match DATA_QUEUE_RX_STA.with(|queue| {
1421 if queue.len() < RX_QUEUE_SIZE {
1422 queue.push_back(packet);
1423 Ok(())
1424 } else {
1425 Err(packet)
1426 }
1427 }) {
1428 Ok(()) => {
1429 embassy::STA_RECEIVE_WAKER.wake();
1430 include::ESP_OK as esp_err_t
1431 }
1432 _ => {
1433 debug!("RX QUEUE FULL");
1434 include::ESP_ERR_NO_MEM as esp_err_t
1435 }
1436 }
1437}
1438
1439unsafe extern "C" fn recv_cb_ap(
1440 buffer: *mut c_types::c_void,
1441 len: u16,
1442 eb: *mut c_types::c_void,
1443) -> esp_err_t {
1444 let packet = EspWifiPacketBuffer { buffer, len, eb };
1445 match DATA_QUEUE_RX_AP.with(|queue| {
1452 if queue.len() < RX_QUEUE_SIZE {
1453 queue.push_back(packet);
1454 Ok(())
1455 } else {
1456 Err(packet)
1457 }
1458 }) {
1459 Ok(()) => {
1460 embassy::AP_RECEIVE_WAKER.wake();
1461 include::ESP_OK as esp_err_t
1462 }
1463 _ => {
1464 debug!("RX QUEUE FULL");
1465 include::ESP_ERR_NO_MEM as esp_err_t
1466 }
1467 }
1468}
1469
1470pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1471
1472fn decrement_inflight_counter() {
1473 unwrap!(
1474 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1475 Some(x.saturating_sub(1))
1476 })
1477 );
1478}
1479
1480#[ram]
1481unsafe extern "C" fn esp_wifi_tx_done_cb(
1482 _ifidx: u8,
1483 _data: *mut u8,
1484 _data_len: *mut u16,
1485 _tx_status: bool,
1486) {
1487 trace!("esp_wifi_tx_done_cb");
1488
1489 decrement_inflight_counter();
1490
1491 embassy::TRANSMIT_WAKER.wake();
1492}
1493
1494pub(crate) fn wifi_start() -> Result<(), WifiError> {
1495 unsafe {
1496 esp_wifi_result!(esp_wifi_start())?;
1497
1498 let mode = WifiMode::current()?;
1499
1500 if mode.is_ap() {
1502 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1503 wifi_interface_t_WIFI_IF_AP,
1504 crate::CONFIG.ap_beacon_timeout
1505 ))?;
1506 }
1507 if mode.is_sta() {
1508 esp_wifi_result!(include::esp_wifi_set_inactive_time(
1509 wifi_interface_t_WIFI_IF_STA,
1510 crate::CONFIG.beacon_timeout
1511 ))?;
1512 };
1513 }
1514
1515 Ok(())
1516}
1517
1518#[derive(Clone, Copy, PartialEq, Eq)]
1527pub enum ScanTypeConfig {
1528 Active {
1538 min: Duration,
1540 max: Duration,
1542 },
1543 Passive(Duration),
1554}
1555
1556impl Default for ScanTypeConfig {
1557 fn default() -> Self {
1558 Self::Active {
1559 min: Duration::from_millis(10),
1560 max: Duration::from_millis(20),
1561 }
1562 }
1563}
1564
1565impl ScanTypeConfig {
1566 fn validate(&self) {
1567 if matches!(self, Self::Passive(dur) if *dur > Duration::from_millis(1500)) {
1568 warn!(
1569 "Passive scan duration longer than 1500ms may cause a station to disconnect from the AP"
1570 );
1571 }
1572 }
1573}
1574
1575#[derive(Clone, Copy, Default, PartialEq, Eq)]
1577pub struct ScanConfig<'a> {
1578 pub ssid: Option<&'a str>,
1583 pub bssid: Option<[u8; 6]>,
1588 pub channel: Option<u8>,
1593 pub show_hidden: bool,
1595 pub scan_type: ScanTypeConfig,
1597}
1598
1599pub(crate) fn wifi_start_scan(
1600 block: bool,
1601 ScanConfig {
1602 ssid,
1603 mut bssid,
1604 channel,
1605 show_hidden,
1606 scan_type,
1607 }: ScanConfig<'_>,
1608) -> i32 {
1609 scan_type.validate();
1610 let (scan_time, scan_type) = match scan_type {
1611 ScanTypeConfig::Active { min, max } => (
1612 wifi_scan_time_t {
1613 active: wifi_active_scan_time_t {
1614 min: min.as_millis() as u32,
1615 max: max.as_millis() as u32,
1616 },
1617 passive: 0,
1618 },
1619 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1620 ),
1621 ScanTypeConfig::Passive(dur) => (
1622 wifi_scan_time_t {
1623 active: wifi_active_scan_time_t { min: 0, max: 0 },
1624 passive: dur.as_millis() as u32,
1625 },
1626 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1627 ),
1628 };
1629
1630 let mut ssid_buf = ssid.map(|m| {
1631 let mut buf = alloc::vec::Vec::from_iter(m.bytes());
1632 buf.push(b'\0');
1633 buf
1634 });
1635
1636 let ssid = ssid_buf
1637 .as_mut()
1638 .map(|e| e.as_mut_ptr())
1639 .unwrap_or_else(core::ptr::null_mut);
1640 let bssid = bssid
1641 .as_mut()
1642 .map(|e| e.as_mut_ptr())
1643 .unwrap_or_else(core::ptr::null_mut);
1644
1645 let scan_config = wifi_scan_config_t {
1646 ssid,
1647 bssid,
1648 channel: channel.unwrap_or(0),
1649 show_hidden,
1650 scan_type,
1651 scan_time,
1652 home_chan_dwell_time: 0,
1653 channel_bitmap: wifi_scan_channel_bitmap_t {
1654 ghz_2_channels: 0,
1655 ghz_5_channels: 0,
1656 },
1657 };
1658
1659 unsafe { esp_wifi_scan_start(&scan_config, block) }
1660}
1661
1662mod private {
1663 use super::*;
1664
1665 #[derive(Debug)]
1666 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1667 pub struct EspWifiPacketBuffer {
1675 pub(crate) buffer: *mut c_types::c_void,
1676 pub(crate) len: u16,
1677 pub(crate) eb: *mut c_types::c_void,
1678 }
1679
1680 unsafe impl Send for EspWifiPacketBuffer {}
1681
1682 impl Drop for EspWifiPacketBuffer {
1683 fn drop(&mut self) {
1684 trace!("Dropping EspWifiPacketBuffer, freeing memory");
1685 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1686 }
1687 }
1688
1689 impl EspWifiPacketBuffer {
1690 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1691 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1692 }
1693 }
1694}
1695
1696#[derive(Debug, Clone, Copy)]
1698pub enum WifiDeviceMode {
1699 Sta,
1700 Ap,
1701}
1702
1703impl WifiDeviceMode {
1704 fn mac_address(&self) -> [u8; 6] {
1705 match self {
1706 WifiDeviceMode::Sta => {
1707 let mut mac = [0; 6];
1708 sta_mac(&mut mac);
1709 mac
1710 }
1711 WifiDeviceMode::Ap => {
1712 let mut mac = [0; 6];
1713 ap_mac(&mut mac);
1714 mac
1715 }
1716 }
1717 }
1718
1719 fn data_queue_rx(&self) -> &'static Locked<VecDeque<EspWifiPacketBuffer>> {
1720 match self {
1721 WifiDeviceMode::Sta => &DATA_QUEUE_RX_STA,
1722 WifiDeviceMode::Ap => &DATA_QUEUE_RX_AP,
1723 }
1724 }
1725
1726 fn can_send(&self) -> bool {
1727 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE
1728 }
1729
1730 fn increase_in_flight_counter(&self) {
1731 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1732 }
1733
1734 fn tx_token(&self) -> Option<WifiTxToken> {
1735 if !self.can_send() {
1736 crate::preempt::yield_task();
1737 }
1738
1739 if self.can_send() {
1740 Some(WifiTxToken { mode: *self })
1741 } else {
1742 None
1743 }
1744 }
1745
1746 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1747 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1748 if is_empty || !self.can_send() {
1749 crate::preempt::yield_task();
1750 }
1751
1752 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1753
1754 if !is_empty {
1755 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1756 } else {
1757 None
1758 }
1759 }
1760
1761 fn interface(&self) -> wifi_interface_t {
1762 match self {
1763 WifiDeviceMode::Sta => wifi_interface_t_WIFI_IF_STA,
1764 WifiDeviceMode::Ap => wifi_interface_t_WIFI_IF_AP,
1765 }
1766 }
1767
1768 fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) {
1769 embassy::TRANSMIT_WAKER.register(cx.waker())
1770 }
1771
1772 fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) {
1773 match self {
1774 WifiDeviceMode::Sta => embassy::STA_RECEIVE_WAKER.register(cx.waker()),
1775 WifiDeviceMode::Ap => embassy::AP_RECEIVE_WAKER.register(cx.waker()),
1776 }
1777 }
1778
1779 fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) {
1780 match self {
1781 WifiDeviceMode::Sta => embassy::STA_LINK_STATE_WAKER.register(cx.waker()),
1782 WifiDeviceMode::Ap => embassy::AP_LINK_STATE_WAKER.register(cx.waker()),
1783 }
1784 }
1785
1786 fn link_state(&self) -> embassy_net_driver::LinkState {
1787 match self {
1788 WifiDeviceMode::Sta => {
1789 if matches!(sta_state(), WifiState::StaConnected) {
1790 embassy_net_driver::LinkState::Up
1791 } else {
1792 embassy_net_driver::LinkState::Down
1793 }
1794 }
1795 WifiDeviceMode::Ap => {
1796 if matches!(ap_state(), WifiState::ApStarted) {
1797 embassy_net_driver::LinkState::Up
1798 } else {
1799 embassy_net_driver::LinkState::Down
1800 }
1801 }
1802 }
1803 }
1804}
1805
1806pub struct WifiDevice<'d> {
1808 _phantom: PhantomData<&'d ()>,
1809 mode: WifiDeviceMode,
1810}
1811
1812impl WifiDevice<'_> {
1813 pub fn mac_address(&self) -> [u8; 6] {
1815 self.mode.mac_address()
1816 }
1817
1818 #[cfg(not(feature = "smoltcp"))]
1821 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1822 self.mode.rx_token()
1823 }
1824
1825 #[cfg(not(feature = "smoltcp"))]
1828 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1829 self.mode.tx_token()
1830 }
1831}
1832
1833fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
1834 let str_len = record
1835 .ssid
1836 .iter()
1837 .position(|&c| c == 0)
1838 .unwrap_or(record.ssid.len());
1839 let ssid_ref = unsafe { core::str::from_utf8_unchecked(&record.ssid[..str_len]) };
1840
1841 let mut ssid = String::new();
1842 ssid.push_str(ssid_ref);
1843
1844 AccessPointInfo {
1845 ssid,
1846 bssid: record.bssid,
1847 channel: record.primary,
1848 secondary_channel: match record.second {
1849 include::wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
1850 include::wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
1851 include::wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
1852 _ => panic!(),
1853 },
1854 signal_strength: record.rssi,
1855 auth_method: Some(AuthMethod::from_raw(record.authmode)),
1856 }
1857}
1858
1859#[cfg(not(any(esp32c6)))]
1862#[derive(Debug, Clone, Copy)]
1863#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1864pub struct RxControlInfo {
1865 pub rssi: i32,
1867 pub rate: u32,
1870 pub sig_mode: u32,
1873 pub mcs: u32,
1876 pub cwb: u32,
1878 pub smoothing: u32,
1881 pub not_sounding: u32,
1884 pub aggregation: u32,
1886 pub stbc: u32,
1889 pub fec_coding: u32,
1892 pub sgi: u32,
1895 pub ampdu_cnt: u32,
1897 pub channel: u32,
1899 pub secondary_channel: u32,
1902 pub timestamp: u32,
1905 pub noise_floor: i32,
1907 pub ant: u32,
1910 pub sig_len: u32,
1912 pub rx_state: u32,
1914}
1915
1916#[cfg(esp32c6)]
1919#[derive(Debug, Clone, Copy)]
1920#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1921pub struct RxControlInfo {
1922 pub rssi: i32,
1924 pub rate: u32,
1927 pub sig_len: u32,
1929 pub rx_state: u32,
1932 pub dump_len: u32,
1934 pub he_sigb_len: u32,
1936 pub cur_single_mpdu: u32,
1938 pub cur_bb_format: u32,
1940 pub rx_channel_estimate_info_vld: u32,
1942 pub rx_channel_estimate_len: u32,
1944 pub second: u32,
1946 pub channel: u32,
1948 pub noise_floor: i32,
1950 pub is_group: u32,
1952 pub rxend_state: u32,
1954 pub rxmatch3: u32,
1956 pub rxmatch2: u32,
1958 pub rxmatch1: u32,
1960 pub rxmatch0: u32,
1962}
1963impl RxControlInfo {
1964 pub unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
1970 #[cfg(not(esp32c6))]
1971 let rx_control_info = unsafe {
1972 RxControlInfo {
1973 rssi: (*rx_cntl).rssi(),
1974 rate: (*rx_cntl).rate(),
1975 sig_mode: (*rx_cntl).sig_mode(),
1976 mcs: (*rx_cntl).mcs(),
1977 cwb: (*rx_cntl).cwb(),
1978 smoothing: (*rx_cntl).smoothing(),
1979 not_sounding: (*rx_cntl).not_sounding(),
1980 aggregation: (*rx_cntl).aggregation(),
1981 stbc: (*rx_cntl).stbc(),
1982 fec_coding: (*rx_cntl).fec_coding(),
1983 sgi: (*rx_cntl).sgi(),
1984 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
1985 channel: (*rx_cntl).channel(),
1986 secondary_channel: (*rx_cntl).secondary_channel(),
1987 timestamp: (*rx_cntl).timestamp(),
1988 noise_floor: (*rx_cntl).noise_floor(),
1989 ant: (*rx_cntl).ant(),
1990 sig_len: (*rx_cntl).sig_len(),
1991 rx_state: (*rx_cntl).rx_state(),
1992 }
1993 };
1994 #[cfg(esp32c6)]
1995 let rx_control_info = unsafe {
1996 RxControlInfo {
1997 rssi: (*rx_cntl).rssi(),
1998 rate: (*rx_cntl).rate(),
1999 sig_len: (*rx_cntl).sig_len(),
2000 rx_state: (*rx_cntl).rx_state(),
2001 dump_len: (*rx_cntl).dump_len(),
2002 he_sigb_len: (*rx_cntl).he_sigb_len(),
2003 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
2004 cur_bb_format: (*rx_cntl).cur_bb_format(),
2005 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
2006 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
2007 second: (*rx_cntl).second(),
2008 channel: (*rx_cntl).channel(),
2009 noise_floor: (*rx_cntl).noise_floor(),
2010 is_group: (*rx_cntl).is_group(),
2011 rxend_state: (*rx_cntl).rxend_state(),
2012 rxmatch3: (*rx_cntl).rxmatch3(),
2013 rxmatch2: (*rx_cntl).rxmatch2(),
2014 rxmatch1: (*rx_cntl).rxmatch1(),
2015 rxmatch0: (*rx_cntl).rxmatch0(),
2016 }
2017 };
2018 rx_control_info
2019 }
2020}
2021#[cfg(feature = "sniffer")]
2023pub struct PromiscuousPkt<'a> {
2024 pub rx_cntl: RxControlInfo,
2026 pub frame_type: wifi_promiscuous_pkt_type_t,
2028 pub len: usize,
2030 pub data: &'a [u8],
2032}
2033#[cfg(feature = "sniffer")]
2034impl PromiscuousPkt<'_> {
2035 pub(crate) unsafe fn from_raw(
2039 buf: *const wifi_promiscuous_pkt_t,
2040 frame_type: wifi_promiscuous_pkt_type_t,
2041 ) -> Self {
2042 let rx_cntl = unsafe { RxControlInfo::from_raw(&(*buf).rx_ctrl) };
2043 let len = rx_cntl.sig_len as usize;
2044 PromiscuousPkt {
2045 rx_cntl,
2046 frame_type,
2047 len,
2048 data: unsafe {
2049 core::slice::from_raw_parts(
2050 (buf as *const u8).add(core::mem::size_of::<wifi_pkt_rx_ctrl_t>()),
2051 len,
2052 )
2053 },
2054 }
2055 }
2056}
2057
2058#[cfg(feature = "sniffer")]
2059static SNIFFER_CB: Locked<Option<fn(PromiscuousPkt<'_>)>> = Locked::new(None);
2060
2061#[cfg(feature = "sniffer")]
2062unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) {
2063 unsafe {
2064 if let Some(sniffer_callback) = SNIFFER_CB.with(|callback| *callback) {
2065 let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type);
2066 sniffer_callback(promiscuous_pkt);
2067 }
2068 }
2069}
2070
2071#[cfg(feature = "sniffer")]
2072#[non_exhaustive]
2074pub struct Sniffer {}
2075
2076#[cfg(feature = "sniffer")]
2077impl Sniffer {
2078 pub(crate) fn new() -> Self {
2079 unwrap!(esp_wifi_result!(unsafe {
2082 esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb))
2083 }));
2084 Self {}
2085 }
2086 pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
2088 esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
2089 Ok(())
2090 }
2091 pub fn send_raw_frame(
2093 &mut self,
2094 use_sta_interface: bool,
2095 buffer: &[u8],
2096 use_internal_seq_num: bool,
2097 ) -> Result<(), WifiError> {
2098 esp_wifi_result!(unsafe {
2099 esp_wifi_80211_tx(
2100 if use_sta_interface {
2101 wifi_interface_t_WIFI_IF_STA
2102 } else {
2103 wifi_interface_t_WIFI_IF_AP
2104 } as wifi_interface_t,
2105 buffer.as_ptr() as *const _,
2106 buffer.len() as i32,
2107 use_internal_seq_num,
2108 )
2109 })
2110 }
2111 pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt<'_>)) {
2113 SNIFFER_CB.with(|callback| *callback = Some(cb));
2114 }
2115}
2116
2117#[cfg(feature = "smoltcp")]
2119impl Device for WifiDevice<'_> {
2120 type RxToken<'a>
2121 = WifiRxToken
2122 where
2123 Self: 'a;
2124 type TxToken<'a>
2125 = WifiTxToken
2126 where
2127 Self: 'a;
2128
2129 fn receive(
2130 &mut self,
2131 _instant: smoltcp::time::Instant,
2132 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2133 self.mode.rx_token()
2134 }
2135
2136 fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
2137 self.mode.tx_token()
2138 }
2139
2140 fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
2141 let mut caps = DeviceCapabilities::default();
2142 caps.max_transmission_unit = MTU;
2143 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2144 None
2145 } else {
2146 Some(crate::CONFIG.max_burst_size)
2147 };
2148 caps
2149 }
2150}
2151
2152#[doc(hidden)]
2153#[derive(Debug)]
2154pub struct WifiRxToken {
2155 mode: WifiDeviceMode,
2156}
2157
2158impl WifiRxToken {
2159 pub fn consume_token<R, F>(self, f: F) -> R
2162 where
2163 F: FnOnce(&mut [u8]) -> R,
2164 {
2165 let mut data = self.mode.data_queue_rx().with(|queue| {
2166 unwrap!(
2167 queue.pop_front(),
2168 "unreachable: transmit()/receive() ensures there is a packet to process"
2169 )
2170 });
2171
2172 let buffer = data.as_slice_mut();
2179 dump_packet_info(buffer);
2180
2181 f(buffer)
2182 }
2183}
2184
2185#[cfg(feature = "smoltcp")]
2186impl RxToken for WifiRxToken {
2187 fn consume<R, F>(self, f: F) -> R
2188 where
2189 F: FnOnce(&[u8]) -> R,
2190 {
2191 self.consume_token(|t| f(t))
2192 }
2193}
2194
2195#[doc(hidden)]
2196#[derive(Debug)]
2197pub struct WifiTxToken {
2198 mode: WifiDeviceMode,
2199}
2200
2201impl WifiTxToken {
2202 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
2205 where
2206 F: FnOnce(&mut [u8]) -> R,
2207 {
2208 self.mode.increase_in_flight_counter();
2209
2210 static mut BUFFER: [u8; MTU] = [0u8; MTU];
2214
2215 let buffer = unsafe { &mut BUFFER[..len] };
2216
2217 let res = f(buffer);
2218
2219 esp_wifi_send_data(self.mode.interface(), buffer);
2220
2221 res
2222 }
2223}
2224
2225#[cfg(feature = "smoltcp")]
2226impl TxToken for WifiTxToken {
2227 fn consume<R, F>(self, len: usize, f: F) -> R
2228 where
2229 F: FnOnce(&mut [u8]) -> R,
2230 {
2231 self.consume_token(len, f)
2232 }
2233}
2234
2235pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
2240 trace!("sending... {} bytes", data.len());
2241 dump_packet_info(data);
2242
2243 let len = data.len() as u16;
2244 let ptr = data.as_mut_ptr().cast();
2245
2246 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
2247
2248 if res != 0 {
2249 warn!("esp_wifi_internal_tx {}", res);
2250 decrement_inflight_counter();
2251 } else {
2252 trace!("esp_wifi_internal_tx ok");
2253 }
2254}
2255
2256fn apply_ap_config(config: &AccessPointConfiguration) -> Result<(), WifiError> {
2257 let mut cfg = wifi_config_t {
2258 ap: wifi_ap_config_t {
2259 ssid: [0; 32],
2260 password: [0; 64],
2261 ssid_len: 0,
2262 channel: config.channel,
2263 authmode: config.auth_method.to_raw(),
2264 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
2265 max_connection: config.max_connections as u8,
2266 beacon_interval: 100,
2267 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
2268 ftm_responder: false,
2269 pmf_cfg: wifi_pmf_config_t {
2270 capable: true,
2271 required: false,
2272 },
2273 sae_pwe_h2e: 0,
2274 csa_count: 3,
2275 dtim_period: 2,
2276 },
2277 };
2278
2279 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2280 return Err(WifiError::InternalError(
2281 InternalWifiError::EspErrInvalidArg,
2282 ));
2283 }
2284
2285 unsafe {
2286 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2287 cfg.ap.ssid_len = config.ssid.len() as u8;
2288 cfg.ap.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2289
2290 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
2291 }
2292}
2293
2294fn apply_sta_config(config: &ClientConfiguration) -> Result<(), WifiError> {
2295 let mut cfg = wifi_config_t {
2296 sta: wifi_sta_config_t {
2297 ssid: [0; 32],
2298 password: [0; 64],
2299 scan_method: crate::CONFIG.scan_method,
2300 bssid_set: config.bssid.is_some(),
2301 bssid: config.bssid.unwrap_or_default(),
2302 channel: config.channel.unwrap_or(0),
2303 listen_interval: crate::CONFIG.listen_interval,
2304 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2305 threshold: wifi_scan_threshold_t {
2306 rssi: -99,
2307 authmode: config.auth_method.to_raw(),
2308 },
2309 pmf_cfg: wifi_pmf_config_t {
2310 capable: true,
2311 required: false,
2312 },
2313 sae_pwe_h2e: 3,
2314 _bitfield_align_1: [0; 0],
2315 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2316 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2317 _bitfield_align_2: [0; 0],
2318 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2319 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2321 },
2322 };
2323
2324 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
2325 return Err(WifiError::InternalError(
2326 InternalWifiError::EspErrInvalidArg,
2327 ));
2328 }
2329
2330 unsafe {
2331 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2332 cfg.sta.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
2333
2334 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
2335 }
2336}
2337
2338fn apply_sta_eap_config(config: &EapClientConfiguration) -> Result<(), WifiError> {
2339 let mut cfg = wifi_config_t {
2340 sta: wifi_sta_config_t {
2341 ssid: [0; 32],
2342 password: [0; 64],
2343 scan_method: crate::CONFIG.scan_method,
2344 bssid_set: config.bssid.is_some(),
2345 bssid: config.bssid.unwrap_or_default(),
2346 channel: config.channel.unwrap_or(0),
2347 listen_interval: crate::CONFIG.listen_interval,
2348 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
2349 threshold: wifi_scan_threshold_t {
2350 rssi: -99,
2351 authmode: config.auth_method.to_raw(),
2352 },
2353 pmf_cfg: wifi_pmf_config_t {
2354 capable: true,
2355 required: false,
2356 },
2357 sae_pwe_h2e: 3,
2358 _bitfield_align_1: [0; 0],
2359 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
2360 failure_retry_cnt: crate::CONFIG.failure_retry_cnt,
2361 _bitfield_align_2: [0; 0],
2362 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
2363 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
2365 },
2366 };
2367
2368 unsafe {
2369 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
2370 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
2371
2372 if let Some(identity) = &config.identity {
2373 esp_wifi_result!(esp_eap_client_set_identity(
2374 identity.as_str().as_ptr(),
2375 identity.len() as i32
2376 ))?;
2377 } else {
2378 esp_eap_client_clear_identity();
2379 }
2380
2381 if let Some(username) = &config.username {
2382 esp_wifi_result!(esp_eap_client_set_username(
2383 username.as_str().as_ptr(),
2384 username.len() as i32
2385 ))?;
2386 } else {
2387 esp_eap_client_clear_username();
2388 }
2389
2390 if let Some(password) = &config.password {
2391 esp_wifi_result!(esp_eap_client_set_password(
2392 password.as_str().as_ptr(),
2393 password.len() as i32
2394 ))?;
2395 } else {
2396 esp_eap_client_clear_password();
2397 }
2398
2399 if let Some(new_password) = &config.new_password {
2400 esp_wifi_result!(esp_eap_client_set_new_password(
2401 new_password.as_str().as_ptr(),
2402 new_password.len() as i32
2403 ))?;
2404 } else {
2405 esp_eap_client_clear_new_password();
2406 }
2407
2408 if let Some(pac_file) = &config.pac_file {
2409 esp_wifi_result!(esp_eap_client_set_pac_file(
2410 pac_file.as_ptr(),
2411 pac_file.len() as i32
2412 ))?;
2413 }
2414
2415 if let Some(phase2_method) = &config.ttls_phase2_method {
2416 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
2417 phase2_method.to_raw()
2418 ))?;
2419 }
2420
2421 if let Some(ca_cert) = config.ca_cert {
2422 esp_wifi_result!(esp_eap_client_set_ca_cert(
2423 ca_cert.as_ptr(),
2424 ca_cert.len() as i32
2425 ))?;
2426 } else {
2427 esp_eap_client_clear_ca_cert();
2428 }
2429
2430 if let Some((cert, key, password)) = config.certificate_and_key {
2431 let (pwd, pwd_len) = if let Some(pwd) = password {
2432 (pwd.as_ptr(), pwd.len() as i32)
2433 } else {
2434 (core::ptr::null(), 0)
2435 };
2436
2437 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
2438 cert.as_ptr(),
2439 cert.len() as i32,
2440 key.as_ptr(),
2441 key.len() as i32,
2442 pwd,
2443 pwd_len,
2444 ))?;
2445 } else {
2446 esp_eap_client_clear_certificate_and_key();
2447 }
2448
2449 if let Some(cfg) = &config.eap_fast_config {
2450 let params = esp_eap_fast_config {
2451 fast_provisioning: cfg.fast_provisioning as i32,
2452 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
2453 fast_pac_format_binary: cfg.fast_pac_format_binary,
2454 };
2455 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
2456 }
2457
2458 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
2459
2460 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
2467
2468 Ok(())
2469 }
2470}
2471
2472fn dump_packet_info(_buffer: &mut [u8]) {
2473 #[cfg(dump_packets)]
2474 {
2475 info!("@WIFIFRAME {:?}", _buffer);
2476 }
2477}
2478
2479#[doc(hidden)]
2480#[macro_export]
2481macro_rules! esp_wifi_result {
2482 ($value:expr) => {{
2483 use num_traits::FromPrimitive;
2484 let result = $value;
2485 if result != esp_wifi_sys::include::ESP_OK as i32 {
2486 warn!("{} returned an error: {}", stringify!($value), result);
2487 Err(WifiError::InternalError(unwrap!(FromPrimitive::from_i32(
2488 result
2489 ))))
2490 } else {
2491 Ok::<(), WifiError>(())
2492 }
2493 }};
2494}
2495
2496pub(crate) mod embassy {
2497 use embassy_net_driver::{Capabilities, Driver, HardwareAddress, RxToken, TxToken};
2498 use esp_hal::asynch::AtomicWaker;
2499
2500 use super::*;
2501
2502 pub(crate) static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
2505
2506 pub(crate) static AP_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2507 pub(crate) static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2508
2509 pub(crate) static STA_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2510 pub(crate) static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2511
2512 impl RxToken for WifiRxToken {
2513 fn consume<R, F>(self, f: F) -> R
2514 where
2515 F: FnOnce(&mut [u8]) -> R,
2516 {
2517 self.consume_token(f)
2518 }
2519 }
2520
2521 impl TxToken for WifiTxToken {
2522 fn consume<R, F>(self, len: usize, f: F) -> R
2523 where
2524 F: FnOnce(&mut [u8]) -> R,
2525 {
2526 self.consume_token(len, f)
2527 }
2528 }
2529
2530 impl Driver for WifiDevice<'_> {
2531 type RxToken<'a>
2532 = WifiRxToken
2533 where
2534 Self: 'a;
2535 type TxToken<'a>
2536 = WifiTxToken
2537 where
2538 Self: 'a;
2539
2540 fn receive(
2541 &mut self,
2542 cx: &mut core::task::Context<'_>,
2543 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2544 self.mode.register_receive_waker(cx);
2545 self.mode.register_transmit_waker(cx);
2546 self.mode.rx_token()
2547 }
2548
2549 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
2550 self.mode.register_transmit_waker(cx);
2551 self.mode.tx_token()
2552 }
2553
2554 fn link_state(
2555 &mut self,
2556 cx: &mut core::task::Context<'_>,
2557 ) -> embassy_net_driver::LinkState {
2558 self.mode.register_link_state_waker(cx);
2559 self.mode.link_state()
2560 }
2561
2562 fn capabilities(&self) -> Capabilities {
2563 let mut caps = Capabilities::default();
2564 caps.max_transmission_unit = MTU;
2565 caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 {
2566 None
2567 } else {
2568 Some(crate::CONFIG.max_burst_size)
2569 };
2570 caps
2571 }
2572
2573 fn hardware_address(&self) -> HardwareAddress {
2574 HardwareAddress::Ethernet(self.mac_address())
2575 }
2576 }
2577}
2578
2579pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
2580 esp_wifi_result!(unsafe { esp_wifi_sys::include::esp_wifi_set_ps(ps.into()) })?;
2581 Ok(())
2582}
2583
2584struct FreeApListOnDrop;
2585impl FreeApListOnDrop {
2586 pub fn defuse(self) {
2587 core::mem::forget(self);
2588 }
2589}
2590
2591impl Drop for FreeApListOnDrop {
2592 fn drop(&mut self) {
2593 unsafe {
2594 include::esp_wifi_clear_ap_list();
2595 }
2596 }
2597}
2598
2599#[non_exhaustive]
2600pub struct Interfaces<'d> {
2601 pub sta: WifiDevice<'d>,
2602 pub ap: WifiDevice<'d>,
2603 #[cfg(feature = "esp-now")]
2604 pub esp_now: crate::esp_now::EspNow<'d>,
2605 #[cfg(feature = "sniffer")]
2606 pub sniffer: Sniffer,
2607}
2608
2609pub fn new<'d>(
2615 inited: &'d EspWifiController<'d>,
2616 _device: crate::hal::peripherals::WIFI<'d>,
2617) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> {
2618 if crate::is_interrupts_disabled() {
2619 return Err(WifiError::Unsupported);
2620 }
2621
2622 let mut controller = WifiController {
2623 _phantom: Default::default(),
2624 };
2625
2626 if !inited.wifi() {
2627 crate::wifi::wifi_init()?;
2628
2629 let mut cntry_code = [0u8; 3];
2630 cntry_code[..crate::CONFIG.country_code.len()]
2631 .copy_from_slice(crate::CONFIG.country_code.as_bytes());
2632 cntry_code[2] = crate::CONFIG.country_code_operating_class;
2633
2634 #[allow(clippy::useless_transmute)]
2635 unsafe {
2636 let country = wifi_country_t {
2637 #[allow(clippy::useless_transmute)]
2639 cc: core::mem::transmute::<[u8; 3], [core::ffi::c_char; 3]>(cntry_code),
2640 schan: 1,
2641 nchan: 13,
2642 max_tx_power: 20,
2643 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
2644 };
2645 esp_wifi_result!(esp_wifi_set_country(&country))?;
2646 }
2647
2648 controller.set_power_saving(PowerSaveMode::default())?;
2649 }
2650
2651 Ok((
2652 controller,
2653 Interfaces {
2654 sta: WifiDevice {
2655 _phantom: Default::default(),
2656 mode: WifiDeviceMode::Sta,
2657 },
2658 ap: WifiDevice {
2659 _phantom: Default::default(),
2660 mode: WifiDeviceMode::Ap,
2661 },
2662 #[cfg(feature = "esp-now")]
2663 esp_now: crate::esp_now::EspNow::new_internal(),
2664 #[cfg(feature = "sniffer")]
2665 sniffer: Sniffer::new(),
2666 },
2667 ))
2668}
2669
2670#[non_exhaustive]
2671pub struct WifiController<'d> {
2672 _phantom: PhantomData<&'d ()>,
2673}
2674
2675impl Drop for WifiController<'_> {
2676 fn drop(&mut self) {
2677 if let Err(e) = crate::wifi::wifi_deinit() {
2678 warn!("Failed to cleanly deinit wifi: {:?}", e);
2679 }
2680 }
2681}
2682
2683impl WifiController<'_> {
2684 #[cfg(feature = "csi")]
2686 pub fn set_csi(
2687 &mut self,
2688 mut csi: CsiConfig,
2689 cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send,
2690 ) -> Result<(), WifiError> {
2691 csi.apply_config()?;
2692 csi.set_receive_cb(cb)?;
2693 csi.set_csi(true)?;
2694
2695 Ok(())
2696 }
2697
2698 pub fn set_protocol(&mut self, protocols: EnumSet<Protocol>) -> Result<(), WifiError> {
2713 let protocol = protocols
2714 .into_iter()
2715 .map(|v| match v {
2716 Protocol::P802D11B => WIFI_PROTOCOL_11B,
2717 Protocol::P802D11BG => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G,
2718 Protocol::P802D11BGN => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
2719 Protocol::P802D11BGNLR => {
2720 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR
2721 }
2722 Protocol::P802D11LR => WIFI_PROTOCOL_LR,
2723 Protocol::P802D11BGNAX => {
2724 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX
2725 }
2726 })
2727 .fold(0, |combined, protocol| combined | protocol) as u8;
2728
2729 let mode = self.mode()?;
2730 if mode.is_sta() {
2731 esp_wifi_result!(unsafe {
2732 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_STA, protocol)
2733 })?;
2734 }
2735 if mode.is_ap() {
2736 esp_wifi_result!(unsafe {
2737 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_AP, protocol)
2738 })?;
2739 }
2740
2741 Ok(())
2742 }
2743
2744 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
2746 apply_power_saving(ps)
2747 }
2748
2749 pub fn scan_with_config_sync(
2751 &mut self,
2752 config: ScanConfig<'_>,
2753 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2754 self.scan_with_config_sync_max(config, usize::MAX)
2755 }
2756
2757 pub fn scan_with_config_sync_max(
2758 &mut self,
2759 config: ScanConfig<'_>,
2760 max: usize,
2761 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2762 esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?;
2763 let result = self.scan_results(max)?;
2764 Ok(result)
2765 }
2766
2767 fn scan_results(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2768 let mut scanned = alloc::vec::Vec::<AccessPointInfo>::new();
2769 let mut bss_total: u16 = max as u16;
2770
2771 let guard = FreeApListOnDrop;
2773
2774 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
2775
2776 guard.defuse();
2777
2778 let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
2779 for _ in 0..usize::min(bss_total as usize, max) {
2780 let record = unsafe { MaybeUninit::assume_init_mut(&mut record) };
2781 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_record(record))? };
2782 let ap_info = convert_ap_info(record);
2783 scanned.push(ap_info);
2784 }
2785
2786 unsafe { esp_wifi_result!(include::esp_wifi_clear_ap_list())? };
2787
2788 Ok(scanned)
2789 }
2790
2791 pub fn scan_n(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2793 self.scan_with_config_sync_max(Default::default(), max)
2794 }
2795
2796 pub fn start(&mut self) -> Result<(), WifiError> {
2798 crate::wifi::wifi_start()
2799 }
2800
2801 pub fn stop(&mut self) -> Result<(), WifiError> {
2803 self.stop_impl()
2804 }
2805
2806 pub fn connect(&mut self) -> Result<(), WifiError> {
2814 self.connect_impl()
2815 }
2816
2817 pub fn disconnect(&mut self) -> Result<(), WifiError> {
2819 self.disconnect_impl()
2820 }
2821
2822 pub fn capabilities(&self) -> Result<EnumSet<crate::wifi::Capability>, WifiError> {
2824 let caps =
2825 enumset::enum_set! { Capability::Client | Capability::AccessPoint | Capability::Mixed };
2826 Ok(caps)
2827 }
2828
2829 pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), WifiError> {
2839 conf.validate()?;
2840
2841 let mode = match conf {
2842 Configuration::None => wifi_mode_t_WIFI_MODE_NULL,
2843 Configuration::Client(_) => wifi_mode_t_WIFI_MODE_STA,
2844 Configuration::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
2845 Configuration::Mixed(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
2846 Configuration::EapClient(_) => wifi_mode_t_WIFI_MODE_STA,
2847 };
2848
2849 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
2850
2851 match conf {
2852 Configuration::None => Ok::<(), WifiError>(()),
2853 Configuration::Client(config) => apply_sta_config(config),
2854 Configuration::AccessPoint(config) => apply_ap_config(config),
2855 Configuration::Mixed(sta_config, ap_config) => {
2856 apply_ap_config(ap_config).and_then(|()| apply_sta_config(sta_config))
2857 }
2858 Configuration::EapClient(config) => apply_sta_eap_config(config),
2859 }
2860 .inspect_err(|_| {
2861 unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL) };
2865 })?;
2866
2867 Ok(())
2868 }
2869
2870 pub fn set_mode(&mut self, mode: WifiMode) -> Result<(), WifiError> {
2874 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?;
2875 Ok(())
2876 }
2877
2878 fn stop_impl(&mut self) -> Result<(), WifiError> {
2879 esp_wifi_result!(unsafe { esp_wifi_stop() })
2880 }
2881
2882 fn connect_impl(&mut self) -> Result<(), WifiError> {
2883 esp_wifi_result!(unsafe { esp_wifi_connect() })
2884 }
2885
2886 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
2887 esp_wifi_result!(unsafe { esp_wifi_disconnect() })
2888 }
2889
2890 pub fn is_started(&self) -> Result<bool, WifiError> {
2895 if matches!(
2896 crate::wifi::sta_state(),
2897 WifiState::StaStarted | WifiState::StaConnected | WifiState::StaDisconnected
2898 ) {
2899 return Ok(true);
2900 }
2901 if matches!(crate::wifi::ap_state(), WifiState::ApStarted) {
2902 return Ok(true);
2903 }
2904 Ok(false)
2905 }
2906
2907 pub fn is_connected(&self) -> Result<bool, WifiError> {
2912 match crate::wifi::sta_state() {
2913 crate::wifi::WifiState::StaConnected => Ok(true),
2914 crate::wifi::WifiState::StaDisconnected => Err(WifiError::Disconnected),
2915 _ => Ok(false),
2917 }
2918 }
2919
2920 fn mode(&self) -> Result<WifiMode, WifiError> {
2921 WifiMode::current()
2922 }
2923
2924 pub async fn scan_n_async(
2926 &mut self,
2927 max: usize,
2928 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2929 self.scan_with_config_async_max(Default::default(), max)
2930 .await
2931 }
2932
2933 pub async fn scan_with_config_async(
2935 &mut self,
2936 config: ScanConfig<'_>,
2937 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2938 self.scan_with_config_async_max(config, usize::MAX).await
2939 }
2940
2941 async fn scan_with_config_async_max(
2942 &mut self,
2943 config: ScanConfig<'_>,
2944 max: usize,
2945 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
2946 Self::clear_events(WifiEvent::ScanDone);
2947 esp_wifi_result!(wifi_start_scan(false, config))?;
2948
2949 let guard = FreeApListOnDrop;
2951 WifiEventFuture::new(WifiEvent::ScanDone).await;
2952
2953 guard.defuse();
2954
2955 let result = self.scan_results(max)?;
2956
2957 Ok(result)
2958 }
2959
2960 pub async fn start_async(&mut self) -> Result<(), WifiError> {
2962 let mut events = enumset::enum_set! {};
2963
2964 let mode = self.mode()?;
2965 if mode.is_ap() {
2966 events |= WifiEvent::ApStart;
2967 }
2968 if mode.is_sta() {
2969 events |= WifiEvent::StaStart;
2970 }
2971
2972 Self::clear_events(events);
2973
2974 wifi_start()?;
2975
2976 self.wait_for_all_events(events, false).await;
2977
2978 Ok(())
2979 }
2980
2981 pub async fn stop_async(&mut self) -> Result<(), WifiError> {
2983 let mut events = enumset::enum_set! {};
2984
2985 let mode = self.mode()?;
2986 if mode.is_ap() {
2987 events |= WifiEvent::ApStop;
2988 }
2989 if mode.is_sta() {
2990 events |= WifiEvent::StaStop;
2991 }
2992
2993 Self::clear_events(events);
2994
2995 crate::wifi::WifiController::stop_impl(self)?;
2996
2997 self.wait_for_all_events(events, false).await;
2998
2999 reset_ap_state();
3000 reset_sta_state();
3001
3002 Ok(())
3003 }
3004
3005 pub async fn connect_async(&mut self) -> Result<(), WifiError> {
3007 Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected);
3008
3009 let err = crate::wifi::WifiController::connect_impl(self).err();
3010
3011 if MultiWifiEventFuture::new(WifiEvent::StaConnected | WifiEvent::StaDisconnected)
3012 .await
3013 .contains(WifiEvent::StaDisconnected)
3014 {
3015 Err(err.unwrap_or(WifiError::Disconnected))
3016 } else {
3017 Ok(())
3018 }
3019 }
3020
3021 pub async fn disconnect_async(&mut self) -> Result<(), WifiError> {
3024 if !matches!(self.is_connected(), Ok(true)) {
3028 return Ok(());
3029 }
3030
3031 Self::clear_events(WifiEvent::StaDisconnected);
3032 crate::wifi::WifiController::disconnect_impl(self)?;
3033 WifiEventFuture::new(WifiEvent::StaDisconnected).await;
3034
3035 Ok(())
3036 }
3037
3038 fn clear_events(events: impl Into<EnumSet<WifiEvent>>) {
3039 WIFI_EVENTS.with(|evts| evts.get_mut().remove_all(events.into()));
3040 }
3041
3042 pub async fn wait_for_event(&mut self, event: WifiEvent) {
3044 Self::clear_events(event);
3045 WifiEventFuture::new(event).await
3046 }
3047
3048 pub async fn wait_for_events(
3051 &mut self,
3052 events: EnumSet<WifiEvent>,
3053 clear_pending: bool,
3054 ) -> EnumSet<WifiEvent> {
3055 if clear_pending {
3056 Self::clear_events(events);
3057 }
3058 MultiWifiEventFuture::new(events).await
3059 }
3060
3061 pub async fn wait_for_all_events(
3063 &mut self,
3064 mut events: EnumSet<WifiEvent>,
3065 clear_pending: bool,
3066 ) {
3067 if clear_pending {
3068 Self::clear_events(events);
3069 }
3070
3071 while !events.is_empty() {
3072 let fired = MultiWifiEventFuture::new(events).await;
3073 events -= fired;
3074 }
3075 }
3076}
3077
3078impl WifiEvent {
3079 pub(crate) fn waker(&self) -> &'static AtomicWaker {
3080 static WAKER: AtomicWaker = AtomicWaker::new();
3084 &WAKER
3085 }
3086}
3087
3088#[must_use = "futures do nothing unless you `.await` or poll them"]
3089pub(crate) struct WifiEventFuture {
3090 event: WifiEvent,
3091}
3092
3093impl WifiEventFuture {
3094 pub fn new(event: WifiEvent) -> Self {
3096 Self { event }
3097 }
3098}
3099
3100impl core::future::Future for WifiEventFuture {
3101 type Output = ();
3102
3103 fn poll(
3104 self: core::pin::Pin<&mut Self>,
3105 cx: &mut core::task::Context<'_>,
3106 ) -> Poll<Self::Output> {
3107 self.event.waker().register(cx.waker());
3108 if WIFI_EVENTS.with(|events| events.get_mut().remove(self.event)) {
3109 Poll::Ready(())
3110 } else {
3111 Poll::Pending
3112 }
3113 }
3114}
3115
3116#[must_use = "futures do nothing unless you `.await` or poll them"]
3117pub(crate) struct MultiWifiEventFuture {
3118 event: EnumSet<WifiEvent>,
3119}
3120
3121impl MultiWifiEventFuture {
3122 pub fn new(event: EnumSet<WifiEvent>) -> Self {
3124 Self { event }
3125 }
3126}
3127
3128impl core::future::Future for MultiWifiEventFuture {
3129 type Output = EnumSet<WifiEvent>;
3130
3131 fn poll(
3132 self: core::pin::Pin<&mut Self>,
3133 cx: &mut core::task::Context<'_>,
3134 ) -> Poll<Self::Output> {
3135 let output = WIFI_EVENTS.with(|events| {
3136 let events = events.get_mut();
3137 let active = events.intersection(self.event);
3138 events.remove_all(active);
3139 active
3140 });
3141 if output.is_empty() {
3142 for event in self.event.iter() {
3143 event.waker().register(cx.waker());
3144 }
3145
3146 Poll::Pending
3147 } else {
3148 Poll::Ready(output)
3149 }
3150 }
3151}