aboutsummaryrefslogtreecommitdiff
path: root/cyw43/src/control.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cyw43/src/control.rs')
-rw-r--r--cyw43/src/control.rs457
1 files changed, 457 insertions, 0 deletions
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs
new file mode 100644
index 000000000..6919d569e
--- /dev/null
+++ b/cyw43/src/control.rs
@@ -0,0 +1,457 @@
1use core::cmp::{max, min};
2
3use ch::driver::LinkState;
4use embassy_net_driver_channel as ch;
5use embassy_time::{Duration, Timer};
6
7pub use crate::bus::SpiBusCyw43;
8use crate::consts::*;
9use crate::events::{Event, EventSubscriber, Events};
10use crate::fmt::Bytes;
11use crate::ioctl::{IoctlState, IoctlType};
12use crate::structs::*;
13use crate::{countries, events, PowerManagementMode};
14
15#[derive(Debug)]
16pub struct Error {
17 pub status: u32,
18}
19
20pub struct Control<'a> {
21 state_ch: ch::StateRunner<'a>,
22 events: &'a Events,
23 ioctl_state: &'a IoctlState,
24}
25
26impl<'a> Control<'a> {
27 pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
28 Self {
29 state_ch,
30 events: event_sub,
31 ioctl_state,
32 }
33 }
34
35 pub async fn init(&mut self, clm: &[u8]) {
36 const CHUNK_SIZE: usize = 1024;
37
38 debug!("Downloading CLM...");
39
40 let mut offs = 0;
41 for chunk in clm.chunks(CHUNK_SIZE) {
42 let mut flag = DOWNLOAD_FLAG_HANDLER_VER;
43 if offs == 0 {
44 flag |= DOWNLOAD_FLAG_BEGIN;
45 }
46 offs += chunk.len();
47 if offs == clm.len() {
48 flag |= DOWNLOAD_FLAG_END;
49 }
50
51 let header = DownloadHeader {
52 flag,
53 dload_type: DOWNLOAD_TYPE_CLM,
54 len: chunk.len() as _,
55 crc: 0,
56 };
57 let mut buf = [0; 8 + 12 + CHUNK_SIZE];
58 buf[0..8].copy_from_slice(b"clmload\x00");
59 buf[8..20].copy_from_slice(&header.to_bytes());
60 buf[20..][..chunk.len()].copy_from_slice(&chunk);
61 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()])
62 .await;
63 }
64
65 // check clmload ok
66 assert_eq!(self.get_iovar_u32("clmload_status").await, 0);
67
68 debug!("Configuring misc stuff...");
69
70 // Disable tx gloming which transfers multiple packets in one request.
71 // 'glom' is short for "conglomerate" which means "gather together into
72 // a compact mass".
73 self.set_iovar_u32("bus:txglom", 0).await;
74 self.set_iovar_u32("apsta", 1).await;
75
76 // read MAC addr.
77 let mut mac_addr = [0; 6];
78 assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6);
79 debug!("mac addr: {:02x}", Bytes(&mac_addr));
80
81 let country = countries::WORLD_WIDE_XX;
82 let country_info = CountryInfo {
83 country_abbrev: [country.code[0], country.code[1], 0, 0],
84 country_code: [country.code[0], country.code[1], 0, 0],
85 rev: if country.rev == 0 { -1 } else { country.rev as _ },
86 };
87 self.set_iovar("country", &country_info.to_bytes()).await;
88
89 // set country takes some time, next ioctls fail if we don't wait.
90 Timer::after(Duration::from_millis(100)).await;
91
92 // Set antenna to chip antenna
93 self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
94
95 self.set_iovar_u32("bus:txglom", 0).await;
96 Timer::after(Duration::from_millis(100)).await;
97 //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...??
98 //Timer::after(Duration::from_millis(100)).await;
99 self.set_iovar_u32("ampdu_ba_wsize", 8).await;
100 Timer::after(Duration::from_millis(100)).await;
101 self.set_iovar_u32("ampdu_mpdu", 4).await;
102 Timer::after(Duration::from_millis(100)).await;
103 //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes
104
105 //Timer::after(Duration::from_millis(100)).await;
106
107 // evts
108 let mut evts = EventMask {
109 iface: 0,
110 events: [0xFF; 24],
111 };
112
113 // Disable spammy uninteresting events.
114 evts.unset(Event::RADIO);
115 evts.unset(Event::IF);
116 evts.unset(Event::PROBREQ_MSG);
117 evts.unset(Event::PROBREQ_MSG_RX);
118 evts.unset(Event::PROBRESP_MSG);
119 evts.unset(Event::PROBRESP_MSG);
120 evts.unset(Event::ROAM);
121
122 self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await;
123
124 Timer::after(Duration::from_millis(100)).await;
125
126 // set wifi up
127 self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
128
129 Timer::after(Duration::from_millis(100)).await;
130
131 self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
132 self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
133
134 Timer::after(Duration::from_millis(100)).await;
135
136 self.state_ch.set_ethernet_address(mac_addr);
137
138 debug!("INIT DONE");
139 }
140
141 pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
142 // power save mode
143 let mode_num = mode.mode();
144 if mode_num == 2 {
145 self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await;
146 self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await;
147 self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await;
148 self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await;
149 }
150 self.ioctl_set_u32(86, 0, mode_num).await;
151 }
152
153 pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> {
154 self.set_iovar_u32("ampdu_ba_wsize", 8).await;
155
156 self.ioctl_set_u32(134, 0, 0).await; // wsec = open
157 self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await;
158 self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1
159 self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0)
160
161 let mut i = SsidInfo {
162 len: ssid.len() as _,
163 ssid: [0; 32],
164 };
165 i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes());
166
167 self.wait_for_join(i).await
168 }
169
170 pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
171 self.set_iovar_u32("ampdu_ba_wsize", 8).await;
172
173 self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2
174 self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await;
175 self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
176 self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
177
178 Timer::after(Duration::from_millis(100)).await;
179
180 let mut pfi = PassphraseInfo {
181 len: passphrase.len() as _,
182 flags: 1,
183 passphrase: [0; 64],
184 };
185 pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes());
186 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes())
187 .await; // WLC_SET_WSEC_PMK
188
189 self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1
190 self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open)
191 self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth
192
193 let mut i = SsidInfo {
194 len: ssid.len() as _,
195 ssid: [0; 32],
196 };
197 i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes());
198
199 self.wait_for_join(i).await
200 }
201
202 async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> {
203 self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]);
204 let mut subscriber = self.events.queue.subscriber().unwrap();
205 // the actual join operation starts here
206 // we make sure to enable events before so we don't miss any
207
208 // set_ssid
209 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes())
210 .await;
211
212 // to complete the join, we wait for a SET_SSID event
213 // we also save the AUTH status for the user, it may be interesting
214 let mut auth_status = 0;
215 let status = loop {
216 let msg = subscriber.next_message_pure().await;
217 if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS {
218 auth_status = msg.header.status;
219 } else if msg.header.event_type == Event::SET_SSID {
220 // join operation ends with SET_SSID event
221 break msg.header.status;
222 }
223 };
224
225 self.events.mask.disable_all();
226 if status == EStatus::SUCCESS {
227 // successful join
228 self.state_ch.set_link_state(LinkState::Up);
229 debug!("JOINED");
230 Ok(())
231 } else {
232 warn!("JOIN failed with status={} auth={}", status, auth_status);
233 Err(Error { status })
234 }
235 }
236
237 pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) {
238 assert!(gpio_n < 3);
239 self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 })
240 .await
241 }
242
243 pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) {
244 self.start_ap(ssid, "", Security::OPEN, channel).await;
245 }
246
247 pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) {
248 self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await;
249 }
250
251 async fn start_ap(&mut self, ssid: &str, passphrase: &str, security: Security, channel: u8) {
252 if security != Security::OPEN
253 && (passphrase.as_bytes().len() < MIN_PSK_LEN || passphrase.as_bytes().len() > MAX_PSK_LEN)
254 {
255 panic!("Passphrase is too short or too long");
256 }
257
258 // Temporarily set wifi down
259 self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
260
261 // Turn off APSTA mode
262 self.set_iovar_u32("apsta", 0).await;
263
264 // Set wifi up again
265 self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
266
267 // Turn on AP mode
268 self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await;
269
270 // Set SSID
271 let mut i = SsidInfoWithIndex {
272 index: 0,
273 ssid_info: SsidInfo {
274 len: ssid.as_bytes().len() as _,
275 ssid: [0; 32],
276 },
277 };
278 i.ssid_info.ssid[..ssid.as_bytes().len()].copy_from_slice(ssid.as_bytes());
279 self.set_iovar("bsscfg:ssid", &i.to_bytes()).await;
280
281 // Set channel number
282 self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await;
283
284 // Set security
285 self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await;
286
287 if security != Security::OPEN {
288 self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK
289
290 Timer::after(Duration::from_millis(100)).await;
291
292 // Set passphrase
293 let mut pfi = PassphraseInfo {
294 len: passphrase.as_bytes().len() as _,
295 flags: 1, // WSEC_PASSPHRASE
296 passphrase: [0; 64],
297 };
298 pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes());
299 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes())
300 .await;
301 }
302
303 // Change mutlicast rate from 1 Mbps to 11 Mbps
304 self.set_iovar_u32("2g_mrate", 11000000 / 500000).await;
305
306 // Start AP
307 self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP
308 }
309
310 async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
311 let mut buf = [0; 8];
312 buf[0..4].copy_from_slice(&val1.to_le_bytes());
313 buf[4..8].copy_from_slice(&val2.to_le_bytes());
314 self.set_iovar(name, &buf).await
315 }
316
317 async fn set_iovar_u32(&mut self, name: &str, val: u32) {
318 self.set_iovar(name, &val.to_le_bytes()).await
319 }
320
321 async fn get_iovar_u32(&mut self, name: &str) -> u32 {
322 let mut buf = [0; 4];
323 let len = self.get_iovar(name, &mut buf).await;
324 assert_eq!(len, 4);
325 u32::from_le_bytes(buf)
326 }
327
328 async fn set_iovar(&mut self, name: &str, val: &[u8]) {
329 self.set_iovar_v::<64>(name, val).await
330 }
331
332 async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) {
333 debug!("set {} = {:02x}", name, Bytes(val));
334
335 let mut buf = [0; BUFSIZE];
336 buf[..name.len()].copy_from_slice(name.as_bytes());
337 buf[name.len()] = 0;
338 buf[name.len() + 1..][..val.len()].copy_from_slice(val);
339
340 let total_len = name.len() + 1 + val.len();
341 self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len])
342 .await;
343 }
344
345 // TODO this is not really working, it always returns all zeros.
346 async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize {
347 debug!("get {}", name);
348
349 let mut buf = [0; 64];
350 buf[..name.len()].copy_from_slice(name.as_bytes());
351 buf[name.len()] = 0;
352
353 let total_len = max(name.len() + 1, res.len());
354 let res_len = self
355 .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len])
356 .await;
357
358 let out_len = min(res.len(), res_len);
359 res[..out_len].copy_from_slice(&buf[..out_len]);
360 out_len
361 }
362
363 async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) {
364 let mut buf = val.to_le_bytes();
365 self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await;
366 }
367
368 async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize {
369 struct CancelOnDrop<'a>(&'a IoctlState);
370
371 impl CancelOnDrop<'_> {
372 fn defuse(self) {
373 core::mem::forget(self);
374 }
375 }
376
377 impl Drop for CancelOnDrop<'_> {
378 fn drop(&mut self) {
379 self.0.cancel_ioctl();
380 }
381 }
382
383 let ioctl = CancelOnDrop(self.ioctl_state);
384
385 ioctl.0.do_ioctl(kind, cmd, iface, buf).await;
386 let resp_len = ioctl.0.wait_complete().await;
387
388 ioctl.defuse();
389
390 resp_len
391 }
392
393 /// Start a wifi scan
394 ///
395 /// Returns a `Stream` of networks found by the device
396 ///
397 /// # Note
398 /// Device events are currently implemented using a bounded queue.
399 /// To not miss any events, you should make sure to always await the stream.
400 pub async fn scan(&mut self) -> Scanner<'_> {
401 const SCANTYPE_PASSIVE: u8 = 1;
402
403 let scan_params = ScanParams {
404 version: 1,
405 action: 1,
406 sync_id: 1,
407 ssid_len: 0,
408 ssid: [0; 32],
409 bssid: [0xff; 6],
410 bss_type: 2,
411 scan_type: SCANTYPE_PASSIVE,
412 nprobes: !0,
413 active_time: !0,
414 passive_time: !0,
415 home_time: !0,
416 channel_num: 0,
417 channel_list: [0; 1],
418 };
419
420 self.events.mask.enable(&[Event::ESCAN_RESULT]);
421 let subscriber = self.events.queue.subscriber().unwrap();
422 self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await;
423
424 Scanner {
425 subscriber,
426 events: &self.events,
427 }
428 }
429}
430
431pub struct Scanner<'a> {
432 subscriber: EventSubscriber<'a>,
433 events: &'a Events,
434}
435
436impl Scanner<'_> {
437 /// wait for the next found network
438 pub async fn next(&mut self) -> Option<BssInfo> {
439 let event = self.subscriber.next_message_pure().await;
440 if event.header.status != EStatus::PARTIAL {
441 self.events.mask.disable_all();
442 return None;
443 }
444
445 if let events::Payload::BssInfo(bss) = event.payload {
446 Some(bss)
447 } else {
448 None
449 }
450 }
451}
452
453impl Drop for Scanner<'_> {
454 fn drop(&mut self) {
455 self.events.mask.disable_all();
456 }
457}