diff options
| -rw-r--r-- | cyw43/src/consts.rs | 15 | ||||
| -rw-r--r-- | cyw43/src/control.rs | 184 | ||||
| -rw-r--r-- | cyw43/src/lib.rs | 4 | ||||
| -rw-r--r-- | cyw43/src/structs.rs | 9 | ||||
| -rw-r--r-- | examples/rp/src/bin/wifi_tcp_server.rs | 7 | ||||
| -rw-r--r-- | examples/rp/src/bin/wifi_webrequest.rs | 7 | ||||
| -rw-r--r-- | tests/rp/src/bin/cyw43-perf.rs | 6 |
7 files changed, 175 insertions, 57 deletions
diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs index d47e5097a..c3f0dbfd8 100644 --- a/cyw43/src/consts.rs +++ b/cyw43/src/consts.rs | |||
| @@ -653,3 +653,18 @@ pub(crate) enum Ioctl { | |||
| 653 | GetWsecPmk = 318, | 653 | GetWsecPmk = 318, |
| 654 | GetRandomBytes = 319, | 654 | GetRandomBytes = 319, |
| 655 | } | 655 | } |
| 656 | |||
| 657 | pub(crate) const WSEC_TKIP: u32 = 0x02; | ||
| 658 | pub(crate) const WSEC_AES: u32 = 0x04; | ||
| 659 | |||
| 660 | pub(crate) const AUTH_OPEN: u32 = 0x00; | ||
| 661 | pub(crate) const AUTH_SAE: u32 = 0x03; | ||
| 662 | |||
| 663 | pub(crate) const MFP_NONE: u32 = 0; | ||
| 664 | pub(crate) const MFP_CAPABLE: u32 = 1; | ||
| 665 | pub(crate) const MFP_REQUIRED: u32 = 2; | ||
| 666 | |||
| 667 | pub(crate) const WPA_AUTH_DISABLED: u32 = 0x0000; | ||
| 668 | pub(crate) const WPA_AUTH_WPA_PSK: u32 = 0x0004; | ||
| 669 | pub(crate) const WPA_AUTH_WPA2_PSK: u32 = 0x0080; | ||
| 670 | pub(crate) const WPA_AUTH_WPA3_SAE_PSK: u32 = 0x40000; | ||
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 97dcb4d09..071ba88e4 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs | |||
| @@ -35,7 +35,7 @@ pub struct Control<'a> { | |||
| 35 | ioctl_state: &'a IoctlState, | 35 | ioctl_state: &'a IoctlState, |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | #[derive(Copy, Clone)] | 38 | #[derive(Copy, Clone, Debug)] |
| 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 40 | pub enum ScanType { | 40 | pub enum ScanType { |
| 41 | Active, | 41 | Active, |
| @@ -43,8 +43,9 @@ pub enum ScanType { | |||
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | /// Scan options. | 45 | /// Scan options. |
| 46 | #[derive(Clone)] | 46 | #[derive(Clone, Debug)] |
| 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 48 | #[non_exhaustive] | ||
| 48 | pub struct ScanOptions { | 49 | pub struct ScanOptions { |
| 49 | /// SSID to scan for. | 50 | /// SSID to scan for. |
| 50 | pub ssid: Option<heapless::String<32>>, | 51 | pub ssid: Option<heapless::String<32>>, |
| @@ -74,6 +75,79 @@ impl Default for ScanOptions { | |||
| 74 | } | 75 | } |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 78 | /// Authentication type, used in [`JoinOptions::auth`]. | ||
| 79 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 80 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 81 | pub enum JoinAuth { | ||
| 82 | /// Open network | ||
| 83 | Open, | ||
| 84 | /// WPA only | ||
| 85 | Wpa, | ||
| 86 | /// WPA2 only | ||
| 87 | Wpa2, | ||
| 88 | /// WPA3 only | ||
| 89 | Wpa3, | ||
| 90 | /// WPA2 + WPA3 | ||
| 91 | Wpa2Wpa3, | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Options for [`Control::join`]. | ||
| 95 | #[derive(Clone, Debug)] | ||
| 96 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 97 | #[non_exhaustive] | ||
| 98 | pub struct JoinOptions<'a> { | ||
| 99 | /// Authentication type. Default `Wpa2Wpa3`. | ||
| 100 | pub auth: JoinAuth, | ||
| 101 | /// Enable TKIP encryption. Default false. | ||
| 102 | pub cipher_tkip: bool, | ||
| 103 | /// Enable AES encryption. Default true. | ||
| 104 | pub cipher_aes: bool, | ||
| 105 | /// Passphrase. Default empty. | ||
| 106 | pub passphrase: &'a [u8], | ||
| 107 | /// If false, `passphrase` is the human-readable passphrase string. | ||
| 108 | /// If true, `passphrase` is the result of applying the PBKDF2 hash to the | ||
| 109 | /// passphrase string. This makes it possible to avoid storing unhashed passwords. | ||
| 110 | /// | ||
| 111 | /// This is not compatible with WPA3. | ||
| 112 | /// Default false. | ||
| 113 | pub passphrase_is_prehashed: bool, | ||
| 114 | } | ||
| 115 | |||
| 116 | impl<'a> JoinOptions<'a> { | ||
| 117 | /// Create a new `JoinOptions` for joining open networks. | ||
| 118 | pub fn new_open() -> Self { | ||
| 119 | Self { | ||
| 120 | auth: JoinAuth::Open, | ||
| 121 | cipher_tkip: false, | ||
| 122 | cipher_aes: false, | ||
| 123 | passphrase: &[], | ||
| 124 | passphrase_is_prehashed: false, | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Create a new `JoinOptions` for joining encrypted networks. | ||
| 129 | /// | ||
| 130 | /// Defaults to supporting WPA2+WPA3 with AES only, you may edit | ||
| 131 | /// the returned options to change this. | ||
| 132 | pub fn new(passphrase: &'a [u8]) -> Self { | ||
| 133 | let mut this = Self::default(); | ||
| 134 | this.passphrase = passphrase; | ||
| 135 | this | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | impl<'a> Default for JoinOptions<'a> { | ||
| 140 | fn default() -> Self { | ||
| 141 | Self { | ||
| 142 | auth: JoinAuth::Wpa2Wpa3, | ||
| 143 | cipher_tkip: false, | ||
| 144 | cipher_aes: true, | ||
| 145 | passphrase: &[], | ||
| 146 | passphrase_is_prehashed: false, | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 77 | impl<'a> Control<'a> { | 151 | impl<'a> Control<'a> { |
| 78 | pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { | 152 | pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { |
| 79 | Self { | 153 | Self { |
| @@ -217,40 +291,70 @@ impl<'a> Control<'a> { | |||
| 217 | } | 291 | } |
| 218 | 292 | ||
| 219 | /// Join an unprotected network with the provided ssid. | 293 | /// Join an unprotected network with the provided ssid. |
| 220 | pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { | 294 | pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), Error> { |
| 221 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | 295 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; |
| 222 | 296 | ||
| 223 | self.ioctl_set_u32(Ioctl::SetWsec, 0, 0).await; // wsec = open | 297 | if options.auth == JoinAuth::Open { |
| 224 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; | 298 | self.ioctl_set_u32(Ioctl::SetWsec, 0, 0).await; |
| 225 | self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await; // set_infra = 1 | 299 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; |
| 226 | self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await; // set_auth = open (0) | 300 | self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await; |
| 227 | 301 | self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await; | |
| 228 | let mut i = SsidInfo { | 302 | self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, WPA_AUTH_DISABLED).await; |
| 229 | len: ssid.len() as _, | 303 | } else { |
| 230 | ssid: [0; 32], | 304 | let mut wsec = 0; |
| 231 | }; | 305 | if options.cipher_aes { |
| 232 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); | 306 | wsec |= WSEC_AES; |
| 307 | } | ||
| 308 | if options.cipher_tkip { | ||
| 309 | wsec |= WSEC_TKIP; | ||
| 310 | } | ||
| 311 | self.ioctl_set_u32(Ioctl::SetWsec, 0, wsec).await; | ||
| 233 | 312 | ||
| 234 | self.wait_for_join(i).await | 313 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; |
| 235 | } | 314 | self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; |
| 315 | self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; | ||
| 236 | 316 | ||
| 237 | /// Join a protected network with the provided ssid and [`PassphraseInfo`]. | 317 | Timer::after_millis(100).await; |
| 238 | async fn join_wpa2_passphrase_info(&mut self, ssid: &str, passphrase_info: &PassphraseInfo) -> Result<(), Error> { | ||
| 239 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 240 | 318 | ||
| 241 | self.ioctl_set_u32(Ioctl::SetWsec, 0, 4).await; // wsec = open | 319 | let (wpa12, wpa3, auth, mfp, wpa_auth) = match options.auth { |
| 242 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; | 320 | JoinAuth::Open => unreachable!(), |
| 243 | self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; | 321 | JoinAuth::Wpa => (true, false, AUTH_OPEN, MFP_NONE, WPA_AUTH_WPA_PSK), |
| 244 | self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; | 322 | JoinAuth::Wpa2 => (true, false, AUTH_OPEN, MFP_CAPABLE, WPA_AUTH_WPA2_PSK), |
| 323 | JoinAuth::Wpa3 => (false, true, AUTH_SAE, MFP_REQUIRED, WPA_AUTH_WPA3_SAE_PSK), | ||
| 324 | JoinAuth::Wpa2Wpa3 => (true, true, AUTH_SAE, MFP_CAPABLE, WPA_AUTH_WPA3_SAE_PSK), | ||
| 325 | }; | ||
| 245 | 326 | ||
| 246 | Timer::after_millis(100).await; | 327 | if wpa12 { |
| 328 | let mut flags = 0; | ||
| 329 | if !options.passphrase_is_prehashed { | ||
| 330 | flags |= 1; | ||
| 331 | } | ||
| 332 | let mut pfi = PassphraseInfo { | ||
| 333 | len: options.passphrase.len() as _, | ||
| 334 | flags, | ||
| 335 | passphrase: [0; 64], | ||
| 336 | }; | ||
| 337 | pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase); | ||
| 338 | Timer::after_millis(3).await; | ||
| 339 | self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes()) | ||
| 340 | .await; | ||
| 341 | } | ||
| 247 | 342 | ||
| 248 | self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut passphrase_info.to_bytes()) | 343 | if wpa3 { |
| 249 | .await; // WLC_SET_WSEC_PMK | 344 | let mut pfi = SaePassphraseInfo { |
| 345 | len: options.passphrase.len() as _, | ||
| 346 | passphrase: [0; 128], | ||
| 347 | }; | ||
| 348 | pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase); | ||
| 349 | Timer::after_millis(3).await; | ||
| 350 | self.set_iovar("sae_password", &pfi.to_bytes()).await; | ||
| 351 | } | ||
| 250 | 352 | ||
| 251 | self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await; // set_infra = 1 | 353 | self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await; |
| 252 | self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await; // set_auth = 0 (open) | 354 | self.ioctl_set_u32(Ioctl::SetAuth, 0, auth).await; |
| 253 | self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, 0x80).await; | 355 | self.set_iovar_u32("mfp", mfp).await; |
| 356 | self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, wpa_auth).await; | ||
| 357 | } | ||
| 254 | 358 | ||
| 255 | let mut i = SsidInfo { | 359 | let mut i = SsidInfo { |
| 256 | len: ssid.len() as _, | 360 | len: ssid.len() as _, |
| @@ -261,28 +365,6 @@ impl<'a> Control<'a> { | |||
| 261 | self.wait_for_join(i).await | 365 | self.wait_for_join(i).await |
| 262 | } | 366 | } |
| 263 | 367 | ||
| 264 | /// Join a protected network with the provided ssid and passphrase. | ||
| 265 | pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> { | ||
| 266 | let mut pfi = PassphraseInfo { | ||
| 267 | len: passphrase.len() as _, | ||
| 268 | flags: 1, | ||
| 269 | passphrase: [0; 64], | ||
| 270 | }; | ||
| 271 | pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); | ||
| 272 | self.join_wpa2_passphrase_info(ssid, &pfi).await | ||
| 273 | } | ||
| 274 | |||
| 275 | /// Join a protected network with the provided ssid and precomputed PSK. | ||
| 276 | pub async fn join_wpa2_psk(&mut self, ssid: &str, psk: &[u8; 32]) -> Result<(), Error> { | ||
| 277 | let mut pfi = PassphraseInfo { | ||
| 278 | len: psk.len() as _, | ||
| 279 | flags: 0, | ||
| 280 | passphrase: [0; 64], | ||
| 281 | }; | ||
| 282 | pfi.passphrase[..psk.len()].copy_from_slice(psk); | ||
| 283 | self.join_wpa2_passphrase_info(ssid, &pfi).await | ||
| 284 | } | ||
| 285 | |||
| 286 | async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { | 368 | async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { |
| 287 | self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); | 369 | self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); |
| 288 | let mut subscriber = self.events.queue.subscriber().unwrap(); | 370 | let mut subscriber = self.events.queue.subscriber().unwrap(); |
| @@ -477,7 +559,7 @@ impl<'a> Control<'a> { | |||
| 477 | } | 559 | } |
| 478 | 560 | ||
| 479 | async fn set_iovar(&mut self, name: &str, val: &[u8]) { | 561 | async fn set_iovar(&mut self, name: &str, val: &[u8]) { |
| 480 | self.set_iovar_v::<64>(name, val).await | 562 | self.set_iovar_v::<196>(name, val).await |
| 481 | } | 563 | } |
| 482 | 564 | ||
| 483 | async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) { | 565 | async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) { |
diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index efeb3f313..6b71c18e6 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs | |||
| @@ -28,7 +28,9 @@ use ioctl::IoctlState; | |||
| 28 | 28 | ||
| 29 | use crate::bus::Bus; | 29 | use crate::bus::Bus; |
| 30 | pub use crate::bus::SpiBusCyw43; | 30 | pub use crate::bus::SpiBusCyw43; |
| 31 | pub use crate::control::{AddMulticastAddressError, Control, Error as ControlError, ScanOptions, Scanner}; | 31 | pub use crate::control::{ |
| 32 | AddMulticastAddressError, Control, Error as ControlError, JoinAuth, JoinOptions, ScanOptions, Scanner, | ||
| 33 | }; | ||
| 32 | pub use crate::runner::Runner; | 34 | pub use crate::runner::Runner; |
| 33 | pub use crate::structs::BssInfo; | 35 | pub use crate::structs::BssInfo; |
| 34 | 36 | ||
diff --git a/cyw43/src/structs.rs b/cyw43/src/structs.rs index ae7ef6038..81ae6a98d 100644 --- a/cyw43/src/structs.rs +++ b/cyw43/src/structs.rs | |||
| @@ -397,6 +397,15 @@ impl_bytes!(PassphraseInfo); | |||
| 397 | #[derive(Clone, Copy)] | 397 | #[derive(Clone, Copy)] |
| 398 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 398 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 399 | #[repr(C)] | 399 | #[repr(C)] |
| 400 | pub struct SaePassphraseInfo { | ||
| 401 | pub len: u16, | ||
| 402 | pub passphrase: [u8; 128], | ||
| 403 | } | ||
| 404 | impl_bytes!(SaePassphraseInfo); | ||
| 405 | |||
| 406 | #[derive(Clone, Copy)] | ||
| 407 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 408 | #[repr(C)] | ||
| 400 | pub struct SsidInfoWithIndex { | 409 | pub struct SsidInfoWithIndex { |
| 401 | pub index: u32, | 410 | pub index: u32, |
| 402 | pub ssid_info: SsidInfo, | 411 | pub ssid_info: SsidInfo, |
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 61eeb82f7..b2950d98a 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | use core::str::from_utf8; | 8 | use core::str::from_utf8; |
| 9 | 9 | ||
| 10 | use cyw43::JoinOptions; | ||
| 10 | use cyw43_pio::PioSpi; | 11 | use cyw43_pio::PioSpi; |
| 11 | use defmt::*; | 12 | use defmt::*; |
| 12 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| @@ -95,8 +96,10 @@ async fn main(spawner: Spawner) { | |||
| 95 | unwrap!(spawner.spawn(net_task(stack))); | 96 | unwrap!(spawner.spawn(net_task(stack))); |
| 96 | 97 | ||
| 97 | loop { | 98 | loop { |
| 98 | //control.join_open(WIFI_NETWORK).await; | 99 | match control |
| 99 | match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { | 100 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) |
| 101 | .await | ||
| 102 | { | ||
| 100 | Ok(_) => break, | 103 | Ok(_) => break, |
| 101 | Err(err) => { | 104 | Err(err) => { |
| 102 | info!("join failed with status={}", err.status); | 105 | info!("join failed with status={}", err.status); |
diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index 889371241..b43be8905 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | use core::str::from_utf8; | 8 | use core::str::from_utf8; |
| 9 | 9 | ||
| 10 | use cyw43::JoinOptions; | ||
| 10 | use cyw43_pio::PioSpi; | 11 | use cyw43_pio::PioSpi; |
| 11 | use defmt::*; | 12 | use defmt::*; |
| 12 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| @@ -98,8 +99,10 @@ async fn main(spawner: Spawner) { | |||
| 98 | unwrap!(spawner.spawn(net_task(stack))); | 99 | unwrap!(spawner.spawn(net_task(stack))); |
| 99 | 100 | ||
| 100 | loop { | 101 | loop { |
| 101 | //match control.join_open(WIFI_NETWORK).await { // for open networks | 102 | match control |
| 102 | match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { | 103 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) |
| 104 | .await | ||
| 105 | { | ||
| 103 | Ok(_) => break, | 106 | Ok(_) => break, |
| 104 | Err(err) => { | 107 | Err(err) => { |
| 105 | info!("join failed with status={}", err.status); | 108 | info!("join failed with status={}", err.status); |
diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 38fbde7c1..11c8aa58c 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | teleprobe_meta::target!(b"rpi-pico"); | 3 | teleprobe_meta::target!(b"rpi-pico"); |
| 4 | 4 | ||
| 5 | use cyw43::JoinOptions; | ||
| 5 | use cyw43_pio::PioSpi; | 6 | use cyw43_pio::PioSpi; |
| 6 | use defmt::{panic, *}; | 7 | use defmt::{panic, *}; |
| 7 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| @@ -81,7 +82,10 @@ async fn main(spawner: Spawner) { | |||
| 81 | unwrap!(spawner.spawn(net_task(stack))); | 82 | unwrap!(spawner.spawn(net_task(stack))); |
| 82 | 83 | ||
| 83 | loop { | 84 | loop { |
| 84 | match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { | 85 | match control |
| 86 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) | ||
| 87 | .await | ||
| 88 | { | ||
| 85 | Ok(_) => break, | 89 | Ok(_) => break, |
| 86 | Err(err) => { | 90 | Err(err) => { |
| 87 | panic!("join failed with status={}", err.status); | 91 | panic!("join failed with status={}", err.status); |
