esp_radio/wifi/
ap.rs

1//! Wi-Fi access point.
2
3use alloc::string::String;
4use core::fmt;
5
6use procmacros::BuilderLite;
7
8#[cfg(feature = "unstable")]
9use super::CountryInfo;
10use super::{AuthenticationMethod, Protocols, SecondaryChannel, Ssid};
11use crate::{WifiError, sys::include::wifi_ap_record_t};
12
13/// Information about a detected Wi-Fi access point.
14#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16#[non_exhaustive]
17pub struct AccessPointInfo {
18    /// The SSID of the access point.
19    pub ssid: Ssid,
20    /// The BSSID (MAC address) of the access point.
21    pub bssid: [u8; 6],
22    /// The channel the access point is operating on.
23    pub channel: u8,
24    /// The secondary channel configuration of the access point.
25    pub secondary_channel: SecondaryChannel,
26    /// The signal strength of the access point (RSSI).
27    pub signal_strength: i8,
28    /// The authentication method used by the access point.
29    pub auth_method: Option<AuthenticationMethod>,
30    #[cfg(feature = "unstable")]
31    #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
32    /// The country information of the access point (if available from beacon frames).
33    pub country: Option<CountryInfo>,
34}
35
36/// Configuration for a Wi-Fi access point.
37#[derive(Clone, PartialEq, Eq, BuilderLite, Hash)]
38pub struct AccessPointConfig {
39    /// The SSID of the access point.
40    #[builder_lite(skip_setter)]
41    pub(crate) ssid: Ssid,
42    /// Whether the SSID is hidden or visible.
43    pub(crate) ssid_hidden: bool,
44    /// The channel the access point will operate on.
45    pub(crate) channel: u8,
46    /// The secondary channel configuration.
47    pub(crate) secondary_channel: Option<SecondaryChannel>,
48    /// The set of protocols supported by the access point.
49    pub(crate) protocols: Protocols,
50    /// The authentication method to be used by the access point.
51    pub(crate) auth_method: AuthenticationMethod,
52    /// The password for securing the access point (if applicable).
53    #[builder_lite(reference)]
54    pub(crate) password: String,
55    /// The maximum number of connections allowed on the access point.
56    pub(crate) max_connections: u16,
57    /// Dtim period of the access point (Range: 1 ~ 10).
58    pub(crate) dtim_period: u8,
59    /// Time to force deauth the station if the Soft-AccessPoint doesn't receive any data.
60    #[builder_lite(unstable)]
61    pub(crate) beacon_timeout: u16,
62}
63
64impl AccessPointConfig {
65    /// Set the SSID of the access point.
66    pub fn with_ssid(mut self, ssid: impl Into<Ssid>) -> Self {
67        self.ssid = ssid.into();
68        self
69    }
70
71    pub(crate) fn validate(&self) -> Result<(), WifiError> {
72        if self.ssid.len() > 32 {
73            return Err(WifiError::InvalidArguments);
74        }
75
76        if self.password.len() > 64 {
77            return Err(WifiError::InvalidArguments);
78        }
79
80        if !(1..=10).contains(&self.dtim_period) {
81            return Err(WifiError::InvalidArguments);
82        }
83
84        Ok(())
85    }
86}
87
88impl Default for AccessPointConfig {
89    fn default() -> Self {
90        Self {
91            ssid: "iot-device".into(),
92            ssid_hidden: false,
93            channel: 1,
94            secondary_channel: None,
95            protocols: Protocols::default(),
96            auth_method: AuthenticationMethod::None,
97            password: String::new(),
98            max_connections: 255,
99            dtim_period: 2,
100            beacon_timeout: 300,
101        }
102    }
103}
104
105impl fmt::Debug for AccessPointConfig {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        f.debug_struct("AccessPointConfig")
108            .field("ssid", &self.ssid)
109            .field("ssid_hidden", &self.ssid_hidden)
110            .field("channel", &self.channel)
111            .field("secondary_channel", &self.secondary_channel)
112            .field("protocols", &self.protocols)
113            .field("auth_method", &self.auth_method)
114            .field("password", &"**REDACTED**")
115            .field("max_connections", &self.max_connections)
116            .field("dtim_period", &self.dtim_period)
117            .field("beacon_timeout", &self.beacon_timeout)
118            .finish()
119    }
120}
121
122#[cfg(feature = "defmt")]
123impl defmt::Format for AccessPointConfig {
124    fn format(&self, fmt: defmt::Formatter<'_>) {
125        defmt::write!(
126            fmt,
127            "AccessPointConfig {{\
128            ssid: {}, \
129            ssid_hidden: {}, \
130            channel: {}, \
131            secondary_channel: {}, \
132            protocols: {}, \
133            auth_method: {}, \
134            password: **REDACTED**, \
135            max_connections: {}, \
136            dtim_period: {}, \
137            beacon_timeout: {} \
138            }}",
139            self.ssid.as_str(),
140            self.ssid_hidden,
141            self.channel,
142            self.secondary_channel,
143            self.protocols,
144            self.auth_method,
145            self.max_connections,
146            self.dtim_period,
147            self.beacon_timeout
148        );
149    }
150}
151
152#[allow(non_upper_case_globals)]
153pub(crate) fn convert_ap_info(record: &wifi_ap_record_t) -> AccessPointInfo {
154    let str_len = record
155        .ssid
156        .iter()
157        .position(|&c| c == 0)
158        .unwrap_or(record.ssid.len());
159    let ssid = Ssid::from(&record.ssid[..str_len]);
160
161    AccessPointInfo {
162        ssid,
163        bssid: record.bssid,
164        channel: record.primary,
165        secondary_channel: SecondaryChannel::from_raw(record.second),
166        signal_strength: record.rssi,
167        auth_method: Some(AuthenticationMethod::from_raw(record.authmode)),
168        #[cfg(feature = "unstable")]
169        #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
170        country: CountryInfo::try_from_c(&record.country),
171    }
172}