1use procmacros::BuilderLite;
4
5#[cfg(feature = "unstable")]
6use super::CountryInfo;
7use super::{AuthenticationMethod, DisconnectReason, Protocols, SecondaryChannel, Ssid};
8use crate::{WifiError, sys::include::wifi_ap_record_t, wifi::AuthenticationMethodConfig};
9
10#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13#[non_exhaustive]
14pub struct AccessPointInfo {
15 pub ssid: Ssid,
17 pub bssid: [u8; 6],
19 pub channel: u8,
21 pub secondary_channel: SecondaryChannel,
23 pub signal_strength: i8,
25 pub auth_method: Option<AuthenticationMethod>,
27 #[cfg(feature = "unstable")]
28 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
29 pub country: Option<CountryInfo>,
31}
32
33#[derive(Clone, PartialEq, Eq, BuilderLite, Hash, Debug)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub struct AccessPointConfig {
37 pub(crate) ssid: Ssid,
39 pub(crate) ssid_hidden: bool,
41 pub(crate) channel: u8,
43 pub(crate) secondary_channel: Option<SecondaryChannel>,
45 pub(crate) protocols: Protocols,
47 pub(crate) authentication: AuthenticationMethodConfig,
49 #[builder_lite(unstable)]
54 pub(crate) max_connections: u16,
55 #[builder_lite(unstable)]
57 pub(crate) dtim_period: u8,
58 #[builder_lite(unstable)]
60 pub(crate) beacon_timeout: u16,
61}
62
63impl AccessPointConfig {
64 pub(crate) fn validate(&self) -> Result<(), WifiError> {
65 if matches!(self.authentication, AuthenticationMethodConfig::Wep(_)) {
68 warn!("WEP is not supported in access point mode.");
69 return Err(WifiError::Unsupported);
70 }
71
72 if let Some(password) = self.authentication.password()
73 && password.is_empty()
74 {
75 warn!("Access point password is empty.");
76 return Err(WifiError::InvalidPassword);
77 }
78
79 if !(1..=10).contains(&self.dtim_period) {
80 return Err(WifiError::InvalidArguments);
81 }
82
83 Ok(())
84 }
85}
86
87impl Default for AccessPointConfig {
88 fn default() -> Self {
89 Self {
90 ssid: "iot-device".try_into().expect("SSID length is valid"),
91 ssid_hidden: false,
92 channel: 1,
93 secondary_channel: None,
94 protocols: Protocols::default(),
95 authentication: AuthenticationMethodConfig::Open,
96 max_connections: 255,
97 dtim_period: 2,
98 beacon_timeout: 300,
99 }
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105#[cfg_attr(feature = "defmt", derive(defmt::Format))]
106#[non_exhaustive]
107pub struct ConnectedInfo {
108 pub mac: [u8; 6],
110 pub aid: u16,
112 pub is_mesh_child: bool,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118#[cfg_attr(feature = "defmt", derive(defmt::Format))]
119#[non_exhaustive]
120pub struct DisconnectedInfo {
121 pub mac: [u8; 6],
123 pub aid: u16,
125 pub is_mesh_child: bool,
127 pub reason: DisconnectReason,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
133#[cfg_attr(feature = "defmt", derive(defmt::Format))]
134pub enum EventInfo {
135 Connected(ConnectedInfo),
137 Disconnected(DisconnectedInfo),
139}
140
141#[allow(non_upper_case_globals)]
142pub(crate) fn convert_ap_info(record: &wifi_ap_record_t) -> AccessPointInfo {
143 let str_len = record.ssid.iter().position(|&c| c == 0).unwrap_or(32);
147 let ssid = Ssid::try_from(&record.ssid[..str_len]).expect("SSID length is valid");
148
149 AccessPointInfo {
150 ssid,
151 bssid: record.bssid,
152 channel: record.primary,
153 secondary_channel: SecondaryChannel::from_raw(record.second),
154 signal_strength: record.rssi,
155 auth_method: Some(AuthenticationMethod::from_raw(record.authmode)),
156 #[cfg(feature = "unstable")]
157 country: CountryInfo::try_from_c(&record.country),
158 }
159}