diff options
| author | xoviat <[email protected]> | 2023-07-15 19:15:01 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-07-15 19:15:01 -0500 |
| commit | cd592cb0550146158ea6f9d90ba8afe9e1b06a92 (patch) | |
| tree | 4878db7dc91d865beea9de1fd8c8f157c6b4c64d /embassy-stm32-wpan | |
| parent | 0b63af33135784c1410dc8667cfefbaa538a1f04 (diff) | |
wpan: add files from cyw43
Diffstat (limited to 'embassy-stm32-wpan')
| -rw-r--r-- | embassy-stm32-wpan/src/lib.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/control.rs | 454 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/driver.rs | 102 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/mod.rs | 92 | ||||
| -rw-r--r-- | embassy-stm32-wpan/src/mac/runner.rs | 489 |
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. |
| 5 | pub mod fmt; | 6 | pub 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 @@ | |||
| 1 | use core::cmp::{max, min}; | ||
| 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 | |||
| 15 | #[derive(Debug)] | ||
| 16 | pub struct Error { | ||
| 17 | pub status: u32, | ||
| 18 | } | ||
| 19 | |||
| 20 | pub struct Control<'a> { | ||
| 21 | state_ch: ch::StateRunner<'a>, | ||
| 22 | events: &'a Events, | ||
| 23 | ioctl_state: &'a IoctlState, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl<'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 | |||
| 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 | } | ||
| 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 | |||
| 7 | use core::slice; | ||
| 8 | |||
| 9 | use embassy_net_driver_channel as ch; | ||
| 10 | use embedded_hal_1::digital::OutputPin; | ||
| 11 | use events::Events; | ||
| 12 | use ioctl::IoctlState; | ||
| 13 | |||
| 14 | use crate::bus::Bus; | ||
| 15 | pub use crate::bus::SpiBusCyw43; | ||
| 16 | pub use crate::control::{Control, Error as ControlError}; | ||
| 17 | pub use crate::runner::Runner; | ||
| 18 | pub use crate::structs::BssInfo; | ||
| 19 | |||
| 20 | const MTU: usize = 1514; | ||
| 21 | |||
| 22 | pub struct State { | ||
| 23 | ioctl_state: IoctlState, | ||
| 24 | ch: ch::State<MTU, 4, 4>, | ||
| 25 | events: Events, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl 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)] | ||
| 39 | pub 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 | |||
| 63 | impl Default for PowerManagementMode { | ||
| 64 | fn default() -> Self { | ||
| 65 | Self::PowerSave | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | impl PowerManagementMode { | ||
| 70 | // TODO | ||
| 71 | } | ||
| 72 | |||
| 73 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | ||
| 74 | |||
| 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 | } | ||
| 98 | |||
| 99 | fn 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 @@ | |||
| 1 | pub mod commands; | 1 | pub mod commands; |
| 2 | mod consts; | 2 | mod consts; |
| 3 | pub mod control; | ||
| 3 | pub mod event; | 4 | pub mod event; |
| 4 | mod helpers; | 5 | mod helpers; |
| 5 | pub mod indications; | 6 | pub mod indications; |
| 6 | mod macros; | 7 | mod macros; |
| 7 | mod opcodes; | 8 | mod opcodes; |
| 8 | pub mod responses; | 9 | pub mod responses; |
| 10 | pub mod runner; | ||
| 9 | pub mod typedefs; | 11 | pub mod typedefs; |
| 12 | |||
| 13 | use core::slice; | ||
| 14 | |||
| 15 | use embassy_net_driver_channel as ch; | ||
| 16 | |||
| 17 | use crate::bus::Bus; | ||
| 18 | pub use crate::bus::SpiBusCyw43; | ||
| 19 | pub use crate::mac::control::{Control, Error as ControlError}; | ||
| 20 | pub use crate::runner::Runner; | ||
| 21 | pub use crate::structs::BssInfo; | ||
| 22 | use crate::sub::mac::Mac; | ||
| 23 | |||
| 24 | const MTU: usize = 1514; | ||
| 25 | |||
| 26 | pub struct State { | ||
| 27 | ioctl_state: IoctlState, | ||
| 28 | ch: ch::State<MTU, 4, 4>, | ||
| 29 | events: Events, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl 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)] | ||
| 43 | pub 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 | |||
| 67 | impl Default for PowerManagementMode { | ||
| 68 | fn default() -> Self { | ||
| 69 | Self::PowerSave | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | impl PowerManagementMode { | ||
| 74 | // TODO | ||
| 75 | } | ||
| 76 | |||
| 77 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | ||
| 78 | |||
| 79 | pub 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 | |||
| 98 | fn 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 @@ | |||
| 1 | use embassy_futures::select::{select3, Either3}; | ||
| 2 | use embassy_net_driver_channel as ch; | ||
| 3 | use embassy_sync::pubsub::PubSubBehavior; | ||
| 4 | |||
| 5 | use crate::sub::mac::Mac; | ||
| 6 | |||
| 7 | #[cfg(feature = "firmware-logs")] | ||
| 8 | struct LogState { | ||
| 9 | addr: u32, | ||
| 10 | last_idx: usize, | ||
| 11 | buf: [u8; 256], | ||
| 12 | buf_count: usize, | ||
| 13 | } | ||
| 14 | |||
| 15 | #[cfg(feature = "firmware-logs")] | ||
| 16 | impl 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 | |||
| 27 | pub 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 | |||
| 42 | impl<'a, PWR, SPI> Runner<'a, PWR, SPI> | ||
| 43 | where | ||
| 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 | } | ||
