1use alloc::string::String;
4use core::fmt;
5
6use procmacros::BuilderLite;
7
8#[cfg(feature = "unstable")]
9use super::CountryInfo;
10use super::{AuthenticationMethod, DisconnectReason, Protocols, SecondaryChannel, Ssid};
11use crate::{WifiError, sys::include::wifi_ap_record_t};
12
13#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16#[non_exhaustive]
17pub struct AccessPointInfo {
18 pub ssid: Ssid,
20 pub bssid: [u8; 6],
22 pub channel: u8,
24 pub secondary_channel: SecondaryChannel,
26 pub signal_strength: i8,
28 pub auth_method: Option<AuthenticationMethod>,
30 #[cfg(feature = "unstable")]
31 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
32 pub country: Option<CountryInfo>,
34}
35
36#[derive(Clone, PartialEq, Eq, BuilderLite, Hash)]
38pub struct AccessPointConfig {
39 #[builder_lite(skip_setter)]
41 pub(crate) ssid: Ssid,
42 pub(crate) ssid_hidden: bool,
44 pub(crate) channel: u8,
46 pub(crate) secondary_channel: Option<SecondaryChannel>,
48 pub(crate) protocols: Protocols,
50 pub(crate) auth_method: AuthenticationMethod,
52 #[builder_lite(reference)]
54 pub(crate) password: String,
55 #[builder_lite(unstable)]
57 pub(crate) max_connections: u16,
58 #[builder_lite(unstable)]
60 pub(crate) dtim_period: u8,
61 #[builder_lite(unstable)]
63 pub(crate) beacon_timeout: u16,
64}
65
66impl AccessPointConfig {
67 pub fn with_ssid(mut self, ssid: impl Into<Ssid>) -> Self {
69 self.ssid = ssid.into();
70 self
71 }
72
73 pub(crate) fn validate(&self) -> Result<(), WifiError> {
74 if self.ssid.len() > 32 {
75 return Err(WifiError::InvalidArguments);
76 }
77
78 if self.password.len() >= 64 {
79 return Err(WifiError::InvalidArguments);
80 }
81
82 if !(1..=10).contains(&self.dtim_period) {
83 return Err(WifiError::InvalidArguments);
84 }
85
86 Ok(())
87 }
88}
89
90impl Default for AccessPointConfig {
91 fn default() -> Self {
92 Self {
93 ssid: "iot-device".into(),
94 ssid_hidden: false,
95 channel: 1,
96 secondary_channel: None,
97 protocols: Protocols::default(),
98 auth_method: AuthenticationMethod::None,
99 password: String::new(),
100 max_connections: 255,
101 dtim_period: 2,
102 beacon_timeout: 300,
103 }
104 }
105}
106
107impl fmt::Debug for AccessPointConfig {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 f.debug_struct("AccessPointConfig")
110 .field("ssid", &self.ssid)
111 .field("ssid_hidden", &self.ssid_hidden)
112 .field("channel", &self.channel)
113 .field("secondary_channel", &self.secondary_channel)
114 .field("protocols", &self.protocols)
115 .field("auth_method", &self.auth_method)
116 .field("password", &"**REDACTED**")
117 .field("max_connections", &self.max_connections)
118 .field("dtim_period", &self.dtim_period)
119 .field("beacon_timeout", &self.beacon_timeout)
120 .finish()
121 }
122}
123
124#[cfg(feature = "defmt")]
125impl defmt::Format for AccessPointConfig {
126 fn format(&self, fmt: defmt::Formatter<'_>) {
127 defmt::write!(
128 fmt,
129 "AccessPointConfig {{\
130 ssid: {}, \
131 ssid_hidden: {}, \
132 channel: {}, \
133 secondary_channel: {}, \
134 protocols: {}, \
135 auth_method: {}, \
136 password: **REDACTED**, \
137 max_connections: {}, \
138 dtim_period: {}, \
139 beacon_timeout: {} \
140 }}",
141 self.ssid.as_str(),
142 self.ssid_hidden,
143 self.channel,
144 self.secondary_channel,
145 self.protocols,
146 self.auth_method,
147 self.max_connections,
148 self.dtim_period,
149 self.beacon_timeout
150 );
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
156#[cfg_attr(feature = "defmt", derive(defmt::Format))]
157#[non_exhaustive]
158pub struct ConnectedInfo {
159 pub mac: [u8; 6],
161 pub aid: u16,
163 pub is_mesh_child: bool,
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
169#[cfg_attr(feature = "defmt", derive(defmt::Format))]
170#[non_exhaustive]
171pub struct DisconnectedInfo {
172 pub mac: [u8; 6],
174 pub aid: u16,
176 pub is_mesh_child: bool,
178 pub reason: DisconnectReason,
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
184#[cfg_attr(feature = "defmt", derive(defmt::Format))]
185pub enum EventInfo {
186 Connected(ConnectedInfo),
188 Disconnected(DisconnectedInfo),
190}
191
192#[allow(non_upper_case_globals)]
193pub(crate) fn convert_ap_info(record: &wifi_ap_record_t) -> AccessPointInfo {
194 let str_len = record
195 .ssid
196 .iter()
197 .position(|&c| c == 0)
198 .unwrap_or(record.ssid.len());
199 let ssid = Ssid::from(&record.ssid[..str_len]);
200
201 AccessPointInfo {
202 ssid,
203 bssid: record.bssid,
204 channel: record.primary,
205 secondary_channel: SecondaryChannel::from_raw(record.second),
206 signal_strength: record.rssi,
207 auth_method: Some(AuthenticationMethod::from_raw(record.authmode)),
208 #[cfg(feature = "unstable")]
209 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
210 country: CountryInfo::try_from_c(&record.country),
211 }
212}