aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-09-09 00:37:17 +0000
committerGitHub <[email protected]>2024-09-09 00:37:17 +0000
commit0ef06cc19b61c8196fea941514ec313a0f15d145 (patch)
tree22a2f15923726f1d51ab96a71a95c2b8d8223fb0
parent74e724f96869680da4893ad7024676bef1c57334 (diff)
parentb9a1aaea5b89bd5689796bdfa4227353ee8a452b (diff)
Merge pull request #3323 from embassy-rs/cyw43-wpa3
cyw43: add support for WPA3 and more extensive security options.
-rw-r--r--cyw43/src/consts.rs314
-rw-r--r--cyw43/src/control.rs237
-rw-r--r--cyw43/src/ioctl.rs7
-rw-r--r--cyw43/src/lib.rs4
-rw-r--r--cyw43/src/runner.rs4
-rw-r--r--cyw43/src/structs.rs9
-rw-r--r--examples/rp/src/bin/wifi_tcp_server.rs7
-rw-r--r--examples/rp/src/bin/wifi_webrequest.rs7
-rw-r--r--tests/rp/src/bin/cyw43-perf.rs6
9 files changed, 496 insertions, 99 deletions
diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs
index b6e22e61d..c3f0dbfd8 100644
--- a/cyw43/src/consts.rs
+++ b/cyw43/src/consts.rs
@@ -113,17 +113,6 @@ pub(crate) const IRQ_F1_INTR: u16 = 0x2000;
113pub(crate) const IRQ_F2_INTR: u16 = 0x4000; 113pub(crate) const IRQ_F2_INTR: u16 = 0x4000;
114pub(crate) const IRQ_F3_INTR: u16 = 0x8000; 114pub(crate) const IRQ_F3_INTR: u16 = 0x8000;
115 115
116pub(crate) const IOCTL_CMD_UP: u32 = 2;
117pub(crate) const IOCTL_CMD_DOWN: u32 = 3;
118pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
119pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30;
120pub(crate) const IOCTL_CMD_DISASSOC: u32 = 52;
121pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
122pub(crate) const IOCTL_CMD_SET_AP: u32 = 118;
123pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
124pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262;
125pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268;
126
127pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0; 116pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0;
128pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1; 117pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1;
129pub(crate) const CHANNEL_TYPE_DATA: u8 = 2; 118pub(crate) const CHANNEL_TYPE_DATA: u8 = 2;
@@ -376,3 +365,306 @@ impl core::fmt::Display for FormatInterrupt {
376 core::fmt::Debug::fmt(self, f) 365 core::fmt::Debug::fmt(self, f)
377 } 366 }
378} 367}
368
369#[derive(Copy, Clone, Debug)]
370#[cfg_attr(feature = "defmt", derive(defmt::Format))]
371#[repr(u32)]
372pub(crate) enum Ioctl {
373 GetMagic = 0,
374 GetVersion = 1,
375 Up = 2,
376 Down = 3,
377 GetLoop = 4,
378 SetLoop = 5,
379 Dump = 6,
380 GetMsglevel = 7,
381 SetMsglevel = 8,
382 GetPromisc = 9,
383 SetPromisc = 10,
384 GetRate = 12,
385 GetInstance = 14,
386 GetInfra = 19,
387 SetInfra = 20,
388 GetAuth = 21,
389 SetAuth = 22,
390 GetBssid = 23,
391 SetBssid = 24,
392 GetSsid = 25,
393 SetSsid = 26,
394 Restart = 27,
395 GetChannel = 29,
396 SetChannel = 30,
397 GetSrl = 31,
398 SetSrl = 32,
399 GetLrl = 33,
400 SetLrl = 34,
401 GetPlcphdr = 35,
402 SetPlcphdr = 36,
403 GetRadio = 37,
404 SetRadio = 38,
405 GetPhytype = 39,
406 DumpRate = 40,
407 SetRateParams = 41,
408 GetKey = 44,
409 SetKey = 45,
410 GetRegulatory = 46,
411 SetRegulatory = 47,
412 GetPassiveScan = 48,
413 SetPassiveScan = 49,
414 Scan = 50,
415 ScanResults = 51,
416 Disassoc = 52,
417 Reassoc = 53,
418 GetRoamTrigger = 54,
419 SetRoamTrigger = 55,
420 GetRoamDelta = 56,
421 SetRoamDelta = 57,
422 GetRoamScanPeriod = 58,
423 SetRoamScanPeriod = 59,
424 Evm = 60,
425 GetTxant = 61,
426 SetTxant = 62,
427 GetAntdiv = 63,
428 SetAntdiv = 64,
429 GetClosed = 67,
430 SetClosed = 68,
431 GetMaclist = 69,
432 SetMaclist = 70,
433 GetRateset = 71,
434 SetRateset = 72,
435 Longtrain = 74,
436 GetBcnprd = 75,
437 SetBcnprd = 76,
438 GetDtimprd = 77,
439 SetDtimprd = 78,
440 GetSrom = 79,
441 SetSrom = 80,
442 GetWepRestrict = 81,
443 SetWepRestrict = 82,
444 GetCountry = 83,
445 SetCountry = 84,
446 GetPm = 85,
447 SetPm = 86,
448 GetWake = 87,
449 SetWake = 88,
450 GetForcelink = 90,
451 SetForcelink = 91,
452 FreqAccuracy = 92,
453 CarrierSuppress = 93,
454 GetPhyreg = 94,
455 SetPhyreg = 95,
456 GetRadioreg = 96,
457 SetRadioreg = 97,
458 GetRevinfo = 98,
459 GetUcantdiv = 99,
460 SetUcantdiv = 100,
461 RReg = 101,
462 WReg = 102,
463 GetMacmode = 105,
464 SetMacmode = 106,
465 GetMonitor = 107,
466 SetMonitor = 108,
467 GetGmode = 109,
468 SetGmode = 110,
469 GetLegacyErp = 111,
470 SetLegacyErp = 112,
471 GetRxAnt = 113,
472 GetCurrRateset = 114,
473 GetScansuppress = 115,
474 SetScansuppress = 116,
475 GetAp = 117,
476 SetAp = 118,
477 GetEapRestrict = 119,
478 SetEapRestrict = 120,
479 ScbAuthorize = 121,
480 ScbDeauthorize = 122,
481 GetWdslist = 123,
482 SetWdslist = 124,
483 GetAtim = 125,
484 SetAtim = 126,
485 GetRssi = 127,
486 GetPhyantdiv = 128,
487 SetPhyantdiv = 129,
488 ApRxOnly = 130,
489 GetTxPathPwr = 131,
490 SetTxPathPwr = 132,
491 GetWsec = 133,
492 SetWsec = 134,
493 GetPhyNoise = 135,
494 GetBssInfo = 136,
495 GetPktcnts = 137,
496 GetLazywds = 138,
497 SetLazywds = 139,
498 GetBandlist = 140,
499 GetBand = 141,
500 SetBand = 142,
501 ScbDeauthenticate = 143,
502 GetShortslot = 144,
503 GetShortslotOverride = 145,
504 SetShortslotOverride = 146,
505 GetShortslotRestrict = 147,
506 SetShortslotRestrict = 148,
507 GetGmodeProtection = 149,
508 GetGmodeProtectionOverride = 150,
509 SetGmodeProtectionOverride = 151,
510 Upgrade = 152,
511 GetIgnoreBcns = 155,
512 SetIgnoreBcns = 156,
513 GetScbTimeout = 157,
514 SetScbTimeout = 158,
515 GetAssoclist = 159,
516 GetClk = 160,
517 SetClk = 161,
518 GetUp = 162,
519 Out = 163,
520 GetWpaAuth = 164,
521 SetWpaAuth = 165,
522 GetUcflags = 166,
523 SetUcflags = 167,
524 GetPwridx = 168,
525 SetPwridx = 169,
526 GetTssi = 170,
527 GetSupRatesetOverride = 171,
528 SetSupRatesetOverride = 172,
529 GetProtectionControl = 178,
530 SetProtectionControl = 179,
531 GetPhylist = 180,
532 EncryptStrength = 181,
533 DecryptStatus = 182,
534 GetKeySeq = 183,
535 GetScanChannelTime = 184,
536 SetScanChannelTime = 185,
537 GetScanUnassocTime = 186,
538 SetScanUnassocTime = 187,
539 GetScanHomeTime = 188,
540 SetScanHomeTime = 189,
541 GetScanNprobes = 190,
542 SetScanNprobes = 191,
543 GetPrbRespTimeout = 192,
544 SetPrbRespTimeout = 193,
545 GetAtten = 194,
546 SetAtten = 195,
547 GetShmem = 196,
548 SetShmem = 197,
549 SetWsecTest = 200,
550 ScbDeauthenticateForReason = 201,
551 TkipCountermeasures = 202,
552 GetPiomode = 203,
553 SetPiomode = 204,
554 SetAssocPrefer = 205,
555 GetAssocPrefer = 206,
556 SetRoamPrefer = 207,
557 GetRoamPrefer = 208,
558 SetLed = 209,
559 GetLed = 210,
560 GetInterferenceMode = 211,
561 SetInterferenceMode = 212,
562 GetChannelQa = 213,
563 StartChannelQa = 214,
564 GetChannelSel = 215,
565 StartChannelSel = 216,
566 GetValidChannels = 217,
567 GetFakefrag = 218,
568 SetFakefrag = 219,
569 GetPwroutPercentage = 220,
570 SetPwroutPercentage = 221,
571 SetBadFramePreempt = 222,
572 GetBadFramePreempt = 223,
573 SetLeapList = 224,
574 GetLeapList = 225,
575 GetCwmin = 226,
576 SetCwmin = 227,
577 GetCwmax = 228,
578 SetCwmax = 229,
579 GetWet = 230,
580 SetWet = 231,
581 GetPub = 232,
582 GetKeyPrimary = 235,
583 SetKeyPrimary = 236,
584 GetAciArgs = 238,
585 SetAciArgs = 239,
586 UnsetCallback = 240,
587 SetCallback = 241,
588 GetRadar = 242,
589 SetRadar = 243,
590 SetSpectManagment = 244,
591 GetSpectManagment = 245,
592 WdsGetRemoteHwaddr = 246,
593 WdsGetWpaSup = 247,
594 SetCsScanTimer = 248,
595 GetCsScanTimer = 249,
596 MeasureRequest = 250,
597 Init = 251,
598 SendQuiet = 252,
599 Keepalive = 253,
600 SendPwrConstraint = 254,
601 UpgradeStatus = 255,
602 CurrentPwr = 256,
603 GetScanPassiveTime = 257,
604 SetScanPassiveTime = 258,
605 LegacyLinkBehavior = 259,
606 GetChannelsInCountry = 260,
607 GetCountryList = 261,
608 GetVar = 262,
609 SetVar = 263,
610 NvramGet = 264,
611 NvramSet = 265,
612 NvramDump = 266,
613 Reboot = 267,
614 SetWsecPmk = 268,
615 GetAuthMode = 269,
616 SetAuthMode = 270,
617 GetWakeentry = 271,
618 SetWakeentry = 272,
619 NdconfigItem = 273,
620 Nvotpw = 274,
621 Otpw = 275,
622 IovBlockGet = 276,
623 IovModulesGet = 277,
624 SoftReset = 278,
625 GetAllowMode = 279,
626 SetAllowMode = 280,
627 GetDesiredBssid = 281,
628 SetDesiredBssid = 282,
629 DisassocMyap = 283,
630 GetNbands = 284,
631 GetBandstates = 285,
632 GetWlcBssInfo = 286,
633 GetAssocInfo = 287,
634 GetOidPhy = 288,
635 SetOidPhy = 289,
636 SetAssocTime = 290,
637 GetDesiredSsid = 291,
638 GetChanspec = 292,
639 GetAssocState = 293,
640 SetPhyState = 294,
641 GetScanPending = 295,
642 GetScanreqPending = 296,
643 GetPrevRoamReason = 297,
644 SetPrevRoamReason = 298,
645 GetBandstatesPi = 299,
646 GetPhyState = 300,
647 GetBssWpaRsn = 301,
648 GetBssWpa2Rsn = 302,
649 GetBssBcnTs = 303,
650 GetIntDisassoc = 304,
651 SetNumPeers = 305,
652 GetNumBss = 306,
653 GetWsecPmk = 318,
654 GetRandomBytes = 319,
655}
656
657pub(crate) const WSEC_TKIP: u32 = 0x02;
658pub(crate) const WSEC_AES: u32 = 0x04;
659
660pub(crate) const AUTH_OPEN: u32 = 0x00;
661pub(crate) const AUTH_SAE: u32 = 0x03;
662
663pub(crate) const MFP_NONE: u32 = 0;
664pub(crate) const MFP_CAPABLE: u32 = 1;
665pub(crate) const MFP_REQUIRED: u32 = 2;
666
667pub(crate) const WPA_AUTH_DISABLED: u32 = 0x0000;
668pub(crate) const WPA_AUTH_WPA_PSK: u32 = 0x0004;
669pub(crate) const WPA_AUTH_WPA2_PSK: u32 = 0x0080;
670pub(crate) const WPA_AUTH_WPA3_SAE_PSK: u32 = 0x40000;
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs
index 22c52bd96..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))]
40pub enum ScanType { 40pub 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]
48pub struct ScanOptions { 49pub 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))]
81pub 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]
98pub 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
116impl<'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
139impl<'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
77impl<'a> Control<'a> { 151impl<'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 {
@@ -109,7 +183,7 @@ impl<'a> Control<'a> {
109 buf[0..8].copy_from_slice(b"clmload\x00"); 183 buf[0..8].copy_from_slice(b"clmload\x00");
110 buf[8..20].copy_from_slice(&header.to_bytes()); 184 buf[8..20].copy_from_slice(&header.to_bytes());
111 buf[20..][..chunk.len()].copy_from_slice(&chunk); 185 buf[20..][..chunk.len()].copy_from_slice(&chunk);
112 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) 186 self.ioctl(IoctlType::Set, Ioctl::SetVar, 0, &mut buf[..8 + 12 + chunk.len()])
113 .await; 187 .await;
114 } 188 }
115 189
@@ -145,7 +219,7 @@ impl<'a> Control<'a> {
145 Timer::after_millis(100).await; 219 Timer::after_millis(100).await;
146 220
147 // Set antenna to chip antenna 221 // Set antenna to chip antenna
148 self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; 222 self.ioctl_set_u32(Ioctl::SetAntdiv, 0, 0).await;
149 223
150 self.set_iovar_u32("bus:txglom", 0).await; 224 self.set_iovar_u32("bus:txglom", 0).await;
151 Timer::after_millis(100).await; 225 Timer::after_millis(100).await;
@@ -183,8 +257,8 @@ impl<'a> Control<'a> {
183 257
184 Timer::after_millis(100).await; 258 Timer::after_millis(100).await;
185 259
186 self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto 260 self.ioctl_set_u32(Ioctl::SetGmode, 0, 1).await; // SET_GMODE = auto
187 self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any 261 self.ioctl_set_u32(Ioctl::SetBand, 0, 0).await; // SET_BAND = any
188 262
189 Timer::after_millis(100).await; 263 Timer::after_millis(100).await;
190 264
@@ -195,12 +269,12 @@ impl<'a> Control<'a> {
195 269
196 /// Set the WiFi interface up. 270 /// Set the WiFi interface up.
197 async fn up(&mut self) { 271 async fn up(&mut self) {
198 self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; 272 self.ioctl(IoctlType::Set, Ioctl::Up, 0, &mut []).await;
199 } 273 }
200 274
201 /// Set the interface down. 275 /// Set the interface down.
202 async fn down(&mut self) { 276 async fn down(&mut self) {
203 self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; 277 self.ioctl(IoctlType::Set, Ioctl::Down, 0, &mut []).await;
204 } 278 }
205 279
206 /// Set power management mode. 280 /// Set power management mode.
@@ -213,49 +287,74 @@ impl<'a> Control<'a> {
213 self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; 287 self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await;
214 self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; 288 self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await;
215 } 289 }
216 self.ioctl_set_u32(86, 0, mode_num).await; 290 self.ioctl_set_u32(Ioctl::SetPm, 0, mode_num).await;
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(134, 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(20, 0, 1).await; // set_infra = 1 299 self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await;
226 self.ioctl_set_u32(22, 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(134, 0, 4).await; // wsec = wpa2 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( 343 if wpa3 {
249 IoctlType::Set, 344 let mut pfi = SaePassphraseInfo {
250 IOCTL_CMD_SET_PASSPHRASE, 345 len: options.passphrase.len() as _,
251 0, 346 passphrase: [0; 128],
252 &mut passphrase_info.to_bytes(), 347 };
253 ) 348 pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase);
254 .await; // WLC_SET_WSEC_PMK 349 Timer::after_millis(3).await;
350 self.set_iovar("sae_password", &pfi.to_bytes()).await;
351 }
255 352
256 self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 353 self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await;
257 self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) 354 self.ioctl_set_u32(Ioctl::SetAuth, 0, auth).await;
258 self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth 355 self.set_iovar_u32("mfp", mfp).await;
356 self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, wpa_auth).await;
357 }
259 358
260 let mut i = SsidInfo { 359 let mut i = SsidInfo {
261 len: ssid.len() as _, 360 len: ssid.len() as _,
@@ -266,37 +365,13 @@ impl<'a> Control<'a> {
266 self.wait_for_join(i).await 365 self.wait_for_join(i).await
267 } 366 }
268 367
269 /// Join a protected network with the provided ssid and passphrase.
270 pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
271 let mut pfi = PassphraseInfo {
272 len: passphrase.len() as _,
273 flags: 1,
274 passphrase: [0; 64],
275 };
276 pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes());
277 self.join_wpa2_passphrase_info(ssid, &pfi).await
278 }
279
280 /// Join a protected network with the provided ssid and precomputed PSK.
281 pub async fn join_wpa2_psk(&mut self, ssid: &str, psk: &[u8; 32]) -> Result<(), Error> {
282 let mut pfi = PassphraseInfo {
283 len: psk.len() as _,
284 flags: 0,
285 passphrase: [0; 64],
286 };
287 pfi.passphrase[..psk.len()].copy_from_slice(psk);
288 self.join_wpa2_passphrase_info(ssid, &pfi).await
289 }
290
291 async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { 368 async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> {
292 self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); 369 self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]);
293 let mut subscriber = self.events.queue.subscriber().unwrap(); 370 let mut subscriber = self.events.queue.subscriber().unwrap();
294 // the actual join operation starts here 371 // the actual join operation starts here
295 // we make sure to enable events before so we don't miss any 372 // we make sure to enable events before so we don't miss any
296 373
297 // set_ssid 374 self.ioctl(IoctlType::Set, Ioctl::SetSsid, 0, &mut i.to_bytes()).await;
298 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes())
299 .await;
300 375
301 // to complete the join, we wait for a SET_SSID event 376 // to complete the join, we wait for a SET_SSID event
302 // we also save the AUTH status for the user, it may be interesting 377 // we also save the AUTH status for the user, it may be interesting
@@ -357,7 +432,7 @@ impl<'a> Control<'a> {
357 self.up().await; 432 self.up().await;
358 433
359 // Turn on AP mode 434 // Turn on AP mode
360 self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; 435 self.ioctl_set_u32(Ioctl::SetAp, 0, 1).await;
361 436
362 // Set SSID 437 // Set SSID
363 let mut i = SsidInfoWithIndex { 438 let mut i = SsidInfoWithIndex {
@@ -371,7 +446,7 @@ impl<'a> Control<'a> {
371 self.set_iovar("bsscfg:ssid", &i.to_bytes()).await; 446 self.set_iovar("bsscfg:ssid", &i.to_bytes()).await;
372 447
373 // Set channel number 448 // Set channel number
374 self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await; 449 self.ioctl_set_u32(Ioctl::SetChannel, 0, channel as u32).await;
375 450
376 // Set security 451 // Set security
377 self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await; 452 self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await;
@@ -388,7 +463,7 @@ impl<'a> Control<'a> {
388 passphrase: [0; 64], 463 passphrase: [0; 64],
389 }; 464 };
390 pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); 465 pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes());
391 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) 466 self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes())
392 .await; 467 .await;
393 } 468 }
394 469
@@ -405,7 +480,7 @@ impl<'a> Control<'a> {
405 self.set_iovar_u32x2("bss", 0, 0).await; // bss = BSS_DOWN 480 self.set_iovar_u32x2("bss", 0, 0).await; // bss = BSS_DOWN
406 481
407 // Turn off AP mode 482 // Turn off AP mode
408 self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 0).await; 483 self.ioctl_set_u32(Ioctl::SetAp, 0, 0).await;
409 484
410 // Temporarily set wifi down 485 // Temporarily set wifi down
411 self.down().await; 486 self.down().await;
@@ -484,11 +559,11 @@ impl<'a> Control<'a> {
484 } 559 }
485 560
486 async fn set_iovar(&mut self, name: &str, val: &[u8]) { 561 async fn set_iovar(&mut self, name: &str, val: &[u8]) {
487 self.set_iovar_v::<64>(name, val).await 562 self.set_iovar_v::<196>(name, val).await
488 } 563 }
489 564
490 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]) {
491 debug!("set {} = {:02x}", name, Bytes(val)); 566 debug!("iovar set {} = {:02x}", name, Bytes(val));
492 567
493 let mut buf = [0; BUFSIZE]; 568 let mut buf = [0; BUFSIZE];
494 buf[..name.len()].copy_from_slice(name.as_bytes()); 569 buf[..name.len()].copy_from_slice(name.as_bytes());
@@ -496,13 +571,13 @@ impl<'a> Control<'a> {
496 buf[name.len() + 1..][..val.len()].copy_from_slice(val); 571 buf[name.len() + 1..][..val.len()].copy_from_slice(val);
497 572
498 let total_len = name.len() + 1 + val.len(); 573 let total_len = name.len() + 1 + val.len();
499 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) 574 self.ioctl_inner(IoctlType::Set, Ioctl::SetVar, 0, &mut buf[..total_len])
500 .await; 575 .await;
501 } 576 }
502 577
503 // TODO this is not really working, it always returns all zeros. 578 // TODO this is not really working, it always returns all zeros.
504 async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { 579 async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize {
505 debug!("get {}", name); 580 debug!("iovar get {}", name);
506 581
507 let mut buf = [0; 64]; 582 let mut buf = [0; 64];
508 buf[..name.len()].copy_from_slice(name.as_bytes()); 583 buf[..name.len()].copy_from_slice(name.as_bytes());
@@ -510,7 +585,7 @@ impl<'a> Control<'a> {
510 585
511 let total_len = max(name.len() + 1, res.len()); 586 let total_len = max(name.len() + 1, res.len());
512 let res_len = self 587 let res_len = self
513 .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) 588 .ioctl_inner(IoctlType::Get, Ioctl::GetVar, 0, &mut buf[..total_len])
514 .await; 589 .await;
515 590
516 let out_len = min(res.len(), res_len); 591 let out_len = min(res.len(), res_len);
@@ -518,12 +593,20 @@ impl<'a> Control<'a> {
518 out_len 593 out_len
519 } 594 }
520 595
521 async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { 596 async fn ioctl_set_u32(&mut self, cmd: Ioctl, iface: u32, val: u32) {
522 let mut buf = val.to_le_bytes(); 597 let mut buf = val.to_le_bytes();
523 self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; 598 self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await;
524 } 599 }
525 600
526 async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { 601 async fn ioctl(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {
602 if kind == IoctlType::Set {
603 debug!("ioctl set {:?} iface {} = {:02x}", cmd, iface, Bytes(buf));
604 }
605 let n = self.ioctl_inner(kind, cmd, iface, buf).await;
606 n
607 }
608
609 async fn ioctl_inner(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {
527 struct CancelOnDrop<'a>(&'a IoctlState); 610 struct CancelOnDrop<'a>(&'a IoctlState);
528 611
529 impl CancelOnDrop<'_> { 612 impl CancelOnDrop<'_> {
@@ -615,7 +698,7 @@ impl<'a> Control<'a> {
615 } 698 }
616 /// Leave the wifi, with which we are currently associated. 699 /// Leave the wifi, with which we are currently associated.
617 pub async fn leave(&mut self) { 700 pub async fn leave(&mut self) {
618 self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await; 701 self.ioctl(IoctlType::Set, Ioctl::Disassoc, 0, &mut []).await;
619 info!("Disassociated") 702 info!("Disassociated")
620 } 703 }
621 704
diff --git a/cyw43/src/ioctl.rs b/cyw43/src/ioctl.rs
index 61524c274..f8b2d9aba 100644
--- a/cyw43/src/ioctl.rs
+++ b/cyw43/src/ioctl.rs
@@ -4,9 +4,10 @@ use core::task::{Poll, Waker};
4 4
5use embassy_sync::waitqueue::WakerRegistration; 5use embassy_sync::waitqueue::WakerRegistration;
6 6
7use crate::consts::Ioctl;
7use crate::fmt::Bytes; 8use crate::fmt::Bytes;
8 9
9#[derive(Clone, Copy)] 10#[derive(Clone, Copy, PartialEq, Eq)]
10pub enum IoctlType { 11pub enum IoctlType {
11 Get = 0, 12 Get = 0,
12 Set = 2, 13 Set = 2,
@@ -16,7 +17,7 @@ pub enum IoctlType {
16pub struct PendingIoctl { 17pub struct PendingIoctl {
17 pub buf: *mut [u8], 18 pub buf: *mut [u8],
18 pub kind: IoctlType, 19 pub kind: IoctlType,
19 pub cmd: u32, 20 pub cmd: Ioctl,
20 pub iface: u32, 21 pub iface: u32,
21} 22}
22 23
@@ -101,7 +102,7 @@ impl IoctlState {
101 self.state.set(IoctlStateInner::Done { resp_len: 0 }); 102 self.state.set(IoctlStateInner::Done { resp_len: 0 });
102 } 103 }
103 104
104 pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { 105 pub async fn do_ioctl(&self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {
105 self.state 106 self.state
106 .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); 107 .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface }));
107 self.wake_runner(); 108 self.wake_runner();
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
29use crate::bus::Bus; 29use crate::bus::Bus;
30pub use crate::bus::SpiBusCyw43; 30pub use crate::bus::SpiBusCyw43;
31pub use crate::control::{AddMulticastAddressError, Control, Error as ControlError, ScanOptions, Scanner}; 31pub use crate::control::{
32 AddMulticastAddressError, Control, Error as ControlError, JoinAuth, JoinOptions, ScanOptions, Scanner,
33};
32pub use crate::runner::Runner; 34pub use crate::runner::Runner;
33pub use crate::structs::BssInfo; 35pub use crate::structs::BssInfo;
34 36
diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs
index 959718341..77910b281 100644
--- a/cyw43/src/runner.rs
+++ b/cyw43/src/runner.rs
@@ -560,7 +560,7 @@ where
560 self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 560 self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0
561 } 561 }
562 562
563 async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8], buf: &mut [u32; 512]) { 563 async fn send_ioctl(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, data: &[u8], buf: &mut [u32; 512]) {
564 let buf8 = slice8_mut(buf); 564 let buf8 = slice8_mut(buf);
565 565
566 let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); 566 let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();
@@ -582,7 +582,7 @@ where
582 }; 582 };
583 583
584 let cdc_header = CdcHeader { 584 let cdc_header = CdcHeader {
585 cmd: cmd, 585 cmd: cmd as u32,
586 len: data.len() as _, 586 len: data.len() as _,
587 flags: kind as u16 | (iface as u16) << 12, 587 flags: kind as u16 | (iface as u16) << 12,
588 id: self.ioctl_id, 588 id: self.ioctl_id,
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)]
400pub struct SaePassphraseInfo {
401 pub len: u16,
402 pub passphrase: [u8; 128],
403}
404impl_bytes!(SaePassphraseInfo);
405
406#[derive(Clone, Copy)]
407#[cfg_attr(feature = "defmt", derive(defmt::Format))]
408#[repr(C)]
400pub struct SsidInfoWithIndex { 409pub 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
8use core::str::from_utf8; 8use core::str::from_utf8;
9 9
10use cyw43::JoinOptions;
10use cyw43_pio::PioSpi; 11use cyw43_pio::PioSpi;
11use defmt::*; 12use defmt::*;
12use embassy_executor::Spawner; 13use 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
8use core::str::from_utf8; 8use core::str::from_utf8;
9 9
10use cyw43::JoinOptions;
10use cyw43_pio::PioSpi; 11use cyw43_pio::PioSpi;
11use defmt::*; 12use defmt::*;
12use embassy_executor::Spawner; 13use 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]
3teleprobe_meta::target!(b"rpi-pico"); 3teleprobe_meta::target!(b"rpi-pico");
4 4
5use cyw43::JoinOptions;
5use cyw43_pio::PioSpi; 6use cyw43_pio::PioSpi;
6use defmt::{panic, *}; 7use defmt::{panic, *};
7use embassy_executor::Spawner; 8use 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);