1#![deny(missing_docs)]
4
5pub mod event;
6mod internal;
7pub(crate) mod os_adapter;
8pub(crate) mod state;
9use alloc::{collections::vec_deque::VecDeque, string::String};
10use core::{
11 fmt::Debug,
12 marker::PhantomData,
13 mem::MaybeUninit,
14 ptr::addr_of,
15 task::Poll,
16 time::Duration,
17};
18
19use enumset::{EnumSet, EnumSetType};
20use esp_config::esp_config_int;
21use esp_hal::{asynch::AtomicWaker, system::Cpu};
22use esp_sync::NonReentrantMutex;
23#[cfg(all(any(feature = "sniffer", feature = "esp-now"), feature = "unstable"))]
24use esp_wifi_sys::include::wifi_pkt_rx_ctrl_t;
25#[cfg(feature = "wifi-eap")]
26use esp_wifi_sys::include::{
27 esp_eap_client_clear_ca_cert,
28 esp_eap_client_clear_certificate_and_key,
29 esp_eap_client_clear_identity,
30 esp_eap_client_clear_new_password,
31 esp_eap_client_clear_password,
32 esp_eap_client_clear_username,
33 esp_eap_client_set_ca_cert,
34 esp_eap_client_set_certificate_and_key,
35 esp_eap_client_set_disable_time_check,
36 esp_eap_client_set_fast_params,
37 esp_eap_client_set_identity,
38 esp_eap_client_set_new_password,
39 esp_eap_client_set_pac_file,
40 esp_eap_client_set_password,
41 esp_eap_client_set_ttls_phase2_method,
42 esp_eap_client_set_username,
43 esp_eap_fast_config,
44 esp_wifi_sta_enterprise_enable,
45};
46#[cfg(all(feature = "sniffer", feature = "unstable"))]
47#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
48use esp_wifi_sys::include::{
49 esp_wifi_80211_tx,
50 esp_wifi_set_promiscuous,
51 esp_wifi_set_promiscuous_rx_cb,
52 wifi_promiscuous_pkt_t,
53 wifi_promiscuous_pkt_type_t,
54};
55use esp_wifi_sys::{
56 c_types::c_uint,
57 include::{
58 WIFI_INIT_CONFIG_MAGIC,
59 WIFI_PROTOCOL_11AX,
60 WIFI_PROTOCOL_11B,
61 WIFI_PROTOCOL_11G,
62 WIFI_PROTOCOL_11N,
63 WIFI_PROTOCOL_LR,
64 esp_wifi_connect_internal,
65 esp_wifi_disconnect_internal,
66 wifi_init_config_t,
67 wifi_scan_channel_bitmap_t,
68 },
69};
70use num_derive::FromPrimitive;
71#[doc(hidden)]
72pub(crate) use os_adapter::*;
73use portable_atomic::{AtomicUsize, Ordering};
74use procmacros::BuilderLite;
75#[cfg(feature = "serde")]
76use serde::{Deserialize, Serialize};
77#[cfg(all(feature = "smoltcp", feature = "unstable"))]
78#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
79use smoltcp::phy::{Device, DeviceCapabilities, RxToken, TxToken};
80pub use state::*;
81
82use crate::{
83 Controller,
84 common_adapter::*,
85 esp_wifi_result,
86 hal::ram,
87 wifi::private::PacketBuffer,
88};
89
90const MTU: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MTU");
91
92#[cfg(all(feature = "csi", esp32c6))]
93use crate::binary::include::wifi_csi_acquire_config_t;
94#[cfg(feature = "csi")]
95#[instability::unstable]
96pub use crate::binary::include::wifi_csi_info_t;
97#[cfg(feature = "csi")]
98#[instability::unstable]
99use crate::binary::include::{
100 esp_wifi_set_csi,
101 esp_wifi_set_csi_config,
102 esp_wifi_set_csi_rx_cb,
103 wifi_csi_config_t,
104};
105use crate::binary::{
106 c_types,
107 include::{
108 self,
109 __BindgenBitfieldUnit,
110 esp_err_t,
111 esp_interface_t_ESP_IF_WIFI_AP,
112 esp_interface_t_ESP_IF_WIFI_STA,
113 esp_supplicant_deinit,
114 esp_supplicant_init,
115 esp_wifi_deinit_internal,
116 esp_wifi_get_mode,
117 esp_wifi_init_internal,
118 esp_wifi_internal_free_rx_buffer,
119 esp_wifi_internal_reg_rxcb,
120 esp_wifi_internal_tx,
121 esp_wifi_scan_start,
122 esp_wifi_set_config,
123 esp_wifi_set_country,
124 esp_wifi_set_mode,
125 esp_wifi_set_protocol,
126 esp_wifi_set_tx_done_cb,
127 esp_wifi_sta_get_rssi,
128 esp_wifi_start,
129 esp_wifi_stop,
130 g_wifi_default_wpa_crypto_funcs,
131 wifi_active_scan_time_t,
132 wifi_ap_config_t,
133 wifi_auth_mode_t,
134 wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
135 wifi_config_t,
136 wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
137 wifi_country_t,
138 wifi_interface_t,
139 wifi_interface_t_WIFI_IF_AP,
140 wifi_interface_t_WIFI_IF_STA,
141 wifi_mode_t,
142 wifi_mode_t_WIFI_MODE_AP,
143 wifi_mode_t_WIFI_MODE_APSTA,
144 wifi_mode_t_WIFI_MODE_NULL,
145 wifi_mode_t_WIFI_MODE_STA,
146 wifi_pmf_config_t,
147 wifi_scan_config_t,
148 wifi_scan_threshold_t,
149 wifi_scan_time_t,
150 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
151 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
152 wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
153 wifi_sta_config_t,
154 },
155};
156
157#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd)]
159#[cfg_attr(feature = "defmt", derive(defmt::Format))]
160#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
161#[non_exhaustive]
162pub enum AuthMethod {
163 None,
165
166 Wep,
168
169 Wpa,
171
172 #[default]
174 Wpa2Personal,
175
176 WpaWpa2Personal,
178
179 Wpa2Enterprise,
181
182 Wpa3Personal,
184
185 Wpa2Wpa3Personal,
187
188 WapiPersonal,
190}
191
192#[derive(Debug, Default, PartialOrd, EnumSetType)]
194#[cfg_attr(feature = "defmt", derive(defmt::Format))]
195#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
196#[non_exhaustive]
197pub enum Protocol {
198 P802D11B,
200
201 P802D11BG,
203
204 #[default]
206 P802D11BGN,
207
208 P802D11BGNLR,
210
211 P802D11LR,
213
214 P802D11BGNAX,
216}
217
218impl Protocol {
219 fn to_mask(self) -> u32 {
220 match self {
221 Protocol::P802D11B => WIFI_PROTOCOL_11B,
222 Protocol::P802D11BG => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G,
223 Protocol::P802D11BGN => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
224 Protocol::P802D11BGNLR => {
225 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR
226 }
227 Protocol::P802D11LR => WIFI_PROTOCOL_LR,
228 Protocol::P802D11BGNAX => {
229 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX
230 }
231 }
232 }
233}
234
235#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd)]
237#[cfg_attr(feature = "defmt", derive(defmt::Format))]
238#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
239pub enum SecondaryChannel {
240 #[default]
243 None,
244
245 Above,
247
248 Below,
250}
251
252#[derive(Clone, Debug, Default, PartialEq, Eq)]
254#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
255pub struct Country([u8; 2]);
256
257impl Country {
258 fn try_from_c(info: &wifi_country_t) -> Option<Self> {
259 let cc_len = info
261 .cc
262 .iter()
263 .position(|&b| b == 0)
264 .unwrap_or(info.cc.len());
265
266 if cc_len < 2 {
267 return None;
268 }
269
270 let cc_slice = &info.cc[..cc_len.min(2)];
272 if cc_slice.iter().all(|&b| b.is_ascii_uppercase()) {
273 Some(Self([cc_slice[0], cc_slice[1]]))
274 } else {
275 None
276 }
277 }
278
279 pub fn country_code(&self) -> &str {
281 unsafe {
282 core::str::from_utf8_unchecked(&self.0)
284 }
285 }
286}
287
288#[cfg(feature = "defmt")]
289impl defmt::Format for Country {
290 fn format(&self, fmt: defmt::Formatter<'_>) {
291 self.country_code().format(fmt)
292 }
293}
294
295#[derive(Clone, Debug, Default, Eq, PartialEq)]
297#[cfg_attr(feature = "defmt", derive(defmt::Format))]
298#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
299#[non_exhaustive]
300pub struct AccessPointInfo {
301 #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
304 pub ssid: String,
305
306 pub bssid: [u8; 6],
308
309 pub channel: u8,
311
312 pub secondary_channel: SecondaryChannel,
314
315 pub signal_strength: i8,
317
318 pub auth_method: Option<AuthMethod>,
320
321 pub country: Option<Country>,
323}
324
325#[derive(BuilderLite, Clone, Eq, PartialEq)]
327#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
328pub struct AccessPointConfig {
329 #[builder_lite(reference)]
331 ssid: String,
332
333 ssid_hidden: bool,
335
336 channel: u8,
338
339 secondary_channel: Option<u8>,
341
342 protocols: EnumSet<Protocol>,
344
345 auth_method: AuthMethod,
347
348 #[builder_lite(reference)]
350 password: String,
351
352 max_connections: u16,
354 dtim_period: u8,
356
357 #[builder_lite(unstable)]
359 beacon_timeout: u16,
360}
361
362impl AccessPointConfig {
363 fn validate(&self) -> Result<(), WifiError> {
364 if self.ssid.len() > 32 {
365 return Err(WifiError::InvalidArguments);
366 }
367
368 if self.password.len() > 64 {
369 return Err(WifiError::InvalidArguments);
370 }
371
372 if !(1..=10).contains(&self.dtim_period) {
373 return Err(WifiError::InvalidArguments);
374 }
375
376 Ok(())
377 }
378}
379
380impl Default for AccessPointConfig {
381 fn default() -> Self {
382 Self {
383 ssid: String::from("iot-device"),
384 ssid_hidden: false,
385 channel: 1,
386 secondary_channel: None,
387 protocols: (Protocol::P802D11B | Protocol::P802D11BG | Protocol::P802D11BGN),
388 auth_method: AuthMethod::None,
389 password: String::new(),
390 max_connections: 255,
391 dtim_period: 2,
392 beacon_timeout: 300,
393 }
394 }
395}
396
397impl core::fmt::Debug for AccessPointConfig {
398 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
399 f.debug_struct("AccessPointConfig")
400 .field("ssid", &self.ssid)
401 .field("ssid_hidden", &self.ssid_hidden)
402 .field("channel", &self.channel)
403 .field("secondary_channel", &self.secondary_channel)
404 .field("protocols", &self.protocols)
405 .field("auth_method", &self.auth_method)
406 .field("password", &"**REDACTED**")
407 .field("max_connections", &self.max_connections)
408 .field("dtim_period", &self.dtim_period)
409 .field("beacon_timeout", &self.beacon_timeout)
410 .finish()
411 }
412}
413
414#[cfg(feature = "defmt")]
415impl defmt::Format for AccessPointConfig {
416 fn format(&self, fmt: defmt::Formatter<'_>) {
417 defmt::write!(
418 fmt,
419 "AccessPointConfiguration {{\
420 ssid: {}, \
421 ssid_hidden: {}, \
422 channel: {}, \
423 secondary_channel: {}, \
424 protocols: {}, \
425 auth_method: {}, \
426 password: **REDACTED**, \
427 max_connections: {}, \
428 dtim_period: {}, \
429 beacon_timeout: {} \
430 }}",
431 self.ssid.as_str(),
432 self.ssid_hidden,
433 self.channel,
434 self.secondary_channel,
435 self.protocols,
436 self.auth_method,
437 self.max_connections,
438 self.dtim_period,
439 self.beacon_timeout
440 );
441 }
442}
443
444#[derive(Debug, Clone, Copy, PartialEq, Eq)]
446#[cfg_attr(feature = "defmt", derive(defmt::Format))]
447#[repr(u8)]
448#[instability::unstable]
449pub enum ScanMethod {
450 Fast,
452
453 AllChannels,
455}
456
457#[derive(BuilderLite, Clone, Eq, PartialEq)]
459#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
460pub struct ClientConfig {
461 #[builder_lite(reference)]
463 ssid: String,
464
465 bssid: Option<[u8; 6]>,
467
468 auth_method: AuthMethod,
470
471 #[builder_lite(reference)]
473 password: String,
474
475 channel: Option<u8>,
477
478 protocols: EnumSet<Protocol>,
480
481 #[builder_lite(unstable)]
487 listen_interval: u16,
488
489 #[builder_lite(unstable)]
493 beacon_timeout: u16,
494
495 #[builder_lite(unstable)]
502 failure_retry_cnt: u8,
503
504 #[builder_lite(unstable)]
506 scan_method: ScanMethod,
507}
508
509impl ClientConfig {
510 fn validate(&self) -> Result<(), WifiError> {
511 if self.ssid.len() > 32 {
512 return Err(WifiError::InvalidArguments);
513 }
514
515 if self.password.len() > 64 {
516 return Err(WifiError::InvalidArguments);
517 }
518
519 if !(6..=31).contains(&self.beacon_timeout) {
520 return Err(WifiError::InvalidArguments);
521 }
522
523 Ok(())
524 }
525}
526
527impl Default for ClientConfig {
528 fn default() -> Self {
529 ClientConfig {
530 ssid: String::new(),
531 bssid: None,
532 auth_method: AuthMethod::Wpa2Personal,
533 password: String::new(),
534 channel: None,
535 protocols: (Protocol::P802D11B | Protocol::P802D11BG | Protocol::P802D11BGN),
536 listen_interval: 3,
537 beacon_timeout: 6,
538 failure_retry_cnt: 1,
539 scan_method: ScanMethod::Fast,
540 }
541 }
542}
543
544impl core::fmt::Debug for ClientConfig {
545 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
546 f.debug_struct("ClientConfig")
547 .field("ssid", &self.ssid)
548 .field("bssid", &self.bssid)
549 .field("auth_method", &self.auth_method)
550 .field("password", &"**REDACTED**")
551 .field("channel", &self.channel)
552 .field("protocols", &self.protocols)
553 .field("listen_interval", &self.listen_interval)
554 .field("beacon_timeout", &self.beacon_timeout)
555 .field("failure_retry_cnt", &self.failure_retry_cnt)
556 .field("scan_method", &self.scan_method)
557 .finish()
558 }
559}
560
561#[cfg(feature = "defmt")]
562impl defmt::Format for ClientConfig {
563 fn format(&self, fmt: defmt::Formatter<'_>) {
564 defmt::write!(
565 fmt,
566 "ClientConfiguration {{\
567 ssid: {}, \
568 bssid: {:?}, \
569 auth_method: {:?}, \
570 password: **REDACTED**, \
571 channel: {:?}, \
572 protocols: {}, \
573 listen_interval: {}, \
574 beacon_timeout: {}, \
575 failure_retry_cnt: {}, \
576 scan_method: {} \
577 }}",
578 self.ssid.as_str(),
579 self.bssid,
580 self.auth_method,
581 self.channel,
582 self.protocols,
583 self.listen_interval,
584 self.beacon_timeout,
585 self.failure_retry_cnt,
586 self.scan_method
587 )
588 }
589}
590
591#[derive(Clone, Debug, PartialEq, Eq)]
593#[cfg_attr(feature = "defmt", derive(defmt::Format))]
594#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
595#[cfg(feature = "wifi-eap")]
596#[instability::unstable]
597pub struct EapFastConfig {
598 pub fast_provisioning: u8,
600 pub fast_max_pac_list_len: u8,
602 pub fast_pac_format_binary: bool,
604}
605
606#[derive(Debug, Clone, PartialEq, Eq)]
608#[cfg_attr(feature = "defmt", derive(defmt::Format))]
609#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
610#[cfg(feature = "wifi-eap")]
611#[instability::unstable]
612pub enum TtlsPhase2Method {
613 Eap,
615
616 Mschapv2,
618
619 Mschap,
621
622 Pap,
624
625 Chap,
627}
628
629#[cfg(feature = "wifi-eap")]
630impl TtlsPhase2Method {
631 fn to_raw(&self) -> u32 {
633 match self {
634 TtlsPhase2Method::Eap => {
635 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_EAP
636 }
637 TtlsPhase2Method::Mschapv2 => {
638 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAPV2
639 }
640 TtlsPhase2Method::Mschap => {
641 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAP
642 }
643 TtlsPhase2Method::Pap => {
644 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_PAP
645 }
646 TtlsPhase2Method::Chap => {
647 esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_CHAP
648 }
649 }
650 }
651}
652
653#[cfg(feature = "wifi-eap")]
654type CertificateAndKey = (&'static [u8], &'static [u8], Option<&'static [u8]>);
655
656#[derive(BuilderLite, Clone, PartialEq, Eq)]
658#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
659#[cfg(feature = "wifi-eap")]
660#[instability::unstable]
661pub struct EapClientConfig {
662 #[builder_lite(reference)]
664 ssid: String,
665
666 bssid: Option<[u8; 6]>,
668
669 auth_method: AuthMethod,
671
672 #[builder_lite(reference)]
674 identity: Option<String>,
675
676 #[builder_lite(reference)]
679 username: Option<String>,
680
681 #[builder_lite(reference)]
683 password: Option<String>,
684
685 #[builder_lite(reference)]
688 new_password: Option<String>,
689
690 #[builder_lite(reference)]
692 eap_fast_config: Option<EapFastConfig>,
693
694 pac_file: Option<&'static [u8]>,
696
697 time_check: bool,
700
701 ca_cert: Option<&'static [u8]>,
704
705 certificate_and_key: Option<CertificateAndKey>,
708 #[builder_lite(reference)]
710 ttls_phase2_method: Option<TtlsPhase2Method>,
711
712 channel: Option<u8>,
714
715 protocols: EnumSet<Protocol>,
717
718 #[builder_lite(unstable)]
724 listen_interval: u16,
725
726 #[builder_lite(unstable)]
730 beacon_timeout: u16,
731
732 #[builder_lite(unstable)]
739 failure_retry_cnt: u8,
740
741 #[builder_lite(unstable)]
743 scan_method: ScanMethod,
744}
745
746#[cfg(feature = "wifi-eap")]
747impl EapClientConfig {
748 fn validate(&self) -> Result<(), WifiError> {
749 if self.ssid.len() > 32 {
750 return Err(WifiError::InvalidArguments);
751 }
752
753 if self.identity.as_ref().unwrap_or(&String::new()).len() > 128 {
754 return Err(WifiError::InvalidArguments);
755 }
756
757 if self.username.as_ref().unwrap_or(&String::new()).len() > 128 {
758 return Err(WifiError::InvalidArguments);
759 }
760
761 if self.password.as_ref().unwrap_or(&String::new()).len() > 64 {
762 return Err(WifiError::InvalidArguments);
763 }
764
765 if self.new_password.as_ref().unwrap_or(&String::new()).len() > 64 {
766 return Err(WifiError::InvalidArguments);
767 }
768
769 if !(6..=31).contains(&self.beacon_timeout) {
770 return Err(WifiError::InvalidArguments);
771 }
772
773 Ok(())
774 }
775}
776
777#[cfg(feature = "wifi-eap")]
778impl Debug for EapClientConfig {
779 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
780 f.debug_struct("EapClientConfig")
781 .field("ssid", &self.ssid)
782 .field("bssid", &self.bssid)
783 .field("auth_method", &self.auth_method)
784 .field("channel", &self.channel)
785 .field("identity", &self.identity)
786 .field("username", &self.username)
787 .field("password", &"**REDACTED**")
788 .field("new_password", &"**REDACTED**")
789 .field("eap_fast_config", &self.eap_fast_config)
790 .field("time_check", &self.time_check)
791 .field("pac_file set", &self.pac_file.is_some())
792 .field("ca_cert set", &self.ca_cert.is_some())
793 .field("certificate_and_key set", &"**REDACTED**")
794 .field("ttls_phase2_method", &self.ttls_phase2_method)
795 .field("protocols", &self.protocols)
796 .field("listen_interval", &self.listen_interval)
797 .field("beacon_timeout", &self.beacon_timeout)
798 .field("failure_retry_cnt", &self.failure_retry_cnt)
799 .field("scan_method", &self.scan_method)
800 .finish()
801 }
802}
803
804#[cfg(feature = "defmt")]
805#[cfg(feature = "wifi-eap")]
806impl defmt::Format for EapClientConfig {
807 fn format(&self, fmt: defmt::Formatter<'_>) {
808 defmt::write!(
809 fmt,
810 "EapClientConfiguration {{\
811 ssid: {}, \
812 bssid: {:?}, \
813 auth_method: {:?}, \
814 channel: {:?}, \
815 identity: {:?}, \
816 username: {:?}, \
817 password: **REDACTED**, \
818 new_password: **REDACTED**, \
819 eap_fast_config: {:?}, \
820 time_check: {}, \
821 pac_file: {}, \
822 ca_cert: {}, \
823 certificate_and_key: **REDACTED**, \
824 ttls_phase2_method: {:?}, \
825 protocols: {}, \
826 listen_interval: {}, \
827 beacon_timeout: {}, \
828 failure_retry_cnt: {}, \
829 scan_method: {},
830 }}",
831 self.ssid.as_str(),
832 self.bssid,
833 self.auth_method,
834 self.channel,
835 &self.identity.as_ref().map_or("", |v| v.as_str()),
836 &self.username.as_ref().map_or("", |v| v.as_str()),
837 self.eap_fast_config,
838 self.time_check,
839 self.pac_file,
840 self.ca_cert,
841 self.ttls_phase2_method,
842 self.protocols,
843 self.listen_interval,
844 self.beacon_timeout,
845 self.failure_retry_cnt,
846 self.scan_method
847 )
848 }
849}
850
851#[cfg(feature = "wifi-eap")]
852impl Default for EapClientConfig {
853 fn default() -> Self {
854 EapClientConfig {
855 ssid: String::new(),
856 bssid: None,
857 auth_method: AuthMethod::Wpa2Enterprise,
858 identity: None,
859 username: None,
860 password: None,
861 channel: None,
862 eap_fast_config: None,
863 time_check: false,
864 new_password: None,
865 pac_file: None,
866 ca_cert: None,
867 certificate_and_key: None,
868 ttls_phase2_method: None,
869 protocols: (Protocol::P802D11B | Protocol::P802D11BG | Protocol::P802D11BGN),
870 listen_interval: 3,
871 beacon_timeout: 6,
872 failure_retry_cnt: 1,
873 scan_method: ScanMethod::Fast,
874 }
875 }
876}
877
878#[derive(EnumSetType, Debug, PartialOrd)]
880#[cfg_attr(feature = "defmt", derive(defmt::Format))]
881#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
882#[non_exhaustive]
883pub enum Capability {
884 Client,
886
887 AccessPoint,
890
891 ApSta,
894}
895
896#[allow(clippy::large_enum_variant)]
898#[derive(Clone, Debug, PartialEq, Eq, Default)]
899#[cfg_attr(feature = "defmt", derive(defmt::Format))]
900#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
901#[non_exhaustive]
902pub enum ModeConfig {
903 #[default]
905 None,
906
907 Client(ClientConfig),
909
910 AccessPoint(AccessPointConfig),
912
913 ApSta(ClientConfig, AccessPointConfig),
915
916 #[cfg(feature = "wifi-eap")]
918 #[cfg_attr(feature = "serde", serde(skip))]
919 EapClient(EapClientConfig),
920}
921
922impl ModeConfig {
923 fn validate(&self) -> Result<(), WifiError> {
924 match self {
925 ModeConfig::None => Ok(()),
926 ModeConfig::Client(client_configuration) => client_configuration.validate(),
927 ModeConfig::AccessPoint(access_point_configuration) => {
928 access_point_configuration.validate()
929 }
930 ModeConfig::ApSta(client_configuration, access_point_configuration) => {
931 client_configuration.validate()?;
932 access_point_configuration.validate()
933 }
934 #[cfg(feature = "wifi-eap")]
935 ModeConfig::EapClient(eap_client_configuration) => eap_client_configuration.validate(),
936 }
937 }
938}
939
940trait AuthMethodExt {
941 fn to_raw(&self) -> wifi_auth_mode_t;
942 fn from_raw(raw: wifi_auth_mode_t) -> Self;
943}
944
945impl AuthMethodExt for AuthMethod {
946 fn to_raw(&self) -> wifi_auth_mode_t {
947 match self {
948 AuthMethod::None => include::wifi_auth_mode_t_WIFI_AUTH_OPEN,
949 AuthMethod::Wep => include::wifi_auth_mode_t_WIFI_AUTH_WEP,
950 AuthMethod::Wpa => include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
951 AuthMethod::Wpa2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
952 AuthMethod::WpaWpa2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK,
953 AuthMethod::Wpa2Enterprise => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE,
954 AuthMethod::Wpa3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
955 AuthMethod::Wpa2Wpa3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK,
956 AuthMethod::WapiPersonal => include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
957 }
958 }
959
960 fn from_raw(raw: wifi_auth_mode_t) -> Self {
961 match raw {
962 include::wifi_auth_mode_t_WIFI_AUTH_OPEN => AuthMethod::None,
963 include::wifi_auth_mode_t_WIFI_AUTH_WEP => AuthMethod::Wep,
964 include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => AuthMethod::Wpa,
965 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => AuthMethod::Wpa2Personal,
966 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => AuthMethod::WpaWpa2Personal,
967 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => AuthMethod::Wpa2Enterprise,
968 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => AuthMethod::Wpa3Personal,
969 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => AuthMethod::Wpa2Wpa3Personal,
970 include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => AuthMethod::WapiPersonal,
971 _ => unreachable!(),
972 }
973 }
974}
975
976#[derive(Debug, Clone, Copy, PartialEq)]
978#[cfg_attr(feature = "defmt", derive(defmt::Format))]
979#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
980#[non_exhaustive]
981pub enum WifiMode {
982 Sta,
984 Ap,
986 ApSta,
988}
989
990impl WifiMode {
991 pub(crate) fn current() -> Result<Self, WifiError> {
992 let mut mode = wifi_mode_t_WIFI_MODE_NULL;
993 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?;
994
995 Self::try_from(mode)
996 }
997
998 pub fn is_sta(&self) -> bool {
1000 match self {
1001 Self::Sta | Self::ApSta => true,
1002 Self::Ap => false,
1003 }
1004 }
1005
1006 pub fn is_ap(&self) -> bool {
1008 match self {
1009 Self::Sta => false,
1010 Self::Ap | Self::ApSta => true,
1011 }
1012 }
1013}
1014
1015impl TryFrom<&ModeConfig> for WifiMode {
1016 type Error = WifiError;
1017
1018 fn try_from(config: &ModeConfig) -> Result<Self, Self::Error> {
1020 let mode = match config {
1021 ModeConfig::None => return Err(WifiError::UnknownWifiMode),
1022 ModeConfig::AccessPoint(_) => Self::Ap,
1023 ModeConfig::Client(_) => Self::Sta,
1024 ModeConfig::ApSta(_, _) => Self::ApSta,
1025 #[cfg(feature = "wifi-eap")]
1026 ModeConfig::EapClient(_) => Self::Sta,
1027 };
1028
1029 Ok(mode)
1030 }
1031}
1032
1033#[doc(hidden)]
1034impl TryFrom<wifi_mode_t> for WifiMode {
1035 type Error = WifiError;
1036
1037 fn try_from(value: wifi_mode_t) -> Result<Self, Self::Error> {
1039 #[allow(non_upper_case_globals)]
1040 match value {
1041 include::wifi_mode_t_WIFI_MODE_STA => Ok(Self::Sta),
1042 include::wifi_mode_t_WIFI_MODE_AP => Ok(Self::Ap),
1043 include::wifi_mode_t_WIFI_MODE_APSTA => Ok(Self::ApSta),
1044 _ => Err(WifiError::UnknownWifiMode),
1045 }
1046 }
1047}
1048
1049#[doc(hidden)]
1050impl From<WifiMode> for wifi_mode_t {
1051 fn from(val: WifiMode) -> Self {
1052 #[allow(non_upper_case_globals)]
1053 match val {
1054 WifiMode::Sta => wifi_mode_t_WIFI_MODE_STA,
1055 WifiMode::Ap => wifi_mode_t_WIFI_MODE_AP,
1056 WifiMode::ApSta => wifi_mode_t_WIFI_MODE_APSTA,
1057 }
1058 }
1059}
1060
1061#[cfg(feature = "csi")]
1062pub(crate) trait CsiCallback: FnMut(crate::binary::include::wifi_csi_info_t) {}
1063
1064#[cfg(feature = "csi")]
1065impl<T> CsiCallback for T where T: FnMut(crate::binary::include::wifi_csi_info_t) {}
1066
1067#[cfg(feature = "csi")]
1068unsafe extern "C" fn csi_rx_cb<C: CsiCallback>(
1069 ctx: *mut crate::wifi::c_types::c_void,
1070 data: *mut crate::binary::include::wifi_csi_info_t,
1071) {
1072 unsafe {
1073 let csi_callback = &mut *(ctx as *mut C);
1074 csi_callback(*data);
1075 }
1076}
1077
1078#[derive(Clone, PartialEq, Eq)]
1080#[cfg(all(not(esp32c6), feature = "csi"))]
1081pub struct CsiConfig {
1082 pub lltf_en: bool,
1084 pub htltf_en: bool,
1086 pub stbc_htltf2_en: bool,
1089 pub ltf_merge_en: bool,
1092 pub channel_filter_en: bool,
1095 pub manu_scale: bool,
1099 pub shift: u8,
1102 pub dump_ack_en: bool,
1104}
1105
1106#[derive(Clone, PartialEq, Eq)]
1108#[cfg(all(esp32c6, feature = "csi"))]
1109pub struct CsiConfig {
1110 pub enable: u32,
1112 pub acquire_csi_legacy: u32,
1114 pub acquire_csi_ht20: u32,
1116 pub acquire_csi_ht40: u32,
1118 pub acquire_csi_su: u32,
1120 pub acquire_csi_mu: u32,
1122 pub acquire_csi_dcm: u32,
1124 pub acquire_csi_beamformed: u32,
1126 pub acquire_csi_he_stbc: u32,
1130 pub val_scale_cfg: u32,
1132 pub dump_ack_en: u32,
1134 pub reserved: u32,
1136}
1137
1138#[cfg(feature = "csi")]
1139impl Default for CsiConfig {
1140 #[cfg(not(esp32c6))]
1141 fn default() -> Self {
1142 Self {
1143 lltf_en: true,
1144 htltf_en: true,
1145 stbc_htltf2_en: true,
1146 ltf_merge_en: true,
1147 channel_filter_en: true,
1148 manu_scale: false,
1149 shift: 0,
1150 dump_ack_en: false,
1151 }
1152 }
1153
1154 #[cfg(esp32c6)]
1155 fn default() -> Self {
1156 Self {
1158 enable: 1,
1159 acquire_csi_legacy: 1,
1160 acquire_csi_ht20: 1,
1161 acquire_csi_ht40: 1,
1162 acquire_csi_su: 1,
1163 acquire_csi_mu: 1,
1164 acquire_csi_dcm: 1,
1165 acquire_csi_beamformed: 1,
1166 acquire_csi_he_stbc: 2,
1167 val_scale_cfg: 2,
1168 dump_ack_en: 1,
1169 reserved: 19,
1170 }
1171 }
1172}
1173
1174#[doc(hidden)]
1175#[cfg(feature = "csi")]
1176impl From<CsiConfig> for wifi_csi_config_t {
1177 fn from(config: CsiConfig) -> Self {
1178 #[cfg(not(esp32c6))]
1179 {
1180 wifi_csi_config_t {
1181 lltf_en: config.lltf_en,
1182 htltf_en: config.htltf_en,
1183 stbc_htltf2_en: config.stbc_htltf2_en,
1184 ltf_merge_en: config.ltf_merge_en,
1185 channel_filter_en: config.channel_filter_en,
1186 manu_scale: config.manu_scale,
1187 shift: config.shift,
1188 dump_ack_en: config.dump_ack_en,
1189 }
1190 }
1191 #[cfg(esp32c6)]
1192 {
1193 wifi_csi_acquire_config_t {
1194 _bitfield_align_1: [0; 0],
1195 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
1196 config.enable,
1197 config.acquire_csi_legacy,
1198 config.acquire_csi_ht20,
1199 config.acquire_csi_ht40,
1200 config.acquire_csi_su,
1201 config.acquire_csi_mu,
1202 config.acquire_csi_dcm,
1203 config.acquire_csi_beamformed,
1204 config.acquire_csi_he_stbc,
1205 config.val_scale_cfg,
1206 config.dump_ack_en,
1207 config.reserved,
1208 ),
1209 }
1210 }
1211 }
1212}
1213
1214#[cfg(feature = "csi")]
1215impl CsiConfig {
1216 pub(crate) fn apply_config(&self) -> Result<(), WifiError> {
1218 let conf: wifi_csi_config_t = self.clone().into();
1219
1220 unsafe {
1221 esp_wifi_result!(esp_wifi_set_csi_config(&conf))?;
1222 }
1223 Ok(())
1224 }
1225
1226 pub(crate) fn set_receive_cb<C: CsiCallback>(&mut self, cb: C) -> Result<(), WifiError> {
1229 let cb = alloc::boxed::Box::new(cb);
1230 let cb_ptr = alloc::boxed::Box::into_raw(cb) as *mut crate::wifi::c_types::c_void;
1231
1232 unsafe {
1233 esp_wifi_result!(esp_wifi_set_csi_rx_cb(Some(csi_rx_cb::<C>), cb_ptr))?;
1234 }
1235 Ok(())
1236 }
1237
1238 pub(crate) fn set_csi(&self, enable: bool) -> Result<(), WifiError> {
1240 unsafe {
1242 esp_wifi_result!(esp_wifi_set_csi(enable))?;
1243 }
1244 Ok(())
1245 }
1246}
1247
1248static RX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0);
1249static TX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0);
1250
1251pub(crate) static DATA_QUEUE_RX_AP: NonReentrantMutex<VecDeque<PacketBuffer>> =
1252 NonReentrantMutex::new(VecDeque::new());
1253
1254pub(crate) static DATA_QUEUE_RX_STA: NonReentrantMutex<VecDeque<PacketBuffer>> =
1255 NonReentrantMutex::new(VecDeque::new());
1256
1257#[derive(Debug, Clone, Copy)]
1259#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1260#[non_exhaustive]
1261pub enum WifiError {
1262 NotInitialized,
1265
1266 InternalError(InternalWifiError),
1268
1269 Disconnected,
1271
1272 UnknownWifiMode,
1274
1275 Unsupported,
1277
1278 InvalidArguments,
1280}
1281
1282impl core::fmt::Display for WifiError {
1284 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1285 match self {
1286 WifiError::NotInitialized => write!(f, "Wi-Fi not initialized."),
1287 WifiError::InternalError(err) => write!(f, "Internal Wi-Fi error: {err:?}."),
1288 WifiError::Disconnected => write!(f, "Wi-Fi disconnected."),
1289 WifiError::UnknownWifiMode => write!(f, "Unknown Wi-Fi mode."),
1290 WifiError::Unsupported => write!(f, "Unsupported operation or mode."),
1291 WifiError::InvalidArguments => write!(f, "Invalid arguments."),
1292 }
1293 }
1294}
1295
1296impl core::error::Error for WifiError {}
1297
1298#[derive(Debug, FromPrimitive, EnumSetType)]
1300#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1301#[non_exhaustive]
1302#[repr(i32)]
1303pub enum WifiEvent {
1304 WifiReady = 0,
1306 ScanDone,
1308 StaStart,
1310 StaStop,
1312 StaConnected,
1314 StaDisconnected,
1316 StaAuthmodeChange,
1318
1319 StaWpsErSuccess,
1321 StaWpsErFailed,
1323 StaWpsErTimeout,
1325 StaWpsErPin,
1327 StaWpsErPbcOverlap,
1329
1330 ApStart,
1332 ApStop,
1334 ApStaConnected,
1336 ApStaDisconnected,
1338 ApProbeReqReceived,
1340
1341 FtmReport,
1343
1344 StaBssRssiLow,
1346 ActionTxStatus,
1348 RocDone,
1350
1351 StaBeaconTimeout,
1353
1354 ConnectionlessModuleWakeIntervalStart,
1356
1357 ApWpsRgSuccess,
1359 ApWpsRgFailed,
1361 ApWpsRgTimeout,
1363 ApWpsRgPin,
1365 ApWpsRgPbcOverlap,
1367
1368 ItwtSetup,
1370 ItwtTeardown,
1372 ItwtProbe,
1374 ItwtSuspend,
1376 TwtWakeup,
1378 BtwtSetup,
1380 BtwtTeardown,
1382
1383 NanStarted,
1385 NanStopped,
1387 NanSvcMatch,
1389 NanReplied,
1391 NanReceive,
1393 NdpIndication,
1395 NdpConfirm,
1397 NdpTerminated,
1399 HomeChannelChange,
1401
1402 StaNeighborRep,
1404}
1405
1406#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
1408#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1409#[non_exhaustive]
1410#[repr(i32)]
1411pub enum InternalWifiError {
1412 NoMem = 0x101,
1414
1415 InvalidArg = 0x102,
1417
1418 NotInit = 0x3001,
1420
1421 NotStarted = 0x3002,
1423
1424 NotStopped = 0x3003,
1426
1427 Interface = 0x3004,
1429
1430 Mode = 0x3005,
1432
1433 State = 0x3006,
1435
1436 Conn = 0x3007,
1438
1439 Nvs = 0x3008,
1441
1442 InvalidMac = 0x3009,
1444
1445 InvalidSsid = 0x300A,
1447
1448 InvalidPassword = 0x300B,
1450
1451 Timeout = 0x300C,
1453
1454 WakeFail = 0x300D,
1456
1457 WouldBlock = 0x300E,
1459
1460 NotConnected = 0x300F,
1462
1463 PostFail = 0x3012,
1465
1466 InvalidInitState = 0x3013,
1468
1469 StopState = 0x3014,
1471
1472 NotAssociated = 0x3015,
1474
1475 TxDisallowed = 0x3016,
1477}
1478
1479pub fn ap_mac() -> [u8; 6] {
1481 let mut mac = [0u8; 6];
1482 unsafe {
1483 read_mac(mac.as_mut_ptr(), 1);
1484 }
1485 mac
1486}
1487
1488pub fn sta_mac() -> [u8; 6] {
1490 let mut mac = [0u8; 6];
1491 unsafe {
1492 read_mac(mac.as_mut_ptr(), 0);
1493 }
1494 mac
1495}
1496
1497#[cfg(esp32)]
1498fn set_mac_time_update_cb(wifi: crate::hal::peripherals::WIFI<'_>) {
1499 use esp_phy::MacTimeExt;
1500 use esp_wifi_sys::include::esp_wifi_internal_update_mac_time;
1501 unsafe {
1502 wifi.set_mac_time_update_cb(|duration| {
1503 esp_wifi_internal_update_mac_time(duration.as_micros() as u32);
1504 });
1505 }
1506}
1507
1508pub(crate) fn wifi_init(_wifi: crate::hal::peripherals::WIFI<'_>) -> Result<(), WifiError> {
1509 #[cfg(esp32)]
1510 set_mac_time_update_cb(_wifi);
1511 unsafe {
1512 #[cfg(coex)]
1513 esp_wifi_result!(coex_init())?;
1514
1515 esp_wifi_result!(esp_wifi_init_internal(addr_of!(internal::G_CONFIG)))?;
1516 esp_wifi_result!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
1517
1518 esp_wifi_result!(esp_supplicant_init())?;
1519
1520 esp_wifi_result!(esp_wifi_set_tx_done_cb(Some(esp_wifi_tx_done_cb)))?;
1521
1522 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1523 esp_interface_t_ESP_IF_WIFI_STA,
1524 Some(recv_cb_sta)
1525 ))?;
1526
1527 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1529 esp_interface_t_ESP_IF_WIFI_AP,
1530 Some(recv_cb_ap)
1531 ))?;
1532
1533 Ok(())
1534 }
1535}
1536
1537#[cfg(coex)]
1538pub(crate) fn coex_initialize() -> i32 {
1539 debug!("call coex-initialize");
1540 unsafe {
1541 let res = crate::binary::include::esp_coex_adapter_register(
1542 core::ptr::addr_of_mut!(internal::G_COEX_ADAPTER_FUNCS).cast(),
1543 );
1544 if res != 0 {
1545 error!("Error: esp_coex_adapter_register {}", res);
1546 return res;
1547 }
1548 let res = crate::binary::include::coex_pre_init();
1549 if res != 0 {
1550 error!("Error: coex_pre_init {}", res);
1551 return res;
1552 }
1553 0
1554 }
1555}
1556
1557pub(crate) unsafe extern "C" fn coex_init() -> i32 {
1558 #[cfg(coex)]
1559 {
1560 debug!("coex-init");
1561 #[allow(clippy::needless_return)]
1562 return unsafe { crate::binary::include::coex_init() };
1563 }
1564
1565 #[cfg(not(coex))]
1566 0
1567}
1568
1569fn wifi_deinit() -> Result<(), crate::InitializationError> {
1570 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
1571 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1572 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1573 Ok(())
1574}
1575
1576unsafe extern "C" fn recv_cb_sta(
1577 buffer: *mut c_types::c_void,
1578 len: u16,
1579 eb: *mut c_types::c_void,
1580) -> esp_err_t {
1581 let packet = PacketBuffer { buffer, len, eb };
1582 match DATA_QUEUE_RX_STA.with(|queue| {
1589 if queue.len() < RX_QUEUE_SIZE.load(Ordering::Relaxed) {
1590 queue.push_back(packet);
1591 Ok(())
1592 } else {
1593 Err(packet)
1594 }
1595 }) {
1596 Ok(()) => {
1597 embassy::STA_RECEIVE_WAKER.wake();
1598 include::ESP_OK as esp_err_t
1599 }
1600 _ => {
1601 debug!("RX QUEUE FULL");
1602 include::ESP_ERR_NO_MEM as esp_err_t
1603 }
1604 }
1605}
1606
1607unsafe extern "C" fn recv_cb_ap(
1608 buffer: *mut c_types::c_void,
1609 len: u16,
1610 eb: *mut c_types::c_void,
1611) -> esp_err_t {
1612 let packet = PacketBuffer { buffer, len, eb };
1613 match DATA_QUEUE_RX_AP.with(|queue| {
1620 if queue.len() < RX_QUEUE_SIZE.load(Ordering::Relaxed) {
1621 queue.push_back(packet);
1622 Ok(())
1623 } else {
1624 Err(packet)
1625 }
1626 }) {
1627 Ok(()) => {
1628 embassy::AP_RECEIVE_WAKER.wake();
1629 include::ESP_OK as esp_err_t
1630 }
1631 _ => {
1632 debug!("RX QUEUE FULL");
1633 include::ESP_ERR_NO_MEM as esp_err_t
1634 }
1635 }
1636}
1637
1638pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1639
1640fn decrement_inflight_counter() {
1641 unwrap!(
1642 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1643 Some(x.saturating_sub(1))
1644 })
1645 );
1646}
1647
1648#[ram]
1649unsafe extern "C" fn esp_wifi_tx_done_cb(
1650 _ifidx: u8,
1651 _data: *mut u8,
1652 _data_len: *mut u16,
1653 _tx_status: bool,
1654) {
1655 trace!("esp_wifi_tx_done_cb");
1656
1657 decrement_inflight_counter();
1658
1659 embassy::TRANSMIT_WAKER.wake();
1660}
1661
1662#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1671#[non_exhaustive]
1672pub enum ScanTypeConfig {
1673 Active {
1683 min: Duration,
1685 max: Duration,
1687 },
1688 Passive(Duration),
1699}
1700
1701impl Default for ScanTypeConfig {
1702 fn default() -> Self {
1703 Self::Active {
1704 min: Duration::from_millis(10),
1705 max: Duration::from_millis(20),
1706 }
1707 }
1708}
1709
1710impl ScanTypeConfig {
1711 fn validate(&self) {
1712 if matches!(self, Self::Passive(dur) if *dur > Duration::from_millis(1500)) {
1713 warn!(
1714 "Passive scan duration longer than 1500ms may cause a station to disconnect from the AP"
1715 );
1716 }
1717 }
1718}
1719
1720#[derive(Clone, Copy, Default, PartialEq, Eq, BuilderLite)]
1722pub struct ScanConfig<'a> {
1723 ssid: Option<&'a str>,
1728 bssid: Option<[u8; 6]>,
1733 channel: Option<u8>,
1738 show_hidden: bool,
1740 scan_type: ScanTypeConfig,
1742 max: Option<usize>,
1746}
1747
1748pub(crate) fn wifi_start_scan(
1749 block: bool,
1750 ScanConfig {
1751 ssid,
1752 mut bssid,
1753 channel,
1754 show_hidden,
1755 scan_type,
1756 ..
1757 }: ScanConfig<'_>,
1758) -> i32 {
1759 scan_type.validate();
1760 let (scan_time, scan_type) = match scan_type {
1761 ScanTypeConfig::Active { min, max } => (
1762 wifi_scan_time_t {
1763 active: wifi_active_scan_time_t {
1764 min: min.as_millis() as u32,
1765 max: max.as_millis() as u32,
1766 },
1767 passive: 0,
1768 },
1769 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1770 ),
1771 ScanTypeConfig::Passive(dur) => (
1772 wifi_scan_time_t {
1773 active: wifi_active_scan_time_t { min: 0, max: 0 },
1774 passive: dur.as_millis() as u32,
1775 },
1776 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1777 ),
1778 };
1779
1780 let mut ssid_buf = ssid.map(|m| {
1781 let mut buf = alloc::vec::Vec::from_iter(m.bytes());
1782 buf.push(b'\0');
1783 buf
1784 });
1785
1786 let ssid = ssid_buf
1787 .as_mut()
1788 .map(|e| e.as_mut_ptr())
1789 .unwrap_or_else(core::ptr::null_mut);
1790 let bssid = bssid
1791 .as_mut()
1792 .map(|e| e.as_mut_ptr())
1793 .unwrap_or_else(core::ptr::null_mut);
1794
1795 let scan_config = wifi_scan_config_t {
1796 ssid,
1797 bssid,
1798 channel: channel.unwrap_or(0),
1799 show_hidden,
1800 scan_type,
1801 scan_time,
1802 home_chan_dwell_time: 0,
1803 channel_bitmap: wifi_scan_channel_bitmap_t {
1804 ghz_2_channels: 0,
1805 ghz_5_channels: 0,
1806 },
1807 coex_background_scan: false,
1808 };
1809
1810 unsafe { esp_wifi_scan_start(&scan_config, block) }
1811}
1812
1813mod private {
1814 use super::*;
1815
1816 #[derive(Debug)]
1817 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1818 pub struct PacketBuffer {
1826 pub(crate) buffer: *mut c_types::c_void,
1827 pub(crate) len: u16,
1828 pub(crate) eb: *mut c_types::c_void,
1829 }
1830
1831 unsafe impl Send for PacketBuffer {}
1832
1833 impl Drop for PacketBuffer {
1834 fn drop(&mut self) {
1835 trace!("Dropping PacketBuffer, freeing memory");
1836 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1837 }
1838 }
1839
1840 impl PacketBuffer {
1841 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1842 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1843 }
1844 }
1845}
1846
1847#[derive(Debug, Clone, Copy)]
1849enum WifiDeviceMode {
1850 Sta,
1852 Ap,
1854}
1855
1856impl WifiDeviceMode {
1857 fn mac_address(&self) -> [u8; 6] {
1858 match self {
1859 WifiDeviceMode::Sta => sta_mac(),
1860 WifiDeviceMode::Ap => ap_mac(),
1861 }
1862 }
1863
1864 fn data_queue_rx(&self) -> &'static NonReentrantMutex<VecDeque<PacketBuffer>> {
1865 match self {
1866 WifiDeviceMode::Sta => &DATA_QUEUE_RX_STA,
1867 WifiDeviceMode::Ap => &DATA_QUEUE_RX_AP,
1868 }
1869 }
1870
1871 fn can_send(&self) -> bool {
1872 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE.load(Ordering::Relaxed)
1873 }
1874
1875 fn increase_in_flight_counter(&self) {
1876 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1877 }
1878
1879 fn tx_token(&self) -> Option<WifiTxToken> {
1880 if !self.can_send() {
1881 crate::preempt::yield_task();
1883 }
1884
1885 if self.can_send() {
1886 Some(WifiTxToken { mode: *self })
1887 } else {
1888 None
1889 }
1890 }
1891
1892 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1893 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1894 if is_empty || !self.can_send() {
1895 crate::preempt::yield_task();
1897 }
1898
1899 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1900
1901 if !is_empty {
1902 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1903 } else {
1904 None
1905 }
1906 }
1907
1908 fn interface(&self) -> wifi_interface_t {
1909 match self {
1910 WifiDeviceMode::Sta => wifi_interface_t_WIFI_IF_STA,
1911 WifiDeviceMode::Ap => wifi_interface_t_WIFI_IF_AP,
1912 }
1913 }
1914
1915 fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) {
1916 embassy::TRANSMIT_WAKER.register(cx.waker())
1917 }
1918
1919 fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) {
1920 match self {
1921 WifiDeviceMode::Sta => embassy::STA_RECEIVE_WAKER.register(cx.waker()),
1922 WifiDeviceMode::Ap => embassy::AP_RECEIVE_WAKER.register(cx.waker()),
1923 }
1924 }
1925
1926 fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) {
1927 match self {
1928 WifiDeviceMode::Sta => embassy::STA_LINK_STATE_WAKER.register(cx.waker()),
1929 WifiDeviceMode::Ap => embassy::AP_LINK_STATE_WAKER.register(cx.waker()),
1930 }
1931 }
1932
1933 fn link_state(&self) -> embassy_net_driver::LinkState {
1934 match self {
1935 WifiDeviceMode::Sta => {
1936 if matches!(sta_state(), WifiStaState::Connected) {
1937 embassy_net_driver::LinkState::Up
1938 } else {
1939 embassy_net_driver::LinkState::Down
1940 }
1941 }
1942 WifiDeviceMode::Ap => {
1943 if matches!(ap_state(), WifiApState::Started) {
1944 embassy_net_driver::LinkState::Up
1945 } else {
1946 embassy_net_driver::LinkState::Down
1947 }
1948 }
1949 }
1950 }
1951}
1952
1953pub struct WifiDevice<'d> {
1955 _phantom: PhantomData<&'d ()>,
1956 mode: WifiDeviceMode,
1957}
1958
1959impl WifiDevice<'_> {
1960 pub fn mac_address(&self) -> [u8; 6] {
1962 self.mode.mac_address()
1963 }
1964
1965 #[cfg(not(feature = "smoltcp"))]
1968 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1969 self.mode.rx_token()
1970 }
1971
1972 #[cfg(not(feature = "smoltcp"))]
1975 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1976 self.mode.tx_token()
1977 }
1978}
1979
1980fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
1981 let str_len = record
1982 .ssid
1983 .iter()
1984 .position(|&c| c == 0)
1985 .unwrap_or(record.ssid.len());
1986 let ssid_ref = unsafe { core::str::from_utf8_unchecked(&record.ssid[..str_len]) };
1987
1988 let mut ssid = String::new();
1989 ssid.push_str(ssid_ref);
1990
1991 AccessPointInfo {
1992 ssid,
1993 bssid: record.bssid,
1994 channel: record.primary,
1995 secondary_channel: match record.second {
1996 include::wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
1997 include::wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
1998 include::wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
1999 _ => panic!(),
2000 },
2001 signal_strength: record.rssi,
2002 auth_method: Some(AuthMethod::from_raw(record.authmode)),
2003 country: Country::try_from_c(&record.country),
2004 }
2005}
2006
2007#[cfg(not(esp32c6))]
2010#[derive(Debug, Clone, Copy)]
2011#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2012#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
2013pub struct RxControlInfo {
2014 pub rssi: i32,
2016 pub rate: u32,
2019 pub sig_mode: u32,
2022 pub mcs: u32,
2025 pub cwb: u32,
2027 pub smoothing: u32,
2030 pub not_sounding: u32,
2033 pub aggregation: u32,
2035 pub stbc: u32,
2038 pub fec_coding: u32,
2041 pub sgi: u32,
2044 pub ampdu_cnt: u32,
2046 pub channel: u32,
2048 pub secondary_channel: u32,
2051 pub timestamp: u32,
2054 pub noise_floor: i32,
2056 pub ant: u32,
2059 pub sig_len: u32,
2061 pub rx_state: u32,
2063}
2064
2065#[cfg(esp32c6)]
2068#[derive(Debug, Clone, Copy)]
2069#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2070#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
2071pub struct RxControlInfo {
2072 pub rssi: i32,
2074 pub rate: u32,
2077 pub sig_len: u32,
2079 pub rx_state: u32,
2082 pub dump_len: u32,
2084 pub he_sigb_len: u32,
2086 pub cur_single_mpdu: u32,
2088 pub cur_bb_format: u32,
2090 pub rx_channel_estimate_info_vld: u32,
2092 pub rx_channel_estimate_len: u32,
2094 pub second: u32,
2096 pub channel: u32,
2098 pub noise_floor: i32,
2100 pub is_group: u32,
2102 pub rxend_state: u32,
2104 pub rxmatch3: u32,
2106 pub rxmatch2: u32,
2108 pub rxmatch1: u32,
2110 pub rxmatch0: u32,
2112}
2113
2114#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
2115impl RxControlInfo {
2116 pub unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
2122 #[cfg(not(esp32c6))]
2123 let rx_control_info = unsafe {
2124 RxControlInfo {
2125 rssi: (*rx_cntl).rssi(),
2126 rate: (*rx_cntl).rate(),
2127 sig_mode: (*rx_cntl).sig_mode(),
2128 mcs: (*rx_cntl).mcs(),
2129 cwb: (*rx_cntl).cwb(),
2130 smoothing: (*rx_cntl).smoothing(),
2131 not_sounding: (*rx_cntl).not_sounding(),
2132 aggregation: (*rx_cntl).aggregation(),
2133 stbc: (*rx_cntl).stbc(),
2134 fec_coding: (*rx_cntl).fec_coding(),
2135 sgi: (*rx_cntl).sgi(),
2136 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
2137 channel: (*rx_cntl).channel(),
2138 secondary_channel: (*rx_cntl).secondary_channel(),
2139 timestamp: (*rx_cntl).timestamp(),
2140 noise_floor: (*rx_cntl).noise_floor(),
2141 ant: (*rx_cntl).ant(),
2142 sig_len: (*rx_cntl).sig_len(),
2143 rx_state: (*rx_cntl).rx_state(),
2144 }
2145 };
2146 #[cfg(esp32c6)]
2147 let rx_control_info = unsafe {
2148 RxControlInfo {
2149 rssi: (*rx_cntl).rssi(),
2150 rate: (*rx_cntl).rate(),
2151 sig_len: (*rx_cntl).sig_len(),
2152 rx_state: (*rx_cntl).rx_state(),
2153 dump_len: (*rx_cntl).dump_len(),
2154 he_sigb_len: (*rx_cntl).he_sigb_len(),
2155 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
2156 cur_bb_format: (*rx_cntl).cur_bb_format(),
2157 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
2158 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
2159 second: (*rx_cntl).second(),
2160 channel: (*rx_cntl).channel(),
2161 noise_floor: (*rx_cntl).noise_floor(),
2162 is_group: (*rx_cntl).is_group(),
2163 rxend_state: (*rx_cntl).rxend_state(),
2164 rxmatch3: (*rx_cntl).rxmatch3(),
2165 rxmatch2: (*rx_cntl).rxmatch2(),
2166 rxmatch1: (*rx_cntl).rxmatch1(),
2167 rxmatch0: (*rx_cntl).rxmatch0(),
2168 }
2169 };
2170 rx_control_info
2171 }
2172}
2173#[cfg(all(feature = "sniffer", feature = "unstable"))]
2175#[instability::unstable]
2176pub struct PromiscuousPkt<'a> {
2177 pub rx_cntl: RxControlInfo,
2179 pub frame_type: wifi_promiscuous_pkt_type_t,
2181 pub len: usize,
2183 pub data: &'a [u8],
2185}
2186#[cfg(all(feature = "sniffer", feature = "unstable"))]
2187#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
2188impl PromiscuousPkt<'_> {
2189 pub(crate) unsafe fn from_raw(
2193 buf: *const wifi_promiscuous_pkt_t,
2194 frame_type: wifi_promiscuous_pkt_type_t,
2195 ) -> Self {
2196 let rx_cntl = unsafe { RxControlInfo::from_raw(&(*buf).rx_ctrl) };
2197 let len = rx_cntl.sig_len as usize;
2198 PromiscuousPkt {
2199 rx_cntl,
2200 frame_type,
2201 len,
2202 data: unsafe {
2203 core::slice::from_raw_parts(
2204 (buf as *const u8).add(core::mem::size_of::<wifi_pkt_rx_ctrl_t>()),
2205 len,
2206 )
2207 },
2208 }
2209 }
2210}
2211
2212#[cfg(all(feature = "sniffer", feature = "unstable"))]
2213static SNIFFER_CB: NonReentrantMutex<Option<fn(PromiscuousPkt<'_>)>> = NonReentrantMutex::new(None);
2214
2215#[cfg(all(feature = "sniffer", feature = "unstable"))]
2216unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) {
2217 unsafe {
2218 if let Some(sniffer_callback) = SNIFFER_CB.with(|callback| *callback) {
2219 let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type);
2220 sniffer_callback(promiscuous_pkt);
2221 }
2222 }
2223}
2224
2225#[cfg(all(feature = "sniffer", feature = "unstable"))]
2226#[instability::unstable]
2227#[non_exhaustive]
2229pub struct Sniffer<'d> {
2230 _phantom: PhantomData<&'d ()>,
2231}
2232
2233#[cfg(all(feature = "sniffer", feature = "unstable"))]
2234impl Sniffer<'_> {
2235 pub(crate) fn new() -> Self {
2236 unwrap!(esp_wifi_result!(unsafe {
2239 esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb))
2240 }));
2241 Self {
2242 _phantom: PhantomData,
2243 }
2244 }
2245 #[instability::unstable]
2247 pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
2248 esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
2249 Ok(())
2250 }
2251 #[instability::unstable]
2253 pub fn send_raw_frame(
2254 &mut self,
2255 use_sta_interface: bool,
2256 buffer: &[u8],
2257 use_internal_seq_num: bool,
2258 ) -> Result<(), WifiError> {
2259 esp_wifi_result!(unsafe {
2260 esp_wifi_80211_tx(
2261 if use_sta_interface {
2262 wifi_interface_t_WIFI_IF_STA
2263 } else {
2264 wifi_interface_t_WIFI_IF_AP
2265 } as wifi_interface_t,
2266 buffer.as_ptr() as *const _,
2267 buffer.len() as i32,
2268 use_internal_seq_num,
2269 )
2270 })
2271 }
2272 #[instability::unstable]
2274 pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt<'_>)) {
2275 SNIFFER_CB.with(|callback| *callback = Some(cb));
2276 }
2277}
2278
2279#[cfg(all(feature = "smoltcp", feature = "unstable"))]
2281#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
2282impl Device for WifiDevice<'_> {
2283 type RxToken<'a>
2284 = WifiRxToken
2285 where
2286 Self: 'a;
2287 type TxToken<'a>
2288 = WifiTxToken
2289 where
2290 Self: 'a;
2291
2292 fn receive(
2293 &mut self,
2294 _instant: smoltcp::time::Instant,
2295 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2296 self.mode.rx_token()
2297 }
2298
2299 fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
2300 self.mode.tx_token()
2301 }
2302
2303 fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
2304 let mut caps = DeviceCapabilities::default();
2305 caps.max_transmission_unit = MTU;
2306 caps.max_burst_size = if esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE") == 0
2307 {
2308 None
2309 } else {
2310 Some(esp_config_int!(
2311 usize,
2312 "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE"
2313 ))
2314 };
2315 caps
2316 }
2317}
2318
2319#[doc(hidden)]
2320#[derive(Debug)]
2321pub struct WifiRxToken {
2322 mode: WifiDeviceMode,
2323}
2324
2325impl WifiRxToken {
2326 pub fn consume_token<R, F>(self, f: F) -> R
2329 where
2330 F: FnOnce(&mut [u8]) -> R,
2331 {
2332 let mut data = self.mode.data_queue_rx().with(|queue| {
2333 unwrap!(
2334 queue.pop_front(),
2335 "unreachable: transmit()/receive() ensures there is a packet to process"
2336 )
2337 });
2338
2339 let buffer = data.as_slice_mut();
2346 dump_packet_info(buffer);
2347
2348 f(buffer)
2349 }
2350}
2351
2352#[cfg(all(feature = "smoltcp", feature = "unstable"))]
2353#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
2354impl RxToken for WifiRxToken {
2355 fn consume<R, F>(self, f: F) -> R
2356 where
2357 F: FnOnce(&[u8]) -> R,
2358 {
2359 self.consume_token(|t| f(t))
2360 }
2361}
2362
2363#[doc(hidden)]
2364#[derive(Debug)]
2365pub struct WifiTxToken {
2366 mode: WifiDeviceMode,
2367}
2368
2369impl WifiTxToken {
2370 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
2373 where
2374 F: FnOnce(&mut [u8]) -> R,
2375 {
2376 self.mode.increase_in_flight_counter();
2377
2378 static mut BUFFER: [u8; MTU] = [0u8; MTU];
2382
2383 let buffer = unsafe { &mut BUFFER[..len] };
2384
2385 let res = f(buffer);
2386
2387 esp_wifi_send_data(self.mode.interface(), buffer);
2388
2389 res
2390 }
2391}
2392
2393#[cfg(all(feature = "smoltcp", feature = "unstable"))]
2394#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
2395impl TxToken for WifiTxToken {
2396 fn consume<R, F>(self, len: usize, f: F) -> R
2397 where
2398 F: FnOnce(&mut [u8]) -> R,
2399 {
2400 self.consume_token(len, f)
2401 }
2402}
2403
2404pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
2409 trace!("sending... {} bytes", data.len());
2410 dump_packet_info(data);
2411
2412 let len = data.len() as u16;
2413 let ptr = data.as_mut_ptr().cast();
2414
2415 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
2416
2417 if res != 0 {
2418 warn!("esp_wifi_internal_tx {}", res);
2419 decrement_inflight_counter();
2420 } else {
2421 trace!("esp_wifi_internal_tx ok");
2422 }
2423}
2424
2425fn dump_packet_info(_buffer: &mut [u8]) {
2426 #[cfg(dump_packets)]
2427 {
2428 info!("@WIFIFRAME {:?}", _buffer);
2429 }
2430}
2431
2432#[doc(hidden)]
2433#[macro_export]
2434macro_rules! esp_wifi_result {
2435 ($value:expr) => {{
2436 use num_traits::FromPrimitive;
2437 let result = $value;
2438 if result != esp_wifi_sys::include::ESP_OK as i32 {
2439 warn!("{} returned an error: {}", stringify!($value), result);
2440 Err(WifiError::InternalError(unwrap!(FromPrimitive::from_i32(
2441 result
2442 ))))
2443 } else {
2444 Ok::<(), WifiError>(())
2445 }
2446 }};
2447}
2448
2449pub(crate) mod embassy {
2450 use embassy_net_driver::{Capabilities, Driver, HardwareAddress, RxToken, TxToken};
2451 use esp_hal::asynch::AtomicWaker;
2452
2453 use super::*;
2454
2455 pub(crate) static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
2458
2459 pub(crate) static AP_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2460 pub(crate) static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2461
2462 pub(crate) static STA_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
2463 pub(crate) static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2464
2465 impl RxToken for WifiRxToken {
2466 fn consume<R, F>(self, f: F) -> R
2467 where
2468 F: FnOnce(&mut [u8]) -> R,
2469 {
2470 self.consume_token(f)
2471 }
2472 }
2473
2474 impl TxToken for WifiTxToken {
2475 fn consume<R, F>(self, len: usize, f: F) -> R
2476 where
2477 F: FnOnce(&mut [u8]) -> R,
2478 {
2479 self.consume_token(len, f)
2480 }
2481 }
2482
2483 impl Driver for WifiDevice<'_> {
2484 type RxToken<'a>
2485 = WifiRxToken
2486 where
2487 Self: 'a;
2488 type TxToken<'a>
2489 = WifiTxToken
2490 where
2491 Self: 'a;
2492
2493 fn receive(
2494 &mut self,
2495 cx: &mut core::task::Context<'_>,
2496 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2497 self.mode.register_receive_waker(cx);
2498 self.mode.register_transmit_waker(cx);
2499 self.mode.rx_token()
2500 }
2501
2502 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
2503 self.mode.register_transmit_waker(cx);
2504 self.mode.tx_token()
2505 }
2506
2507 fn link_state(
2508 &mut self,
2509 cx: &mut core::task::Context<'_>,
2510 ) -> embassy_net_driver::LinkState {
2511 self.mode.register_link_state_waker(cx);
2512 self.mode.link_state()
2513 }
2514
2515 fn capabilities(&self) -> Capabilities {
2516 let mut caps = Capabilities::default();
2517 caps.max_transmission_unit = MTU;
2518 caps.max_burst_size =
2519 if esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE") == 0 {
2520 None
2521 } else {
2522 Some(esp_config_int!(
2523 usize,
2524 "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE"
2525 ))
2526 };
2527 caps
2528 }
2529
2530 fn hardware_address(&self) -> HardwareAddress {
2531 HardwareAddress::Ethernet(self.mac_address())
2532 }
2533 }
2534}
2535
2536#[non_exhaustive]
2538#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
2539#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2540pub enum PowerSaveMode {
2541 #[default]
2543 None,
2544 Minimum,
2547 Maximum,
2550}
2551
2552pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
2553 esp_wifi_result!(unsafe {
2554 esp_wifi_sys::include::esp_wifi_set_ps(match ps {
2555 PowerSaveMode::None => esp_wifi_sys::include::wifi_ps_type_t_WIFI_PS_NONE,
2556 PowerSaveMode::Minimum => esp_wifi_sys::include::wifi_ps_type_t_WIFI_PS_MIN_MODEM,
2557 PowerSaveMode::Maximum => esp_wifi_sys::include::wifi_ps_type_t_WIFI_PS_MAX_MODEM,
2558 })
2559 })?;
2560 Ok(())
2561}
2562
2563struct FreeApListOnDrop;
2564impl FreeApListOnDrop {
2565 pub fn defuse(self) {
2566 core::mem::forget(self);
2567 }
2568}
2569
2570impl Drop for FreeApListOnDrop {
2571 fn drop(&mut self) {
2572 unsafe {
2573 include::esp_wifi_clear_ap_list();
2574 }
2575 }
2576}
2577
2578#[non_exhaustive]
2580pub struct Interfaces<'d> {
2581 pub sta: WifiDevice<'d>,
2583 pub ap: WifiDevice<'d>,
2585 #[cfg(all(feature = "esp-now", feature = "unstable"))]
2587 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
2588 pub esp_now: crate::esp_now::EspNow<'d>,
2589 #[cfg(all(feature = "sniffer", feature = "unstable"))]
2591 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
2592 pub sniffer: Sniffer<'d>,
2593}
2594
2595#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
2599#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2600#[instability::unstable]
2601pub enum OperatingClass {
2602 AllEnvironments,
2605
2606 Outdoors,
2608
2609 Indoors,
2611
2612 NonCountryEntity,
2615
2616 Repr(u8),
2619}
2620
2621impl Default for OperatingClass {
2622 fn default() -> Self {
2623 OperatingClass::Repr(0) }
2625}
2626
2627impl OperatingClass {
2628 fn into_code(self) -> u8 {
2629 match self {
2630 OperatingClass::AllEnvironments => b' ',
2631 OperatingClass::Outdoors => b'O',
2632 OperatingClass::Indoors => b'I',
2633 OperatingClass::NonCountryEntity => b'X',
2634 OperatingClass::Repr(code) => code,
2635 }
2636 }
2637}
2638
2639#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BuilderLite)]
2656#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2657pub struct CountryInfo {
2658 #[builder_lite(skip)]
2660 country: [u8; 2],
2661
2662 #[builder_lite(unstable)]
2664 operating_class: OperatingClass,
2665}
2666
2667impl From<[u8; 2]> for CountryInfo {
2668 fn from(country: [u8; 2]) -> Self {
2669 Self {
2670 country,
2671 operating_class: OperatingClass::default(),
2672 }
2673 }
2674}
2675
2676impl CountryInfo {
2677 fn into_blob(self) -> wifi_country_t {
2678 wifi_country_t {
2679 cc: [
2680 self.country[0],
2681 self.country[1],
2682 self.operating_class.into_code(),
2683 ],
2684 schan: 1,
2686 nchan: 13,
2687 max_tx_power: 20,
2688 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
2689 }
2690 }
2691}
2692
2693#[derive(Clone, Copy, BuilderLite, Debug)]
2695#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2696pub struct Config {
2697 power_save_mode: PowerSaveMode,
2699
2700 #[builder_lite(into)]
2702 #[builder_lite(unstable)]
2703 country_code: CountryInfo,
2704
2705 #[builder_lite(unstable)]
2707 rx_queue_size: usize,
2708 #[builder_lite(unstable)]
2710 tx_queue_size: usize,
2711
2712 #[builder_lite(unstable)]
2722 static_rx_buf_num: u8,
2723
2724 #[builder_lite(unstable)]
2741 dynamic_rx_buf_num: u16,
2742
2743 #[builder_lite(unstable)]
2755 static_tx_buf_num: u8,
2756
2757 #[builder_lite(unstable)]
2768 dynamic_tx_buf_num: u16,
2769
2770 #[builder_lite(unstable)]
2772 ampdu_rx_enable: bool,
2773
2774 #[builder_lite(unstable)]
2776 ampdu_tx_enable: bool,
2777
2778 #[builder_lite(unstable)]
2780 amsdu_tx_enable: bool,
2781
2782 #[builder_lite(unstable)]
2793 rx_ba_win: u8,
2794}
2795
2796impl Default for Config {
2797 fn default() -> Self {
2798 Self {
2799 power_save_mode: PowerSaveMode::default(),
2800 country_code: CountryInfo::from(*b"CN"),
2801
2802 rx_queue_size: 5,
2803 tx_queue_size: 3,
2804
2805 static_rx_buf_num: 10,
2806 dynamic_rx_buf_num: 32,
2807
2808 static_tx_buf_num: 0,
2809 dynamic_tx_buf_num: 32,
2810
2811 ampdu_rx_enable: true,
2812 ampdu_tx_enable: true,
2813 amsdu_tx_enable: false,
2814
2815 rx_ba_win: 6,
2816 }
2817 }
2818}
2819
2820impl Config {
2821 fn validate(&self) {
2822 if self.rx_ba_win as u16 >= self.dynamic_rx_buf_num {
2823 warn!("RX BA window size should be less than the number of dynamic RX buffers.");
2824 }
2825 if self.rx_ba_win as u16 >= 2 * (self.static_rx_buf_num as u16) {
2826 warn!("RX BA window size should be less than twice the number of static RX buffers.");
2827 }
2828 }
2829}
2830
2831pub fn new<'d>(
2838 _inited: &'d Controller<'d>,
2839 device: crate::hal::peripherals::WIFI<'d>,
2840 config: Config,
2841) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> {
2842 if crate::is_interrupts_disabled() {
2843 return Err(WifiError::Unsupported);
2844 }
2845
2846 config.validate();
2847
2848 unsafe {
2849 internal::G_CONFIG = wifi_init_config_t {
2850 osi_funcs: (&raw const internal::__ESP_RADIO_G_WIFI_OSI_FUNCS).cast_mut(),
2851
2852 wpa_crypto_funcs: g_wifi_default_wpa_crypto_funcs,
2853 static_rx_buf_num: config.static_rx_buf_num as _,
2854 dynamic_rx_buf_num: config.dynamic_rx_buf_num as _,
2855 tx_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_TX_BUFFER_TYPE as i32,
2856 static_tx_buf_num: config.static_tx_buf_num as _,
2857 dynamic_tx_buf_num: config.dynamic_tx_buf_num as _,
2858 rx_mgmt_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as i32,
2859 rx_mgmt_buf_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF as i32,
2860 cache_tx_buf_num: esp_wifi_sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32,
2861 csi_enable: cfg!(feature = "csi") as i32,
2862 ampdu_rx_enable: config.ampdu_rx_enable as _,
2863 ampdu_tx_enable: config.ampdu_tx_enable as _,
2864 amsdu_tx_enable: config.amsdu_tx_enable as _,
2865 nvs_enable: 0,
2866 nano_enable: 0,
2867 rx_ba_win: config.rx_ba_win as _,
2868 wifi_task_core_id: Cpu::current() as _,
2869 beacon_max_len: esp_wifi_sys::include::WIFI_SOFTAP_BEACON_MAX_LEN as i32,
2870 mgmt_sbuf_num: esp_wifi_sys::include::WIFI_MGMT_SBUF_NUM as i32,
2871 feature_caps: internal::__ESP_RADIO_G_WIFI_FEATURE_CAPS,
2872 sta_disconnected_pm: false,
2873 espnow_max_encrypt_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM
2874 as i32,
2875
2876 tx_hetb_queue_num: 3,
2877 dump_hesigb_enable: false,
2878
2879 magic: WIFI_INIT_CONFIG_MAGIC as i32,
2880 };
2881
2882 RX_QUEUE_SIZE.store(config.rx_queue_size, Ordering::Relaxed);
2883 TX_QUEUE_SIZE.store(config.tx_queue_size, Ordering::Relaxed);
2884 };
2885
2886 crate::wifi::wifi_init(device)?;
2887
2888 unsafe {
2889 let country = config.country_code.into_blob();
2890 esp_wifi_result!(esp_wifi_set_country(&country))?;
2891 }
2892
2893 unsafe { esp_hal::rng::TrngSource::increase_entropy_source_counter() };
2895
2896 let mut controller = WifiController {
2899 _phantom: Default::default(),
2900 beacon_timeout: 6,
2901 ap_beacon_timeout: 100,
2902 };
2903
2904 controller.set_power_saving(config.power_save_mode)?;
2905
2906 Ok((
2907 controller,
2908 Interfaces {
2909 sta: WifiDevice {
2910 _phantom: Default::default(),
2911 mode: WifiDeviceMode::Sta,
2912 },
2913 ap: WifiDevice {
2914 _phantom: Default::default(),
2915 mode: WifiDeviceMode::Ap,
2916 },
2917 #[cfg(all(feature = "esp-now", feature = "unstable"))]
2918 esp_now: crate::esp_now::EspNow::new_internal(),
2919 #[cfg(all(feature = "sniffer", feature = "unstable"))]
2920 sniffer: Sniffer::new(),
2921 },
2922 ))
2923}
2924
2925#[non_exhaustive]
2927pub struct WifiController<'d> {
2928 _phantom: PhantomData<&'d ()>,
2929 beacon_timeout: u16,
2931 ap_beacon_timeout: u16,
2932}
2933
2934impl Drop for WifiController<'_> {
2935 fn drop(&mut self) {
2936 if let Err(e) = crate::wifi::wifi_deinit() {
2937 warn!("Failed to cleanly deinit wifi: {:?}", e);
2938 }
2939
2940 esp_hal::rng::TrngSource::decrease_entropy_source_counter(unsafe {
2941 esp_hal::Internal::conjure()
2942 });
2943 }
2944}
2945
2946impl WifiController<'_> {
2947 #[cfg(feature = "csi")]
2949 #[instability::unstable]
2950 pub fn set_csi(
2951 &mut self,
2952 mut csi: CsiConfig,
2953 cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send,
2954 ) -> Result<(), WifiError> {
2955 csi.apply_config()?;
2956 csi.set_receive_cb(cb)?;
2957 csi.set_csi(true)?;
2958
2959 Ok(())
2960 }
2961
2962 pub fn set_protocol(&mut self, protocols: EnumSet<Protocol>) -> Result<(), WifiError> {
2979 let protocol = protocols
2980 .into_iter()
2981 .map(|v| match v {
2982 Protocol::P802D11B => WIFI_PROTOCOL_11B,
2983 Protocol::P802D11BG => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G,
2984 Protocol::P802D11BGN => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
2985 Protocol::P802D11BGNLR => {
2986 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR
2987 }
2988 Protocol::P802D11LR => WIFI_PROTOCOL_LR,
2989 Protocol::P802D11BGNAX => {
2990 WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX
2991 }
2992 })
2993 .fold(0, |combined, protocol| combined | protocol) as u8;
2994
2995 let mode = self.mode()?;
2996 if mode.is_sta() {
2997 esp_wifi_result!(unsafe {
2998 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_STA, protocol)
2999 })?;
3000 }
3001 if mode.is_ap() {
3002 esp_wifi_result!(unsafe {
3003 esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_AP, protocol)
3004 })?;
3005 }
3006
3007 Ok(())
3008 }
3009
3010 fn apply_protocols(
3011 iface: wifi_interface_t,
3012 protocols: &EnumSet<Protocol>,
3013 ) -> Result<(), WifiError> {
3014 let mask = protocols.iter().fold(0, |acc, p| acc | p.to_mask());
3015 debug!("Setting protocols with mask {:b}", mask);
3016 esp_wifi_result!(unsafe { esp_wifi_set_protocol(iface, mask as u8) })
3017 }
3018
3019 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
3021 apply_power_saving(ps)
3022 }
3023
3024 pub fn scan_with_config(
3026 &mut self,
3027 config: ScanConfig<'_>,
3028 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
3029 esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?;
3030 self.scan_results(config.max.unwrap_or(usize::MAX))
3031 }
3032
3033 fn scan_results(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
3034 let mut scanned = alloc::vec::Vec::<AccessPointInfo>::new();
3035 let mut bss_total: u16 = max as u16;
3036
3037 let guard = FreeApListOnDrop;
3039
3040 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
3041
3042 guard.defuse();
3043
3044 let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
3045 for _ in 0..usize::min(bss_total as usize, max) {
3046 let record = unsafe { MaybeUninit::assume_init_mut(&mut record) };
3047 unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_record(record))? };
3048 let ap_info = convert_ap_info(record);
3049 scanned.push(ap_info);
3050 }
3051
3052 unsafe { esp_wifi_result!(include::esp_wifi_clear_ap_list())? };
3053
3054 Ok(scanned)
3055 }
3056
3057 pub fn start(&mut self) -> Result<(), WifiError> {
3062 unsafe {
3063 esp_wifi_result!(esp_wifi_start())?;
3064
3065 let mode = WifiMode::current()?;
3066
3067 if mode.is_ap() {
3069 esp_wifi_result!(include::esp_wifi_set_inactive_time(
3070 wifi_interface_t_WIFI_IF_AP,
3071 self.ap_beacon_timeout
3072 ))?;
3073 }
3074 if mode.is_sta() {
3075 esp_wifi_result!(include::esp_wifi_set_inactive_time(
3076 wifi_interface_t_WIFI_IF_STA,
3077 self.beacon_timeout
3078 ))?;
3079 }
3080 }
3081
3082 Ok(())
3083 }
3084
3085 pub fn stop(&mut self) -> Result<(), WifiError> {
3090 self.stop_impl()
3091 }
3092
3093 pub fn connect(&mut self) -> Result<(), WifiError> {
3104 self.connect_impl()
3105 }
3106
3107 pub fn disconnect(&mut self) -> Result<(), WifiError> {
3112 self.disconnect_impl()
3113 }
3114
3115 pub fn rssi(&self) -> Result<i32, WifiError> {
3128 if self.mode()?.is_sta() {
3129 let mut rssi: i32 = 0;
3130 esp_wifi_result!(unsafe { esp_wifi_sta_get_rssi(&mut rssi) })?;
3132 Ok(rssi)
3133 } else {
3134 Err(WifiError::Unsupported)
3135 }
3136 }
3137
3138 pub fn capabilities(&self) -> Result<EnumSet<crate::wifi::Capability>, WifiError> {
3140 let caps =
3141 enumset::enum_set! { Capability::Client | Capability::AccessPoint | Capability::ApSta };
3142
3143 Ok(caps)
3144 }
3145
3146 pub fn set_config(&mut self, conf: &ModeConfig) -> Result<(), WifiError> {
3156 conf.validate()?;
3157
3158 let mode = match conf {
3159 ModeConfig::None => wifi_mode_t_WIFI_MODE_NULL,
3160 ModeConfig::Client(_) => wifi_mode_t_WIFI_MODE_STA,
3161 ModeConfig::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
3162 ModeConfig::ApSta(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
3163 #[cfg(feature = "wifi-eap")]
3164 ModeConfig::EapClient(_) => wifi_mode_t_WIFI_MODE_STA,
3165 };
3166
3167 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
3168
3169 match conf {
3170 ModeConfig::None => Ok(()),
3171 ModeConfig::Client(config) => {
3172 self.apply_sta_config(config)?;
3173 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)
3174 }
3175 ModeConfig::AccessPoint(config) => {
3176 self.apply_ap_config(config)?;
3177 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &config.protocols)
3178 }
3179 ModeConfig::ApSta(sta_config, ap_config) => {
3180 self.apply_ap_config(ap_config)?;
3181 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &ap_config.protocols)?;
3182 self.apply_sta_config(sta_config)?;
3183 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &sta_config.protocols)
3184 }
3185 #[cfg(feature = "wifi-eap")]
3186 ModeConfig::EapClient(config) => {
3187 self.apply_sta_eap_config(config)?;
3188 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)
3189 }
3190 }
3191 .inspect_err(|_| {
3192 unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL) };
3196 })?;
3197
3198 Ok(())
3199 }
3200
3201 pub fn set_mode(&mut self, mode: WifiMode) -> Result<(), WifiError> {
3205 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?;
3206 Ok(())
3207 }
3208
3209 fn stop_impl(&mut self) -> Result<(), WifiError> {
3210 esp_wifi_result!(unsafe { esp_wifi_stop() })
3211 }
3212
3213 fn connect_impl(&mut self) -> Result<(), WifiError> {
3214 esp_wifi_result!(unsafe { esp_wifi_connect_internal() })
3216 }
3217
3218 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
3219 esp_wifi_result!(unsafe { esp_wifi_disconnect_internal() })
3221 }
3222
3223 pub fn is_started(&self) -> Result<bool, WifiError> {
3228 if matches!(
3229 crate::wifi::sta_state(),
3230 WifiStaState::Started | WifiStaState::Connected | WifiStaState::Disconnected
3231 ) {
3232 return Ok(true);
3233 }
3234 if matches!(crate::wifi::ap_state(), WifiApState::Started) {
3235 return Ok(true);
3236 }
3237 Ok(false)
3238 }
3239
3240 pub fn is_connected(&self) -> Result<bool, WifiError> {
3245 match crate::wifi::sta_state() {
3246 crate::wifi::WifiStaState::Connected => Ok(true),
3247 crate::wifi::WifiStaState::Disconnected => Err(WifiError::Disconnected),
3248 _ => Ok(false),
3250 }
3251 }
3252
3253 fn mode(&self) -> Result<WifiMode, WifiError> {
3254 WifiMode::current()
3255 }
3256
3257 pub async fn scan_with_config_async(
3259 &mut self,
3260 config: ScanConfig<'_>,
3261 ) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
3262 Self::clear_events(WifiEvent::ScanDone);
3263 esp_wifi_result!(wifi_start_scan(false, config))?;
3264
3265 let guard = FreeApListOnDrop;
3267 WifiEventFuture::new(WifiEvent::ScanDone).await;
3268
3269 guard.defuse();
3270
3271 let result = self.scan_results(config.max.unwrap_or(usize::MAX))?;
3272
3273 Ok(result)
3274 }
3275
3276 pub async fn start_async(&mut self) -> Result<(), WifiError> {
3280 let mut events = enumset::enum_set! {};
3281
3282 let mode = self.mode()?;
3283 if mode.is_ap() {
3284 events |= WifiEvent::ApStart;
3285 }
3286 if mode.is_sta() {
3287 events |= WifiEvent::StaStart;
3288 }
3289
3290 Self::clear_events(events);
3291
3292 self.start()?;
3293
3294 self.wait_for_all_events(events, false).await;
3295
3296 Ok(())
3297 }
3298
3299 pub async fn stop_async(&mut self) -> Result<(), WifiError> {
3303 let mut events = enumset::enum_set! {};
3304
3305 let mode = self.mode()?;
3306 if mode.is_ap() {
3307 events |= WifiEvent::ApStop;
3308 }
3309 if mode.is_sta() {
3310 events |= WifiEvent::StaStop;
3311 }
3312
3313 Self::clear_events(events);
3314
3315 self.stop_impl()?;
3316
3317 self.wait_for_all_events(events, false).await;
3318
3319 reset_ap_state();
3320 reset_sta_state();
3321
3322 Ok(())
3323 }
3324
3325 pub async fn connect_async(&mut self) -> Result<(), WifiError> {
3329 Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected);
3330
3331 let err = self.connect_impl().err();
3332
3333 if MultiWifiEventFuture::new(WifiEvent::StaConnected | WifiEvent::StaDisconnected)
3334 .await
3335 .contains(WifiEvent::StaDisconnected)
3336 {
3337 Err(err.unwrap_or(WifiError::Disconnected))
3338 } else {
3339 Ok(())
3340 }
3341 }
3342
3343 pub async fn disconnect_async(&mut self) -> Result<(), WifiError> {
3347 if !matches!(self.is_connected(), Ok(true)) {
3351 return Ok(());
3352 }
3353
3354 Self::clear_events(WifiEvent::StaDisconnected);
3355 self.disconnect_impl()?;
3356 WifiEventFuture::new(WifiEvent::StaDisconnected).await;
3357
3358 Ok(())
3359 }
3360
3361 fn clear_events(events: impl Into<EnumSet<WifiEvent>>) {
3362 WIFI_EVENTS.with(|evts| evts.remove_all(events.into()));
3363 }
3364
3365 pub async fn wait_for_event(&mut self, event: WifiEvent) {
3367 Self::clear_events(event);
3368 WifiEventFuture::new(event).await
3369 }
3370
3371 pub async fn wait_for_events(
3374 &mut self,
3375 events: EnumSet<WifiEvent>,
3376 clear_pending: bool,
3377 ) -> EnumSet<WifiEvent> {
3378 if clear_pending {
3379 Self::clear_events(events);
3380 }
3381 MultiWifiEventFuture::new(events).await
3382 }
3383
3384 pub async fn wait_for_all_events(
3386 &mut self,
3387 mut events: EnumSet<WifiEvent>,
3388 clear_pending: bool,
3389 ) {
3390 if clear_pending {
3391 Self::clear_events(events);
3392 }
3393
3394 while !events.is_empty() {
3395 let fired = MultiWifiEventFuture::new(events).await;
3396 events -= fired;
3397 }
3398 }
3399
3400 fn apply_ap_config(&mut self, config: &AccessPointConfig) -> Result<(), WifiError> {
3401 self.ap_beacon_timeout = config.beacon_timeout;
3402
3403 let mut cfg = wifi_config_t {
3404 ap: wifi_ap_config_t {
3405 ssid: [0; 32],
3406 password: [0; 64],
3407 ssid_len: 0,
3408 channel: config.channel,
3409 authmode: config.auth_method.to_raw(),
3410 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
3411 max_connection: config.max_connections as u8,
3412 beacon_interval: 100,
3413 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
3414 ftm_responder: false,
3415 pmf_cfg: wifi_pmf_config_t {
3416 capable: true,
3417 required: false,
3418 },
3419 sae_pwe_h2e: 0,
3420 csa_count: 3,
3421 dtim_period: config.dtim_period,
3422 transition_disable: 0,
3423 sae_ext: 0,
3424 bss_max_idle_cfg: include::wifi_bss_max_idle_config_t {
3425 period: 0,
3426 protected_keep_alive: false,
3427 },
3428 gtk_rekey_interval: 0,
3429 },
3430 };
3431
3432 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
3433 return Err(WifiError::InternalError(InternalWifiError::InvalidArg));
3434 }
3435
3436 unsafe {
3437 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3438 cfg.ap.ssid_len = config.ssid.len() as u8;
3439 cfg.ap.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
3440
3441 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
3442 }
3443 }
3444
3445 fn apply_sta_config(&mut self, config: &ClientConfig) -> Result<(), WifiError> {
3446 self.beacon_timeout = config.beacon_timeout;
3447
3448 let mut cfg = wifi_config_t {
3449 sta: wifi_sta_config_t {
3450 ssid: [0; 32],
3451 password: [0; 64],
3452 scan_method: config.scan_method as c_uint,
3453 bssid_set: config.bssid.is_some(),
3454 bssid: config.bssid.unwrap_or_default(),
3455 channel: config.channel.unwrap_or(0),
3456 listen_interval: config.listen_interval,
3457 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3458 threshold: wifi_scan_threshold_t {
3459 rssi: -99,
3460 authmode: config.auth_method.to_raw(),
3461 rssi_5g_adjustment: 0,
3462 },
3463 pmf_cfg: wifi_pmf_config_t {
3464 capable: true,
3465 required: false,
3466 },
3467 sae_pwe_h2e: 3,
3468 _bitfield_align_1: [0; 0],
3469 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3470 failure_retry_cnt: config.failure_retry_cnt,
3471 _bitfield_align_2: [0; 0],
3472 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3473 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3475 },
3476 };
3477
3478 if config.auth_method == AuthMethod::None && !config.password.is_empty() {
3479 return Err(WifiError::InternalError(InternalWifiError::InvalidArg));
3480 }
3481
3482 unsafe {
3483 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3484 cfg.sta.password[0..(config.password.len())]
3485 .copy_from_slice(config.password.as_bytes());
3486
3487 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
3488 }
3489 }
3490
3491 #[cfg(feature = "wifi-eap")]
3492 fn apply_sta_eap_config(&mut self, config: &EapClientConfig) -> Result<(), WifiError> {
3493 self.beacon_timeout = config.beacon_timeout;
3494
3495 let mut cfg = wifi_config_t {
3496 sta: wifi_sta_config_t {
3497 ssid: [0; 32],
3498 password: [0; 64],
3499 scan_method: config.scan_method as c_uint,
3500 bssid_set: config.bssid.is_some(),
3501 bssid: config.bssid.unwrap_or_default(),
3502 channel: config.channel.unwrap_or(0),
3503 listen_interval: config.listen_interval,
3504 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3505 threshold: wifi_scan_threshold_t {
3506 rssi: -99,
3507 authmode: config.auth_method.to_raw(),
3508 rssi_5g_adjustment: 0,
3509 },
3510 pmf_cfg: wifi_pmf_config_t {
3511 capable: true,
3512 required: false,
3513 },
3514 sae_pwe_h2e: 3,
3515 _bitfield_align_1: [0; 0],
3516 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3517 failure_retry_cnt: config.failure_retry_cnt,
3518 _bitfield_align_2: [0; 0],
3519 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3520 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3522 },
3523 };
3524
3525 unsafe {
3526 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3527 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
3528
3529 if let Some(identity) = &config.identity {
3530 esp_wifi_result!(esp_eap_client_set_identity(
3531 identity.as_str().as_ptr(),
3532 identity.len() as i32
3533 ))?;
3534 } else {
3535 esp_eap_client_clear_identity();
3536 }
3537
3538 if let Some(username) = &config.username {
3539 esp_wifi_result!(esp_eap_client_set_username(
3540 username.as_str().as_ptr(),
3541 username.len() as i32
3542 ))?;
3543 } else {
3544 esp_eap_client_clear_username();
3545 }
3546
3547 if let Some(password) = &config.password {
3548 esp_wifi_result!(esp_eap_client_set_password(
3549 password.as_str().as_ptr(),
3550 password.len() as i32
3551 ))?;
3552 } else {
3553 esp_eap_client_clear_password();
3554 }
3555
3556 if let Some(new_password) = &config.new_password {
3557 esp_wifi_result!(esp_eap_client_set_new_password(
3558 new_password.as_str().as_ptr(),
3559 new_password.len() as i32
3560 ))?;
3561 } else {
3562 esp_eap_client_clear_new_password();
3563 }
3564
3565 if let Some(pac_file) = &config.pac_file {
3566 esp_wifi_result!(esp_eap_client_set_pac_file(
3567 pac_file.as_ptr(),
3568 pac_file.len() as i32
3569 ))?;
3570 }
3571
3572 if let Some(phase2_method) = &config.ttls_phase2_method {
3573 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
3574 phase2_method.to_raw()
3575 ))?;
3576 }
3577
3578 if let Some(ca_cert) = config.ca_cert {
3579 esp_wifi_result!(esp_eap_client_set_ca_cert(
3580 ca_cert.as_ptr(),
3581 ca_cert.len() as i32
3582 ))?;
3583 } else {
3584 esp_eap_client_clear_ca_cert();
3585 }
3586
3587 if let Some((cert, key, password)) = config.certificate_and_key {
3588 let (pwd, pwd_len) = if let Some(pwd) = password {
3589 (pwd.as_ptr(), pwd.len() as i32)
3590 } else {
3591 (core::ptr::null(), 0)
3592 };
3593
3594 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
3595 cert.as_ptr(),
3596 cert.len() as i32,
3597 key.as_ptr(),
3598 key.len() as i32,
3599 pwd,
3600 pwd_len,
3601 ))?;
3602 } else {
3603 esp_eap_client_clear_certificate_and_key();
3604 }
3605
3606 if let Some(cfg) = &config.eap_fast_config {
3607 let params = esp_eap_fast_config {
3608 fast_provisioning: cfg.fast_provisioning as i32,
3609 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
3610 fast_pac_format_binary: cfg.fast_pac_format_binary,
3611 };
3612 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
3613 }
3614
3615 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
3616
3617 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
3624
3625 Ok(())
3626 }
3627 }
3628}
3629
3630impl WifiEvent {
3631 pub(crate) fn waker(&self) -> &'static AtomicWaker {
3632 static WAKER: AtomicWaker = AtomicWaker::new();
3636 &WAKER
3637 }
3638}
3639
3640#[must_use = "futures do nothing unless you `.await` or poll them"]
3641pub(crate) struct WifiEventFuture {
3642 event: WifiEvent,
3643}
3644
3645impl WifiEventFuture {
3646 pub fn new(event: WifiEvent) -> Self {
3648 Self { event }
3649 }
3650}
3651
3652impl core::future::Future for WifiEventFuture {
3653 type Output = ();
3654
3655 fn poll(
3656 self: core::pin::Pin<&mut Self>,
3657 cx: &mut core::task::Context<'_>,
3658 ) -> Poll<Self::Output> {
3659 self.event.waker().register(cx.waker());
3660 if WIFI_EVENTS.with(|events| events.remove(self.event)) {
3661 Poll::Ready(())
3662 } else {
3663 Poll::Pending
3664 }
3665 }
3666}
3667
3668#[must_use = "futures do nothing unless you `.await` or poll them"]
3669pub(crate) struct MultiWifiEventFuture {
3670 event: EnumSet<WifiEvent>,
3671}
3672
3673impl MultiWifiEventFuture {
3674 pub fn new(event: EnumSet<WifiEvent>) -> Self {
3676 Self { event }
3677 }
3678}
3679
3680impl core::future::Future for MultiWifiEventFuture {
3681 type Output = EnumSet<WifiEvent>;
3682
3683 fn poll(
3684 self: core::pin::Pin<&mut Self>,
3685 cx: &mut core::task::Context<'_>,
3686 ) -> Poll<Self::Output> {
3687 let output = WIFI_EVENTS.with(|events| {
3688 let active = events.intersection(self.event);
3689 events.remove_all(active);
3690 active
3691 });
3692 if output.is_empty() {
3693 for event in self.event.iter() {
3694 event.waker().register(cx.waker());
3695 }
3696
3697 Poll::Pending
3698 } else {
3699 Poll::Ready(output)
3700 }
3701 }
3702}