aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-07-15 19:15:01 -0500
committerxoviat <[email protected]>2023-07-15 19:15:01 -0500
commitcd592cb0550146158ea6f9d90ba8afe9e1b06a92 (patch)
tree4878db7dc91d865beea9de1fd8c8f157c6b4c64d
parent0b63af33135784c1410dc8667cfefbaa538a1f04 (diff)
wpan: add files from cyw43
-rw-r--r--embassy-stm32-wpan/src/lib.rs3
-rw-r--r--embassy-stm32-wpan/src/mac/control.rs454
-rw-r--r--embassy-stm32-wpan/src/mac/driver.rs102
-rw-r--r--embassy-stm32-wpan/src/mac/mod.rs92
-rw-r--r--embassy-stm32-wpan/src/mac/runner.rs489
5 files changed, 1139 insertions, 1 deletions
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index 57f0dc4fa..6836d7a8b 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -1,5 +1,6 @@
1#![no_std] 1#![no_std]
2#![cfg_attr(feature = "ble", feature(async_fn_in_trait))] 2#![cfg_attr(any(feature = "ble", feature = "mac"), feature(async_fn_in_trait))]
3#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))]
3 4
4// This must go FIRST so that all the other modules see its macros. 5// This must go FIRST so that all the other modules see its macros.
5pub mod fmt; 6pub mod fmt;
diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs
new file mode 100644
index 000000000..c67614dd6
--- /dev/null
+++ b/embassy-stm32-wpan/src/mac/control.rs
@@ -0,0 +1,454 @@
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 let resp_len = ioctl.0.do_ioctl(kind, cmd, iface, buf).await;
385 ioctl.defuse();
386
387 resp_len
388 }
389
390 /// Start a wifi scan
391 ///
392 /// Returns a `Stream` of networks found by the device
393 ///
394 /// # Note
395 /// Device events are currently implemented using a bounded queue.
396 /// To not miss any events, you should make sure to always await the stream.
397 pub async fn scan(&mut self) -> Scanner<'_> {
398 const SCANTYPE_PASSIVE: u8 = 1;
399
400 let scan_params = ScanParams {
401 version: 1,
402 action: 1,
403 sync_id: 1,
404 ssid_len: 0,
405 ssid: [0; 32],
406 bssid: [0xff; 6],
407 bss_type: 2,
408 scan_type: SCANTYPE_PASSIVE,
409 nprobes: !0,
410 active_time: !0,
411 passive_time: !0,
412 home_time: !0,
413 channel_num: 0,
414 channel_list: [0; 1],
415 };
416
417 self.events.mask.enable(&[Event::ESCAN_RESULT]);
418 let subscriber = self.events.queue.subscriber().unwrap();
419 self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await;
420
421 Scanner {
422 subscriber,
423 events: &self.events,
424 }
425 }
426}
427
428pub struct Scanner<'a> {
429 subscriber: EventSubscriber<'a>,
430 events: &'a Events,
431}
432
433impl Scanner<'_> {
434 /// wait for the next found network
435 pub async fn next(&mut self) -> Option<BssInfo> {
436 let event = self.subscriber.next_message_pure().await;
437 if event.header.status != EStatus::PARTIAL {
438 self.events.mask.disable_all();
439 return None;
440 }
441
442 if let events::Payload::BssInfo(bss) = event.payload {
443 Some(bss)
444 } else {
445 None
446 }
447 }
448}
449
450impl Drop for Scanner<'_> {
451 fn drop(&mut self) {
452 self.events.mask.disable_all();
453 }
454}
diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs
new file mode 100644
index 000000000..3171d61fe
--- /dev/null
+++ b/embassy-stm32-wpan/src/mac/driver.rs
@@ -0,0 +1,102 @@
1#![no_std]
2#![no_main]
3#![allow(incomplete_features)]
4#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
5#![deny(unused_must_use)]
6
7use core::slice;
8
9use embassy_net_driver_channel as ch;
10use embedded_hal_1::digital::OutputPin;
11use events::Events;
12use ioctl::IoctlState;
13
14use crate::bus::Bus;
15pub use crate::bus::SpiBusCyw43;
16pub use crate::control::{Control, Error as ControlError};
17pub use crate::runner::Runner;
18pub use crate::structs::BssInfo;
19
20const MTU: usize = 1514;
21
22pub struct State {
23 ioctl_state: IoctlState,
24 ch: ch::State<MTU, 4, 4>,
25 events: Events,
26}
27
28impl State {
29 pub fn new() -> Self {
30 Self {
31 ioctl_state: IoctlState::new(),
32 ch: ch::State::new(),
33 events: Events::new(),
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum PowerManagementMode {
40 /// Custom, officially unsupported mode. Use at your own risk.
41 /// All power-saving features set to their max at only a marginal decrease in power consumption
42 /// as oppposed to `Aggressive`.
43 SuperSave,
44
45 /// Aggressive power saving mode.
46 Aggressive,
47
48 /// The default mode.
49 PowerSave,
50
51 /// Performance is prefered over power consumption but still some power is conserved as opposed to
52 /// `None`.
53 Performance,
54
55 /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of
56 /// a much lower throughput.
57 ThroughputThrottling,
58
59 /// No power management is configured. This consumes the most power.
60 None,
61}
62
63impl Default for PowerManagementMode {
64 fn default() -> Self {
65 Self::PowerSave
66 }
67}
68
69impl PowerManagementMode {
70 // TODO
71}
72
73pub type NetDriver<'a> = ch::Device<'a, MTU>;
74
75pub async fn new<'a, PWR, SPI>(
76 state: &'a mut State,
77 pwr: PWR,
78 spi: SPI,
79 firmware: &[u8],
80) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>)
81where
82 PWR: OutputPin,
83 SPI: SpiBusCyw43,
84{
85 let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
86 let state_ch = ch_runner.state_runner();
87
88 let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);
89
90 runner.init(firmware).await;
91
92 (
93 device,
94 Control::new(state_ch, &state.events, &state.ioctl_state),
95 runner,
96 )
97}
98
99fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
100 let len = x.len() * 4;
101 unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
102}
diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs
index 1af8fe6ba..e27f0ba73 100644
--- a/embassy-stm32-wpan/src/mac/mod.rs
+++ b/embassy-stm32-wpan/src/mac/mod.rs
@@ -1,9 +1,101 @@
1pub mod commands; 1pub mod commands;
2mod consts; 2mod consts;
3pub mod control;
3pub mod event; 4pub mod event;
4mod helpers; 5mod helpers;
5pub mod indications; 6pub mod indications;
6mod macros; 7mod macros;
7mod opcodes; 8mod opcodes;
8pub mod responses; 9pub mod responses;
10pub mod runner;
9pub mod typedefs; 11pub mod typedefs;
12
13use core::slice;
14
15use embassy_net_driver_channel as ch;
16
17use crate::bus::Bus;
18pub use crate::bus::SpiBusCyw43;
19pub use crate::mac::control::{Control, Error as ControlError};
20pub use crate::runner::Runner;
21pub use crate::structs::BssInfo;
22use crate::sub::mac::Mac;
23
24const MTU: usize = 1514;
25
26pub struct State {
27 ioctl_state: IoctlState,
28 ch: ch::State<MTU, 4, 4>,
29 events: Events,
30}
31
32impl State {
33 pub fn new() -> Self {
34 Self {
35 ioctl_state: IoctlState::new(),
36 ch: ch::State::new(),
37 events: Events::new(),
38 }
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum PowerManagementMode {
44 /// Custom, officially unsupported mode. Use at your own risk.
45 /// All power-saving features set to their max at only a marginal decrease in power consumption
46 /// as oppposed to `Aggressive`.
47 SuperSave,
48
49 /// Aggressive power saving mode.
50 Aggressive,
51
52 /// The default mode.
53 PowerSave,
54
55 /// Performance is prefered over power consumption but still some power is conserved as opposed to
56 /// `None`.
57 Performance,
58
59 /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of
60 /// a much lower throughput.
61 ThroughputThrottling,
62
63 /// No power management is configured. This consumes the most power.
64 None,
65}
66
67impl Default for PowerManagementMode {
68 fn default() -> Self {
69 Self::PowerSave
70 }
71}
72
73impl PowerManagementMode {
74 // TODO
75}
76
77pub type NetDriver<'a> = ch::Device<'a, MTU>;
78
79pub async fn new<'a, PWR, SPI>(
80 state: &'a mut State,
81 mac_subsystem: Mac,
82 firmware: &[u8],
83) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) {
84 let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
85 let state_ch = ch_runner.state_runner();
86
87 let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);
88
89 runner.init(firmware).await;
90
91 (
92 device,
93 Control::new(state_ch, &state.events, &state.ioctl_state),
94 runner,
95 )
96}
97
98fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
99 let len = x.len() * 4;
100 unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
101}
diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs
new file mode 100644
index 000000000..7f0bbd670
--- /dev/null
+++ b/embassy-stm32-wpan/src/mac/runner.rs
@@ -0,0 +1,489 @@
1use embassy_futures::select::{select3, Either3};
2use embassy_net_driver_channel as ch;
3use embassy_sync::pubsub::PubSubBehavior;
4
5use crate::sub::mac::Mac;
6
7#[cfg(feature = "firmware-logs")]
8struct LogState {
9 addr: u32,
10 last_idx: usize,
11 buf: [u8; 256],
12 buf_count: usize,
13}
14
15#[cfg(feature = "firmware-logs")]
16impl Default for LogState {
17 fn default() -> Self {
18 Self {
19 addr: Default::default(),
20 last_idx: Default::default(),
21 buf: [0; 256],
22 buf_count: Default::default(),
23 }
24 }
25}
26
27pub struct Runner<'a, PWR, SPI> {
28 ch: ch::Runner<'a, MTU>,
29 mac: Mac,
30
31 ioctl_state: &'a IoctlState,
32 ioctl_id: u16,
33 sdpcm_seq: u8,
34 sdpcm_seq_max: u8,
35
36 events: &'a Events,
37
38 #[cfg(feature = "firmware-logs")]
39 log: LogState,
40}
41
42impl<'a, PWR, SPI> Runner<'a, PWR, SPI>
43where
44 PWR: OutputPin,
45 SPI: SpiBusCyw43,
46{
47 pub(crate) fn new(
48 ch: ch::Runner<'a, MTU>,
49 bus: Bus<PWR, SPI>,
50 ioctl_state: &'a IoctlState,
51 events: &'a Events,
52 ) -> Self {
53 Self {
54 ch,
55 bus,
56 ioctl_state,
57 ioctl_id: 0,
58 sdpcm_seq: 0,
59 sdpcm_seq_max: 1,
60 events,
61 #[cfg(feature = "firmware-logs")]
62 log: LogState::default(),
63 }
64 }
65
66 pub(crate) async fn init(&mut self, firmware: &[u8]) {
67 self.bus.init().await;
68
69 #[cfg(feature = "firmware-logs")]
70 self.log_init().await;
71
72 debug!("wifi init done");
73 }
74
75 #[cfg(feature = "firmware-logs")]
76 async fn log_init(&mut self) {
77 // Initialize shared memory for logging.
78
79 let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size;
80 let shared_addr = self.bus.bp_read32(addr).await;
81 debug!("shared_addr {:08x}", shared_addr);
82
83 let mut shared = [0; SharedMemData::SIZE];
84 self.bus.bp_read(shared_addr, &mut shared).await;
85 let shared = SharedMemData::from_bytes(&shared);
86
87 self.log.addr = shared.console_addr + 8;
88 }
89
90 #[cfg(feature = "firmware-logs")]
91 async fn log_read(&mut self) {
92 // Read log struct
93 let mut log = [0; SharedMemLog::SIZE];
94 self.bus.bp_read(self.log.addr, &mut log).await;
95 let log = SharedMemLog::from_bytes(&log);
96
97 let idx = log.idx as usize;
98
99 // If pointer hasn't moved, no need to do anything.
100 if idx == self.log.last_idx {
101 return;
102 }
103
104 // Read entire buf for now. We could read only what we need, but then we
105 // run into annoying alignment issues in `bp_read`.
106 let mut buf = [0; 0x400];
107 self.bus.bp_read(log.buf, &mut buf).await;
108
109 while self.log.last_idx != idx as usize {
110 let b = buf[self.log.last_idx];
111 if b == b'\r' || b == b'\n' {
112 if self.log.buf_count != 0 {
113 let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) };
114 debug!("LOGS: {}", s);
115 self.log.buf_count = 0;
116 }
117 } else if self.log.buf_count < self.log.buf.len() {
118 self.log.buf[self.log.buf_count] = b;
119 self.log.buf_count += 1;
120 }
121
122 self.log.last_idx += 1;
123 if self.log.last_idx == 0x400 {
124 self.log.last_idx = 0;
125 }
126 }
127 }
128
129 pub async fn run(mut self) -> ! {
130 let mut buf = [0; 512];
131 loop {
132 #[cfg(feature = "firmware-logs")]
133 self.log_read().await;
134
135 if self.has_credit() {
136 let ioctl = self.ioctl_state.wait_pending();
137 let tx = self.ch.tx_buf();
138 let ev = self.bus.wait_for_event();
139
140 match select3(ioctl, tx, ev).await {
141 Either3::First(PendingIoctl {
142 buf: iobuf,
143 kind,
144 cmd,
145 iface,
146 }) => {
147 self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await;
148 self.check_status(&mut buf).await;
149 }
150 Either3::Second(packet) => {
151 trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
152
153 let mut buf = [0; 512];
154 let buf8 = slice8_mut(&mut buf);
155
156 // There MUST be 2 bytes of padding between the SDPCM and BDC headers.
157 // And ONLY for data packets!
158 // No idea why, but the firmware will append two zero bytes to the tx'd packets
159 // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it
160 // be oversized and get dropped.
161 // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90
162 // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597
163 // ¯\_(ツ)_/¯
164 const PADDING_SIZE: usize = 2;
165 let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len();
166
167 let seq = self.sdpcm_seq;
168 self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1);
169
170 let sdpcm_header = SdpcmHeader {
171 len: total_len as u16, // TODO does this len need to be rounded up to u32?
172 len_inv: !total_len as u16,
173 sequence: seq,
174 channel_and_flags: CHANNEL_TYPE_DATA,
175 next_length: 0,
176 header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _,
177 wireless_flow_control: 0,
178 bus_data_credit: 0,
179 reserved: [0, 0],
180 };
181
182 let bdc_header = BdcHeader {
183 flags: BDC_VERSION << BDC_VERSION_SHIFT,
184 priority: 0,
185 flags2: 0,
186 data_offset: 0,
187 };
188 trace!("tx {:?}", sdpcm_header);
189 trace!(" {:?}", bdc_header);
190
191 buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes());
192 buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE]
193 .copy_from_slice(&bdc_header.to_bytes());
194 buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()]
195 .copy_from_slice(packet);
196
197 let total_len = (total_len + 3) & !3; // round up to 4byte
198
199 trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)]));
200
201 self.bus.wlan_write(&buf[..(total_len / 4)]).await;
202 self.ch.tx_done();
203 self.check_status(&mut buf).await;
204 }
205 Either3::Third(()) => {
206 self.handle_irq(&mut buf).await;
207 }
208 }
209 } else {
210 warn!("TX stalled");
211 self.bus.wait_for_event().await;
212 self.handle_irq(&mut buf).await;
213 }
214 }
215 }
216
217 /// Wait for IRQ on F2 packet available
218 async fn handle_irq(&mut self, buf: &mut [u32; 512]) {
219 // Receive stuff
220 let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await;
221 trace!("irq{}", FormatInterrupt(irq));
222
223 if irq & IRQ_F2_PACKET_AVAILABLE != 0 {
224 self.check_status(buf).await;
225 }
226
227 if irq & IRQ_DATA_UNAVAILABLE != 0 {
228 // TODO what should we do here?
229 warn!("IRQ DATA_UNAVAILABLE, clearing...");
230 self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await;
231 }
232 }
233
234 /// Handle F2 events while status register is set
235 async fn check_status(&mut self, buf: &mut [u32; 512]) {
236 loop {
237 let status = self.bus.status();
238 trace!("check status{}", FormatStatus(status));
239
240 if status & STATUS_F2_PKT_AVAILABLE != 0 {
241 let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT;
242 self.bus.wlan_read(buf, len).await;
243 trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)]));
244 self.rx(&mut slice8_mut(buf)[..len as usize]);
245 } else {
246 break;
247 }
248 }
249 }
250
251 fn rx(&mut self, packet: &mut [u8]) {
252 let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else {
253 return;
254 };
255
256 self.update_credit(&sdpcm_header);
257
258 let channel = sdpcm_header.channel_and_flags & 0x0f;
259
260 match channel {
261 CHANNEL_TYPE_CONTROL => {
262 let Some((cdc_header, response)) = CdcHeader::parse(payload) else {
263 return;
264 };
265 trace!(" {:?}", cdc_header);
266
267 if cdc_header.id == self.ioctl_id {
268 if cdc_header.status != 0 {
269 // TODO: propagate error instead
270 panic!("IOCTL error {}", cdc_header.status as i32);
271 }
272
273 self.ioctl_state.ioctl_done(response);
274 }
275 }
276 CHANNEL_TYPE_EVENT => {
277 let Some((_, bdc_packet)) = BdcHeader::parse(payload) else {
278 warn!("BDC event, incomplete header");
279 return;
280 };
281
282 let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else {
283 warn!("BDC event, incomplete data");
284 return;
285 };
286
287 const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h
288 if event_packet.eth.ether_type != ETH_P_LINK_CTL {
289 warn!(
290 "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}",
291 event_packet.eth.ether_type, ETH_P_LINK_CTL
292 );
293 return;
294 }
295 const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18];
296 if event_packet.hdr.oui != BROADCOM_OUI {
297 warn!(
298 "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}",
299 Bytes(&event_packet.hdr.oui),
300 Bytes(BROADCOM_OUI)
301 );
302 return;
303 }
304 const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769;
305 if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG {
306 warn!("unexpected subtype {}", event_packet.hdr.subtype);
307 return;
308 }
309
310 const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1;
311 if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT {
312 warn!("unexpected user_subtype {}", event_packet.hdr.subtype);
313 return;
314 }
315
316 let evt_type = events::Event::from(event_packet.msg.event_type as u8);
317 debug!(
318 "=== EVENT {:?}: {:?} {:02x}",
319 evt_type,
320 event_packet.msg,
321 Bytes(evt_data)
322 );
323
324 if self.events.mask.is_enabled(evt_type) {
325 let status = event_packet.msg.status;
326 let event_payload = match evt_type {
327 Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
328 let Some((_, bss_info)) = ScanResults::parse(evt_data) else {
329 return;
330 };
331 let Some(bss_info) = BssInfo::parse(bss_info) else {
332 return;
333 };
334 events::Payload::BssInfo(*bss_info)
335 }
336 Event::ESCAN_RESULT => events::Payload::None,
337 _ => events::Payload::None,
338 };
339
340 // this intentionally uses the non-blocking publish immediate
341 // publish() is a deadlock risk in the current design as awaiting here prevents ioctls
342 // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event
343 // (if they are actively awaiting the queue)
344 self.events.queue.publish_immediate(events::Message::new(
345 Status {
346 event_type: evt_type,
347 status,
348 },
349 event_payload,
350 ));
351 }
352 }
353 CHANNEL_TYPE_DATA => {
354 let Some((_, packet)) = BdcHeader::parse(payload) else {
355 return;
356 };
357 trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
358
359 match self.ch.try_rx_buf() {
360 Some(buf) => {
361 buf[..packet.len()].copy_from_slice(packet);
362 self.ch.rx_done(packet.len())
363 }
364 None => warn!("failed to push rxd packet to the channel."),
365 }
366 }
367 _ => {}
368 }
369 }
370
371 fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) {
372 if sdpcm_header.channel_and_flags & 0xf < 3 {
373 let mut sdpcm_seq_max = sdpcm_header.bus_data_credit;
374 if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 {
375 sdpcm_seq_max = self.sdpcm_seq + 2;
376 }
377 self.sdpcm_seq_max = sdpcm_seq_max;
378 }
379 }
380
381 fn has_credit(&self) -> bool {
382 self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0
383 }
384
385 async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) {
386 let mut buf = [0; 512];
387 let buf8 = slice8_mut(&mut buf);
388
389 let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();
390
391 let sdpcm_seq = self.sdpcm_seq;
392 self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1);
393 self.ioctl_id = self.ioctl_id.wrapping_add(1);
394
395 let sdpcm_header = SdpcmHeader {
396 len: total_len as u16, // TODO does this len need to be rounded up to u32?
397 len_inv: !total_len as u16,
398 sequence: sdpcm_seq,
399 channel_and_flags: CHANNEL_TYPE_CONTROL,
400 next_length: 0,
401 header_length: SdpcmHeader::SIZE as _,
402 wireless_flow_control: 0,
403 bus_data_credit: 0,
404 reserved: [0, 0],
405 };
406
407 let cdc_header = CdcHeader {
408 cmd: cmd,
409 len: data.len() as _,
410 flags: kind as u16 | (iface as u16) << 12,
411 id: self.ioctl_id,
412 status: 0,
413 };
414 trace!("tx {:?}", sdpcm_header);
415 trace!(" {:?}", cdc_header);
416
417 buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes());
418 buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes());
419 buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data);
420
421 let total_len = (total_len + 3) & !3; // round up to 4byte
422
423 trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)]));
424
425 self.bus.wlan_write(&buf[..total_len / 4]).await;
426 }
427
428 async fn core_disable(&mut self, core: Core) {
429 let base = core.base_addr();
430
431 // Dummy read?
432 let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
433
434 // Check it isn't already reset
435 let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
436 if r & AI_RESETCTRL_BIT_RESET != 0 {
437 return;
438 }
439
440 self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await;
441 let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
442
443 block_for(Duration::from_millis(1));
444
445 self.bus
446 .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET)
447 .await;
448 let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
449 }
450
451 async fn core_reset(&mut self, core: Core) {
452 self.core_disable(core).await;
453
454 let base = core.base_addr();
455 self.bus
456 .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN)
457 .await;
458 let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
459
460 self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
461
462 Timer::after(Duration::from_millis(1)).await;
463
464 self.bus
465 .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
466 .await;
467 let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
468
469 Timer::after(Duration::from_millis(1)).await;
470 }
471
472 async fn core_is_up(&mut self, core: Core) -> bool {
473 let base = core.base_addr();
474
475 let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
476 if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN {
477 debug!("core_is_up: returning false due to bad ioctrl {:02x}", io);
478 return false;
479 }
480
481 let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
482 if r & (AI_RESETCTRL_BIT_RESET) != 0 {
483 debug!("core_is_up: returning false due to bad resetctrl {:02x}", r);
484 return false;
485 }
486
487 true
488 }
489}