diff options
| author | xoviat <[email protected]> | 2023-07-17 19:26:58 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-07-17 19:26:58 -0500 |
| commit | 8f23b6faa6f04f83ece119e94335f892d516f6b3 (patch) | |
| tree | a83d6eaa1623a3ff8a6e6536e4e59523ee499c38 | |
| parent | 1d2c47273db6593a53018b1643dcadb491afc3de (diff) | |
wpan: refactor control, driver
| -rw-r--r-- | embassy-stm32-wpan/src/mac/control.rs | 446 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/driver.rs | 162 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/event.rs | 120 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/ioctl.rs | 124 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/mod.rs | 83 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/runner.rs | 329 |
6 files changed, 106 insertions, 1158 deletions
diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index c67614dd6..6e45e595f 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs | |||
| @@ -1,16 +1,4 @@ | |||
| 1 | use core::cmp::{max, min}; | 1 | use crate::mac::runner::Runner; |
| 2 | |||
| 3 | use ch::driver::LinkState; | ||
| 4 | use embassy_net_driver_channel as ch; | ||
| 5 | use embassy_time::{Duration, Timer}; | ||
| 6 | |||
| 7 | pub use crate::bus::SpiBusCyw43; | ||
| 8 | use crate::consts::*; | ||
| 9 | use crate::events::{Event, EventSubscriber, Events}; | ||
| 10 | use crate::fmt::Bytes; | ||
| 11 | use crate::ioctl::{IoctlState, IoctlType}; | ||
| 12 | use crate::structs::*; | ||
| 13 | use crate::{countries, events, PowerManagementMode}; | ||
| 14 | 2 | ||
| 15 | #[derive(Debug)] | 3 | #[derive(Debug)] |
| 16 | pub struct Error { | 4 | pub struct Error { |
| @@ -18,437 +6,15 @@ pub struct Error { | |||
| 18 | } | 6 | } |
| 19 | 7 | ||
| 20 | pub struct Control<'a> { | 8 | pub struct Control<'a> { |
| 21 | state_ch: ch::StateRunner<'a>, | 9 | runner: &'a Runner, |
| 22 | events: &'a Events, | ||
| 23 | ioctl_state: &'a IoctlState, | ||
| 24 | } | 10 | } |
| 25 | 11 | ||
| 26 | impl<'a> Control<'a> { | 12 | impl<'a> Control<'a> { |
| 27 | pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { | 13 | pub(crate) fn new(runner: &'a Runner) -> Self { |
| 28 | Self { | 14 | Self { runner: runner } |
| 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 | } | 15 | } |
| 257 | 16 | ||
| 258 | // Temporarily set wifi down | 17 | pub async fn init(&mut self) { |
| 259 | self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; | 18 | // TODO |
| 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 | |||
| 428 | pub struct Scanner<'a> { | ||
| 429 | subscriber: EventSubscriber<'a>, | ||
| 430 | events: &'a Events, | ||
| 431 | } | ||
| 432 | |||
| 433 | impl 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 | |||
| 450 | impl Drop for Scanner<'_> { | ||
| 451 | fn drop(&mut self) { | ||
| 452 | self.events.mask.disable_all(); | ||
| 453 | } | 19 | } |
| 454 | } | 20 | } |
diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 3171d61fe..00072749b 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs | |||
| @@ -1,102 +1,110 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![allow(incomplete_features)] | 1 | #![allow(incomplete_features)] |
| 4 | #![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)] | ||
| 5 | #![deny(unused_must_use)] | 2 | #![deny(unused_must_use)] |
| 6 | 3 | ||
| 7 | use core::slice; | 4 | use core::task::Context; |
| 8 | 5 | ||
| 9 | use embassy_net_driver_channel as ch; | 6 | use embassy_net_driver::{Capabilities, LinkState, Medium}; |
| 10 | use embedded_hal_1::digital::OutputPin; | ||
| 11 | use events::Events; | ||
| 12 | use ioctl::IoctlState; | ||
| 13 | 7 | ||
| 14 | use crate::bus::Bus; | 8 | use crate::mac::runner::Runner; |
| 15 | pub use crate::bus::SpiBusCyw43; | 9 | use crate::mac::MTU; |
| 16 | pub use crate::control::{Control, Error as ControlError}; | ||
| 17 | pub use crate::runner::Runner; | ||
| 18 | pub use crate::structs::BssInfo; | ||
| 19 | 10 | ||
| 20 | const MTU: usize = 1514; | 11 | pub struct Driver<'d> { |
| 21 | 12 | runner: &'d Runner, | |
| 22 | pub struct State { | ||
| 23 | ioctl_state: IoctlState, | ||
| 24 | ch: ch::State<MTU, 4, 4>, | ||
| 25 | events: Events, | ||
| 26 | } | 13 | } |
| 27 | 14 | ||
| 28 | impl State { | 15 | impl<'d> Driver<'d> { |
| 29 | pub fn new() -> Self { | 16 | pub(crate) fn new(runner: &'d Runner) -> Self { |
| 30 | Self { | 17 | Self { runner: runner } |
| 31 | ioctl_state: IoctlState::new(), | ||
| 32 | ch: ch::State::new(), | ||
| 33 | events: Events::new(), | ||
| 34 | } | ||
| 35 | } | 18 | } |
| 36 | } | 19 | } |
| 37 | 20 | ||
| 38 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 21 | impl<'d> embassy_net_driver::Driver for Driver<'d> { |
| 39 | pub enum PowerManagementMode { | 22 | // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; |
| 40 | /// Custom, officially unsupported mode. Use at your own risk. | 23 | // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; |
| 41 | /// All power-saving features set to their max at only a marginal decrease in power consumption | 24 | type RxToken<'a> = RxToken where Self: 'a; |
| 42 | /// as oppposed to `Aggressive`. | 25 | type TxToken<'a> = TxToken where Self: 'a; |
| 43 | SuperSave, | 26 | |
| 27 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | ||
| 28 | // WAKER.register(cx.waker()); | ||
| 29 | // if self.rx.available().is_some() && self.tx.available().is_some() { | ||
| 30 | // Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) | ||
| 31 | // } else { | ||
| 32 | // None | ||
| 33 | // } | ||
| 34 | |||
| 35 | None | ||
| 36 | } | ||
| 37 | |||
| 38 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { | ||
| 39 | // WAKER.register(cx.waker()); | ||
| 40 | // / if self.tx.available().is_some() { | ||
| 41 | // / Some(TxToken { tx: &mut self.tx }) | ||
| 42 | // / } else { | ||
| 43 | // / None | ||
| 44 | // / } | ||
| 44 | 45 | ||
| 45 | /// Aggressive power saving mode. | 46 | None |
| 46 | Aggressive, | 47 | } |
| 47 | 48 | ||
| 48 | /// The default mode. | 49 | fn capabilities(&self) -> Capabilities { |
| 49 | PowerSave, | 50 | let mut caps = Capabilities::default(); |
| 51 | caps.max_transmission_unit = MTU; | ||
| 52 | // caps.max_burst_size = Some(self.tx.len()); | ||
| 50 | 53 | ||
| 51 | /// Performance is prefered over power consumption but still some power is conserved as opposed to | 54 | caps.medium = Medium::Ieee802154; |
| 52 | /// `None`. | 55 | caps |
| 53 | Performance, | 56 | } |
| 54 | 57 | ||
| 55 | /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of | 58 | fn link_state(&mut self, cx: &mut Context) -> LinkState { |
| 56 | /// a much lower throughput. | 59 | // if self.phy.poll_link(&mut self.station_management, cx) { |
| 57 | ThroughputThrottling, | 60 | // LinkState::Up |
| 61 | // } else { | ||
| 62 | // LinkState::Down | ||
| 63 | // } | ||
| 58 | 64 | ||
| 59 | /// No power management is configured. This consumes the most power. | 65 | LinkState::Down |
| 60 | None, | 66 | } |
| 61 | } | 67 | |
| 68 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 69 | // self.mac_addr | ||
| 62 | 70 | ||
| 63 | impl Default for PowerManagementMode { | 71 | [0; 6] |
| 64 | fn default() -> Self { | ||
| 65 | Self::PowerSave | ||
| 66 | } | 72 | } |
| 67 | } | 73 | } |
| 68 | 74 | ||
| 69 | impl PowerManagementMode { | 75 | pub struct RxToken { |
| 70 | // TODO | 76 | // rx: &'a mut RDesRing<'d>, |
| 77 | } | ||
| 78 | |||
| 79 | impl embassy_net_driver::RxToken for RxToken { | ||
| 80 | fn consume<R, F>(self, f: F) -> R | ||
| 81 | where | ||
| 82 | F: FnOnce(&mut [u8]) -> R, | ||
| 83 | { | ||
| 84 | // NOTE(unwrap): we checked the queue wasn't full when creating the token. | ||
| 85 | // let pkt = unwrap!(self.rx.available()); | ||
| 86 | |||
| 87 | let pkt = &[]; | ||
| 88 | let r = f(&mut pkt[0..]); | ||
| 89 | // self.rx.pop_packet(); | ||
| 90 | r | ||
| 91 | } | ||
| 71 | } | 92 | } |
| 72 | 93 | ||
| 73 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | 94 | pub struct TxToken { |
| 74 | 95 | // tx: &'a mut TDesRing<'d>, | |
| 75 | pub 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>) | ||
| 81 | where | ||
| 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 | } | 96 | } |
| 98 | 97 | ||
| 99 | fn slice8_mut(x: &mut [u32]) -> &mut [u8] { | 98 | impl embassy_net_driver::TxToken for TxToken { |
| 100 | let len = x.len() * 4; | 99 | fn consume<R, F>(self, len: usize, f: F) -> R |
| 101 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } | 100 | where |
| 101 | F: FnOnce(&mut [u8]) -> R, | ||
| 102 | { | ||
| 103 | // NOTE(unwrap): we checked the queue wasn't full when creating the token. | ||
| 104 | // let pkt = unwrap!(self.tx.available()); | ||
| 105 | let pkt = &[]; | ||
| 106 | let r = f(&mut pkt[..len]); | ||
| 107 | // self.tx.transmit(len); | ||
| 108 | r | ||
| 109 | } | ||
| 102 | } | 110 | } |
diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 661c06ac4..a2bb79222 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs | |||
| @@ -1,9 +1,3 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | |||
| 3 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 4 | use embassy_sync::pubsub::{PubSubChannel, Subscriber}; | ||
| 5 | |||
| 6 | use super::helpers::to_u16; | ||
| 7 | use core::mem; | 1 | use core::mem; |
| 8 | 2 | ||
| 9 | use super::indications::{ | 3 | use super::indications::{ |
| @@ -80,7 +74,6 @@ impl Event { | |||
| 80 | } | 74 | } |
| 81 | } | 75 | } |
| 82 | 76 | ||
| 83 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 84 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 77 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 85 | pub enum MacEvent<'a> { | 78 | pub enum MacEvent<'a> { |
| 86 | MlmeAssociateCnf(&'a AssociateConfirm), | 79 | MlmeAssociateCnf(&'a AssociateConfirm), |
| @@ -109,116 +102,3 @@ pub enum MacEvent<'a> { | |||
| 109 | McpsDataInd(&'a DataIndication), | 102 | McpsDataInd(&'a DataIndication), |
| 110 | MlmePollInd(&'a PollIndication), | 103 | MlmePollInd(&'a PollIndication), |
| 111 | } | 104 | } |
| 112 | |||
| 113 | // TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. | ||
| 114 | pub type EventQueue = PubSubChannel<NoopRawMutex, Message, 2, 1, 1>; | ||
| 115 | pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; | ||
| 116 | |||
| 117 | pub struct Events { | ||
| 118 | pub queue: EventQueue, | ||
| 119 | pub mask: SharedEventMask, | ||
| 120 | } | ||
| 121 | |||
| 122 | impl Events { | ||
| 123 | pub fn new() -> Self { | ||
| 124 | Self { | ||
| 125 | queue: EventQueue::new(), | ||
| 126 | mask: SharedEventMask::default(), | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | #[derive(Clone)] | ||
| 132 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 133 | pub struct Status { | ||
| 134 | pub event_type: MacEvent, | ||
| 135 | pub status: u32, | ||
| 136 | } | ||
| 137 | |||
| 138 | #[derive(Clone, Copy)] | ||
| 139 | pub enum Payload { | ||
| 140 | None, | ||
| 141 | // BssInfo(BssInfo), | ||
| 142 | } | ||
| 143 | |||
| 144 | #[derive(Clone)] | ||
| 145 | |||
| 146 | pub struct Message { | ||
| 147 | pub header: Status, | ||
| 148 | pub payload: Payload, | ||
| 149 | } | ||
| 150 | |||
| 151 | impl Message { | ||
| 152 | pub fn new(status: Status, payload: Payload) -> Self { | ||
| 153 | Self { | ||
| 154 | header: status, | ||
| 155 | payload, | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | #[derive(Default)] | ||
| 161 | struct EventMask { | ||
| 162 | mask: [u32; Self::WORD_COUNT], | ||
| 163 | } | ||
| 164 | |||
| 165 | impl EventMask { | ||
| 166 | const WORD_COUNT: usize = ((Event::LAST as u32 + (u32::BITS - 1)) / u32::BITS) as usize; | ||
| 167 | |||
| 168 | fn enable(&mut self, event: MacEvent) { | ||
| 169 | let n = event as u32; | ||
| 170 | let word = n / u32::BITS; | ||
| 171 | let bit = n % u32::BITS; | ||
| 172 | |||
| 173 | self.mask[word as usize] |= 1 << bit; | ||
| 174 | } | ||
| 175 | |||
| 176 | fn disable(&mut self, event: MacEvent) { | ||
| 177 | let n = event as u32; | ||
| 178 | let word = n / u32::BITS; | ||
| 179 | let bit = n % u32::BITS; | ||
| 180 | |||
| 181 | self.mask[word as usize] &= !(1 << bit); | ||
| 182 | } | ||
| 183 | |||
| 184 | fn is_enabled(&self, event: MacEvent) -> bool { | ||
| 185 | let n = event as u32; | ||
| 186 | let word = n / u32::BITS; | ||
| 187 | let bit = n % u32::BITS; | ||
| 188 | |||
| 189 | self.mask[word as usize] & (1 << bit) > 0 | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | #[derive(Default)] | ||
| 194 | |||
| 195 | pub struct SharedEventMask { | ||
| 196 | mask: RefCell<EventMask>, | ||
| 197 | } | ||
| 198 | |||
| 199 | impl SharedEventMask { | ||
| 200 | pub fn enable(&self, events: &[MacEvent]) { | ||
| 201 | let mut mask = self.mask.borrow_mut(); | ||
| 202 | for event in events { | ||
| 203 | mask.enable(*event); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | #[allow(dead_code)] | ||
| 208 | pub fn disable(&self, events: &[MacEvent]) { | ||
| 209 | let mut mask = self.mask.borrow_mut(); | ||
| 210 | for event in events { | ||
| 211 | mask.disable(*event); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | pub fn disable_all(&self) { | ||
| 216 | let mut mask = self.mask.borrow_mut(); | ||
| 217 | mask.mask = Default::default(); | ||
| 218 | } | ||
| 219 | |||
| 220 | pub fn is_enabled(&self, event: MacEvent) -> bool { | ||
| 221 | let mask = self.mask.borrow(); | ||
| 222 | mask.is_enabled(event) | ||
| 223 | } | ||
| 224 | } | ||
diff --git a/embassy-stm32-wpan/src/mac/ioctl.rs b/embassy-stm32-wpan/src/mac/ioctl.rs deleted file mode 100644 index 0fe55cd6c..000000000 --- a/embassy-stm32-wpan/src/mac/ioctl.rs +++ /dev/null | |||
| @@ -1,124 +0,0 @@ | |||
| 1 | use core::cell::{Cell, RefCell}; | ||
| 2 | use core::future::poll_fn; | ||
| 3 | use core::task::{Poll, Waker}; | ||
| 4 | |||
| 5 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 6 | |||
| 7 | #[derive(Clone, Copy)] | ||
| 8 | pub enum IoctlType { | ||
| 9 | Get = 0, | ||
| 10 | Set = 2, | ||
| 11 | } | ||
| 12 | |||
| 13 | #[derive(Clone, Copy)] | ||
| 14 | pub struct PendingIoctl { | ||
| 15 | pub buf: *mut [u8], | ||
| 16 | pub kind: IoctlType, | ||
| 17 | pub cmd: u32, | ||
| 18 | pub iface: u32, | ||
| 19 | } | ||
| 20 | |||
| 21 | #[derive(Clone, Copy)] | ||
| 22 | enum IoctlStateInner { | ||
| 23 | Pending(PendingIoctl), | ||
| 24 | Sent { buf: *mut [u8] }, | ||
| 25 | Done { resp_len: usize }, | ||
| 26 | } | ||
| 27 | |||
| 28 | struct Wakers { | ||
| 29 | control: WakerRegistration, | ||
| 30 | runner: WakerRegistration, | ||
| 31 | } | ||
| 32 | |||
| 33 | impl Default for Wakers { | ||
| 34 | fn default() -> Self { | ||
| 35 | Self { | ||
| 36 | control: WakerRegistration::new(), | ||
| 37 | runner: WakerRegistration::new(), | ||
| 38 | } | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | pub struct IoctlState { | ||
| 43 | state: Cell<IoctlStateInner>, | ||
| 44 | wakers: RefCell<Wakers>, | ||
| 45 | } | ||
| 46 | |||
| 47 | impl IoctlState { | ||
| 48 | pub fn new() -> Self { | ||
| 49 | Self { | ||
| 50 | state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), | ||
| 51 | wakers: Default::default(), | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | fn wake_control(&self) { | ||
| 56 | self.wakers.borrow_mut().control.wake(); | ||
| 57 | } | ||
| 58 | |||
| 59 | fn register_control(&self, waker: &Waker) { | ||
| 60 | self.wakers.borrow_mut().control.register(waker); | ||
| 61 | } | ||
| 62 | |||
| 63 | fn wake_runner(&self) { | ||
| 64 | self.wakers.borrow_mut().runner.wake(); | ||
| 65 | } | ||
| 66 | |||
| 67 | fn register_runner(&self, waker: &Waker) { | ||
| 68 | self.wakers.borrow_mut().runner.register(waker); | ||
| 69 | } | ||
| 70 | |||
| 71 | pub async fn wait_complete(&self) -> usize { | ||
| 72 | poll_fn(|cx| { | ||
| 73 | if let IoctlStateInner::Done { resp_len } = self.state.get() { | ||
| 74 | Poll::Ready(resp_len) | ||
| 75 | } else { | ||
| 76 | self.register_control(cx.waker()); | ||
| 77 | Poll::Pending | ||
| 78 | } | ||
| 79 | }) | ||
| 80 | .await | ||
| 81 | } | ||
| 82 | |||
| 83 | pub async fn wait_pending(&self) -> PendingIoctl { | ||
| 84 | let pending = poll_fn(|cx| { | ||
| 85 | if let IoctlStateInner::Pending(pending) = self.state.get() { | ||
| 86 | Poll::Ready(pending) | ||
| 87 | } else { | ||
| 88 | self.register_runner(cx.waker()); | ||
| 89 | Poll::Pending | ||
| 90 | } | ||
| 91 | }) | ||
| 92 | .await; | ||
| 93 | |||
| 94 | self.state.set(IoctlStateInner::Sent { buf: pending.buf }); | ||
| 95 | pending | ||
| 96 | } | ||
| 97 | |||
| 98 | pub fn cancel_ioctl(&self) { | ||
| 99 | self.state.set(IoctlStateInner::Done { resp_len: 0 }); | ||
| 100 | } | ||
| 101 | |||
| 102 | pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { | ||
| 103 | self.state | ||
| 104 | .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); | ||
| 105 | self.wake_runner(); | ||
| 106 | self.wait_complete().await | ||
| 107 | } | ||
| 108 | |||
| 109 | pub fn ioctl_done(&self, response: &[u8]) { | ||
| 110 | if let IoctlStateInner::Sent { buf } = self.state.get() { | ||
| 111 | // trace!("IOCTL Response: {:02x}", Bytes(response)); | ||
| 112 | |||
| 113 | // TODO fix this | ||
| 114 | (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); | ||
| 115 | |||
| 116 | self.state.set(IoctlStateInner::Done { | ||
| 117 | resp_len: response.len(), | ||
| 118 | }); | ||
| 119 | self.wake_control(); | ||
| 120 | } else { | ||
| 121 | warn!("IOCTL Response but no pending Ioctl"); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index df03e4236..e024aeae3 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | pub mod commands; | 1 | pub mod commands; |
| 2 | mod consts; | 2 | mod consts; |
| 3 | pub mod control; | 3 | pub mod control; |
| 4 | mod driver; | ||
| 4 | pub mod event; | 5 | pub mod event; |
| 5 | pub mod indications; | 6 | pub mod indications; |
| 6 | mod ioctl; | ||
| 7 | mod macros; | 7 | mod macros; |
| 8 | mod opcodes; | 8 | mod opcodes; |
| 9 | pub mod responses; | 9 | pub mod responses; |
| @@ -12,86 +12,19 @@ pub mod typedefs; | |||
| 12 | 12 | ||
| 13 | use core::slice; | 13 | use core::slice; |
| 14 | 14 | ||
| 15 | use embassy_net_driver_channel as ch; | ||
| 16 | |||
| 17 | pub use crate::mac::control::{Control, Error as ControlError}; | 15 | pub use crate::mac::control::{Control, Error as ControlError}; |
| 18 | use crate::mac::event::Events; | 16 | use crate::mac::driver::Driver; |
| 19 | use crate::mac::ioctl::IoctlState; | ||
| 20 | pub use crate::mac::runner::Runner; | 17 | pub use crate::mac::runner::Runner; |
| 21 | use crate::sub::mac::Mac; | 18 | use crate::sub::mac::Mac; |
| 22 | 19 | ||
| 23 | const MTU: usize = 1514; | 20 | const MTU: usize = 127; |
| 24 | |||
| 25 | pub struct State { | ||
| 26 | ioctl_state: IoctlState, | ||
| 27 | ch: ch::State<MTU, 4, 4>, | ||
| 28 | events: Events, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl State { | ||
| 32 | pub fn new() -> Self { | ||
| 33 | Self { | ||
| 34 | ioctl_state: IoctlState::new(), | ||
| 35 | ch: ch::State::new(), | ||
| 36 | events: Events::new(), | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 42 | pub enum PowerManagementMode { | ||
| 43 | /// Custom, officially unsupported mode. Use at your own risk. | ||
| 44 | /// All power-saving features set to their max at only a marginal decrease in power consumption | ||
| 45 | /// as oppposed to `Aggressive`. | ||
| 46 | SuperSave, | ||
| 47 | |||
| 48 | /// Aggressive power saving mode. | ||
| 49 | Aggressive, | ||
| 50 | |||
| 51 | /// The default mode. | ||
| 52 | PowerSave, | ||
| 53 | |||
| 54 | /// Performance is prefered over power consumption but still some power is conserved as opposed to | ||
| 55 | /// `None`. | ||
| 56 | Performance, | ||
| 57 | |||
| 58 | /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of | ||
| 59 | /// a much lower throughput. | ||
| 60 | ThroughputThrottling, | ||
| 61 | |||
| 62 | /// No power management is configured. This consumes the most power. | ||
| 63 | None, | ||
| 64 | } | ||
| 65 | |||
| 66 | impl Default for PowerManagementMode { | ||
| 67 | fn default() -> Self { | ||
| 68 | Self::PowerSave | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | impl PowerManagementMode { | ||
| 73 | // TODO | ||
| 74 | } | ||
| 75 | |||
| 76 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | ||
| 77 | |||
| 78 | pub async fn new<'a>( | ||
| 79 | state: &'a mut State, | ||
| 80 | mac_subsystem: Mac, | ||
| 81 | firmware: &[u8], | ||
| 82 | ) -> (NetDriver<'a>, Control<'a>, Runner<'a>) { | ||
| 83 | let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); | ||
| 84 | let state_ch = ch_runner.state_runner(); | ||
| 85 | |||
| 86 | let mut runner = Runner::new(ch_runner, mac_subsystem, &state.ioctl_state, &state.events); | ||
| 87 | 21 | ||
| 88 | runner.init(firmware).await; | 22 | pub async fn new<'a>(mac: Mac) -> (Runner, Control<'a>, Driver<'a>) { |
| 23 | let runner = Runner::new(mac); | ||
| 24 | let control = Control::new(&runner); | ||
| 25 | let driver = Driver::new(&runner); | ||
| 89 | 26 | ||
| 90 | ( | 27 | (runner, control, driver) |
| 91 | device, | ||
| 92 | Control::new(state_ch, &state.events, &state.ioctl_state), | ||
| 93 | runner, | ||
| 94 | ) | ||
| 95 | } | 28 | } |
| 96 | 29 | ||
| 97 | fn slice8_mut(x: &mut [u32]) -> &mut [u8] { | 30 | fn slice8_mut(x: &mut [u32]) -> &mut [u8] { |
diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index fbb7cb747..e97c9c8e2 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs | |||
| @@ -1,342 +1,27 @@ | |||
| 1 | use embassy_futures::select::{select3, Either3}; | 1 | use embassy_futures::select::{select3, Either3}; |
| 2 | use embassy_net_driver_channel as ch; | ||
| 3 | use embassy_sync::pubsub::PubSubBehavior; | ||
| 4 | 2 | ||
| 5 | use crate::mac::event::Events; | ||
| 6 | use crate::mac::ioctl::{IoctlState, PendingIoctl}; | ||
| 7 | use crate::mac::MTU; | 3 | use crate::mac::MTU; |
| 8 | use crate::sub::mac::Mac; | 4 | use crate::sub::mac::Mac; |
| 9 | 5 | ||
| 10 | pub struct Runner<'a> { | 6 | pub struct Runner { |
| 11 | ch: ch::Runner<'a, MTU>, | ||
| 12 | mac: Mac, | 7 | mac: Mac, |
| 13 | 8 | // TODO: tx_ring | |
| 14 | ioctl_state: &'a IoctlState, | 9 | // TODO: rx_buf |
| 15 | ioctl_id: u16, | ||
| 16 | sdpcm_seq: u8, | ||
| 17 | sdpcm_seq_max: u8, | ||
| 18 | |||
| 19 | events: &'a Events, | ||
| 20 | } | 10 | } |
| 21 | 11 | ||
| 22 | impl<'a> Runner<'a> { | 12 | impl Runner { |
| 23 | pub(crate) fn new(ch: ch::Runner<'a, MTU>, mac: Mac, ioctl_state: &'a IoctlState, events: &'a Events) -> Self { | 13 | pub(crate) fn new(mac: Mac) -> Self { |
| 24 | Self { | 14 | Self { mac } |
| 25 | ch, | ||
| 26 | mac, | ||
| 27 | ioctl_state, | ||
| 28 | ioctl_id: 0, | ||
| 29 | sdpcm_seq: 0, | ||
| 30 | sdpcm_seq_max: 1, | ||
| 31 | events, | ||
| 32 | } | ||
| 33 | } | 15 | } |
| 34 | 16 | ||
| 35 | pub(crate) async fn init(&mut self, firmware: &[u8]) { | 17 | pub(crate) async fn init(&mut self, firmware: &[u8]) { |
| 36 | self.bus.init().await; | ||
| 37 | |||
| 38 | #[cfg(feature = "firmware-logs")] | ||
| 39 | self.log_init().await; | ||
| 40 | |||
| 41 | debug!("wifi init done"); | 18 | debug!("wifi init done"); |
| 42 | } | 19 | } |
| 43 | 20 | ||
| 44 | pub async fn run(mut self) -> ! { | 21 | pub async fn run(mut self) -> ! { |
| 45 | let mut buf = [0; 512]; | 22 | let mut buf = [0; 512]; |
| 46 | loop { | 23 | loop { |
| 47 | #[cfg(feature = "firmware-logs")] | 24 | // TODO |
| 48 | self.log_read().await; | ||
| 49 | |||
| 50 | if self.has_credit() { | ||
| 51 | let ioctl = self.ioctl_state.wait_pending(); | ||
| 52 | let tx = self.ch.tx_buf(); | ||
| 53 | let ev = self.bus.wait_for_event(); | ||
| 54 | |||
| 55 | match select3(ioctl, tx, ev).await { | ||
| 56 | Either3::First(PendingIoctl { | ||
| 57 | buf: iobuf, | ||
| 58 | kind, | ||
| 59 | cmd, | ||
| 60 | iface, | ||
| 61 | }) => { | ||
| 62 | self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; | ||
| 63 | self.check_status(&mut buf).await; | ||
| 64 | } | ||
| 65 | Either3::Second(packet) => { | ||
| 66 | trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); | ||
| 67 | |||
| 68 | let mut buf = [0; 512]; | ||
| 69 | let buf8 = slice8_mut(&mut buf); | ||
| 70 | |||
| 71 | // There MUST be 2 bytes of padding between the SDPCM and BDC headers. | ||
| 72 | // And ONLY for data packets! | ||
| 73 | // No idea why, but the firmware will append two zero bytes to the tx'd packets | ||
| 74 | // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it | ||
| 75 | // be oversized and get dropped. | ||
| 76 | // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90 | ||
| 77 | // 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 | ||
| 78 | // ¯\_(ツ)_/¯ | ||
| 79 | const PADDING_SIZE: usize = 2; | ||
| 80 | let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len(); | ||
| 81 | |||
| 82 | let seq = self.sdpcm_seq; | ||
| 83 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 84 | |||
| 85 | let sdpcm_header = SdpcmHeader { | ||
| 86 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 87 | len_inv: !total_len as u16, | ||
| 88 | sequence: seq, | ||
| 89 | channel_and_flags: CHANNEL_TYPE_DATA, | ||
| 90 | next_length: 0, | ||
| 91 | header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _, | ||
| 92 | wireless_flow_control: 0, | ||
| 93 | bus_data_credit: 0, | ||
| 94 | reserved: [0, 0], | ||
| 95 | }; | ||
| 96 | |||
| 97 | let bdc_header = BdcHeader { | ||
| 98 | flags: BDC_VERSION << BDC_VERSION_SHIFT, | ||
| 99 | priority: 0, | ||
| 100 | flags2: 0, | ||
| 101 | data_offset: 0, | ||
| 102 | }; | ||
| 103 | trace!("tx {:?}", sdpcm_header); | ||
| 104 | trace!(" {:?}", bdc_header); | ||
| 105 | |||
| 106 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 107 | buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE] | ||
| 108 | .copy_from_slice(&bdc_header.to_bytes()); | ||
| 109 | buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()] | ||
| 110 | .copy_from_slice(packet); | ||
| 111 | |||
| 112 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 113 | |||
| 114 | trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); | ||
| 115 | |||
| 116 | self.bus.wlan_write(&buf[..(total_len / 4)]).await; | ||
| 117 | self.ch.tx_done(); | ||
| 118 | self.check_status(&mut buf).await; | ||
| 119 | } | ||
| 120 | Either3::Third(()) => { | ||
| 121 | self.handle_irq(&mut buf).await; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } else { | ||
| 125 | warn!("TX stalled"); | ||
| 126 | self.bus.wait_for_event().await; | ||
| 127 | self.handle_irq(&mut buf).await; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Wait for IRQ on F2 packet available | ||
| 133 | async fn handle_irq(&mut self, buf: &mut [u32; 512]) { | ||
| 134 | // Receive stuff | ||
| 135 | let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; | ||
| 136 | trace!("irq{}", FormatInterrupt(irq)); | ||
| 137 | |||
| 138 | if irq & IRQ_F2_PACKET_AVAILABLE != 0 { | ||
| 139 | self.check_status(buf).await; | ||
| 140 | } | ||
| 141 | |||
| 142 | if irq & IRQ_DATA_UNAVAILABLE != 0 { | ||
| 143 | // TODO what should we do here? | ||
| 144 | warn!("IRQ DATA_UNAVAILABLE, clearing..."); | ||
| 145 | self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Handle F2 events while status register is set | ||
| 150 | async fn check_status(&mut self, buf: &mut [u32; 512]) { | ||
| 151 | loop { | ||
| 152 | let status = self.bus.status(); | ||
| 153 | trace!("check status{}", FormatStatus(status)); | ||
| 154 | |||
| 155 | if status & STATUS_F2_PKT_AVAILABLE != 0 { | ||
| 156 | let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; | ||
| 157 | self.bus.wlan_read(buf, len).await; | ||
| 158 | trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); | ||
| 159 | self.rx(&mut slice8_mut(buf)[..len as usize]); | ||
| 160 | } else { | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | fn rx(&mut self, packet: &mut [u8]) { | ||
| 167 | let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { | ||
| 168 | return; | ||
| 169 | }; | ||
| 170 | |||
| 171 | self.update_credit(&sdpcm_header); | ||
| 172 | |||
| 173 | let channel = sdpcm_header.channel_and_flags & 0x0f; | ||
| 174 | |||
| 175 | match channel { | ||
| 176 | CHANNEL_TYPE_CONTROL => { | ||
| 177 | let Some((cdc_header, response)) = CdcHeader::parse(payload) else { | ||
| 178 | return; | ||
| 179 | }; | ||
| 180 | trace!(" {:?}", cdc_header); | ||
| 181 | |||
| 182 | if cdc_header.id == self.ioctl_id { | ||
| 183 | if cdc_header.status != 0 { | ||
| 184 | // TODO: propagate error instead | ||
| 185 | panic!("IOCTL error {}", cdc_header.status as i32); | ||
| 186 | } | ||
| 187 | |||
| 188 | self.ioctl_state.ioctl_done(response); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | CHANNEL_TYPE_EVENT => { | ||
| 192 | let Some((_, bdc_packet)) = BdcHeader::parse(payload) else { | ||
| 193 | warn!("BDC event, incomplete header"); | ||
| 194 | return; | ||
| 195 | }; | ||
| 196 | |||
| 197 | let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else { | ||
| 198 | warn!("BDC event, incomplete data"); | ||
| 199 | return; | ||
| 200 | }; | ||
| 201 | |||
| 202 | const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h | ||
| 203 | if event_packet.eth.ether_type != ETH_P_LINK_CTL { | ||
| 204 | warn!( | ||
| 205 | "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", | ||
| 206 | event_packet.eth.ether_type, ETH_P_LINK_CTL | ||
| 207 | ); | ||
| 208 | return; | ||
| 209 | } | ||
| 210 | const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; | ||
| 211 | if event_packet.hdr.oui != BROADCOM_OUI { | ||
| 212 | warn!( | ||
| 213 | "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", | ||
| 214 | Bytes(&event_packet.hdr.oui), | ||
| 215 | Bytes(BROADCOM_OUI) | ||
| 216 | ); | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; | ||
| 220 | if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { | ||
| 221 | warn!("unexpected subtype {}", event_packet.hdr.subtype); | ||
| 222 | return; | ||
| 223 | } | ||
| 224 | |||
| 225 | const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; | ||
| 226 | if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { | ||
| 227 | warn!("unexpected user_subtype {}", event_packet.hdr.subtype); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | |||
| 231 | let evt_type = events::Event::from(event_packet.msg.event_type as u8); | ||
| 232 | debug!( | ||
| 233 | "=== EVENT {:?}: {:?} {:02x}", | ||
| 234 | evt_type, | ||
| 235 | event_packet.msg, | ||
| 236 | Bytes(evt_data) | ||
| 237 | ); | ||
| 238 | |||
| 239 | if self.events.mask.is_enabled(evt_type) { | ||
| 240 | let status = event_packet.msg.status; | ||
| 241 | let event_payload = match evt_type { | ||
| 242 | Event::ESCAN_RESULT if status == EStatus::PARTIAL => { | ||
| 243 | let Some((_, bss_info)) = ScanResults::parse(evt_data) else { | ||
| 244 | return; | ||
| 245 | }; | ||
| 246 | let Some(bss_info) = BssInfo::parse(bss_info) else { | ||
| 247 | return; | ||
| 248 | }; | ||
| 249 | events::Payload::BssInfo(*bss_info) | ||
| 250 | } | ||
| 251 | Event::ESCAN_RESULT => events::Payload::None, | ||
| 252 | _ => events::Payload::None, | ||
| 253 | }; | ||
| 254 | |||
| 255 | // this intentionally uses the non-blocking publish immediate | ||
| 256 | // publish() is a deadlock risk in the current design as awaiting here prevents ioctls | ||
| 257 | // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event | ||
| 258 | // (if they are actively awaiting the queue) | ||
| 259 | self.events.queue.publish_immediate(events::Message::new( | ||
| 260 | Status { | ||
| 261 | event_type: evt_type, | ||
| 262 | status, | ||
| 263 | }, | ||
| 264 | event_payload, | ||
| 265 | )); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | CHANNEL_TYPE_DATA => { | ||
| 269 | let Some((_, packet)) = BdcHeader::parse(payload) else { | ||
| 270 | return; | ||
| 271 | }; | ||
| 272 | trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); | ||
| 273 | |||
| 274 | match self.ch.try_rx_buf() { | ||
| 275 | Some(buf) => { | ||
| 276 | buf[..packet.len()].copy_from_slice(packet); | ||
| 277 | self.ch.rx_done(packet.len()) | ||
| 278 | } | ||
| 279 | None => warn!("failed to push rxd packet to the channel."), | ||
| 280 | } | ||
| 281 | } | ||
| 282 | _ => {} | ||
| 283 | } | 25 | } |
| 284 | } | 26 | } |
| 285 | |||
| 286 | fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { | ||
| 287 | if sdpcm_header.channel_and_flags & 0xf < 3 { | ||
| 288 | let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; | ||
| 289 | if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { | ||
| 290 | sdpcm_seq_max = self.sdpcm_seq + 2; | ||
| 291 | } | ||
| 292 | self.sdpcm_seq_max = sdpcm_seq_max; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | fn has_credit(&self) -> bool { | ||
| 297 | self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 | ||
| 298 | } | ||
| 299 | |||
| 300 | async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { | ||
| 301 | let mut buf = [0; 512]; | ||
| 302 | let buf8 = slice8_mut(&mut buf); | ||
| 303 | |||
| 304 | let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); | ||
| 305 | |||
| 306 | let sdpcm_seq = self.sdpcm_seq; | ||
| 307 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 308 | self.ioctl_id = self.ioctl_id.wrapping_add(1); | ||
| 309 | |||
| 310 | let sdpcm_header = SdpcmHeader { | ||
| 311 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 312 | len_inv: !total_len as u16, | ||
| 313 | sequence: sdpcm_seq, | ||
| 314 | channel_and_flags: CHANNEL_TYPE_CONTROL, | ||
| 315 | next_length: 0, | ||
| 316 | header_length: SdpcmHeader::SIZE as _, | ||
| 317 | wireless_flow_control: 0, | ||
| 318 | bus_data_credit: 0, | ||
| 319 | reserved: [0, 0], | ||
| 320 | }; | ||
| 321 | |||
| 322 | let cdc_header = CdcHeader { | ||
| 323 | cmd: cmd, | ||
| 324 | len: data.len() as _, | ||
| 325 | flags: kind as u16 | (iface as u16) << 12, | ||
| 326 | id: self.ioctl_id, | ||
| 327 | status: 0, | ||
| 328 | }; | ||
| 329 | trace!("tx {:?}", sdpcm_header); | ||
| 330 | trace!(" {:?}", cdc_header); | ||
| 331 | |||
| 332 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 333 | buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); | ||
| 334 | buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); | ||
| 335 | |||
| 336 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 337 | |||
| 338 | trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); | ||
| 339 | |||
| 340 | self.bus.wlan_write(&buf[..total_len / 4]).await; | ||
| 341 | } | ||
| 342 | } | 27 | } |
