diff options
Diffstat (limited to 'cyw43/src/runner.rs')
| -rw-r--r-- | cyw43/src/runner.rs | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs new file mode 100644 index 000000000..5706696b4 --- /dev/null +++ b/cyw43/src/runner.rs | |||
| @@ -0,0 +1,575 @@ | |||
| 1 | use embassy_futures::select::{select3, Either3}; | ||
| 2 | use embassy_net_driver_channel as ch; | ||
| 3 | use embassy_sync::pubsub::PubSubBehavior; | ||
| 4 | use embassy_time::{block_for, Duration, Timer}; | ||
| 5 | use embedded_hal_1::digital::OutputPin; | ||
| 6 | |||
| 7 | use crate::bus::Bus; | ||
| 8 | pub use crate::bus::SpiBusCyw43; | ||
| 9 | use crate::consts::*; | ||
| 10 | use crate::events::{Event, Events, Status}; | ||
| 11 | use crate::fmt::Bytes; | ||
| 12 | use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; | ||
| 13 | use crate::nvram::NVRAM; | ||
| 14 | use crate::structs::*; | ||
| 15 | use crate::{events, slice8_mut, Core, CHIP, MTU}; | ||
| 16 | |||
| 17 | #[cfg(feature = "firmware-logs")] | ||
| 18 | struct LogState { | ||
| 19 | addr: u32, | ||
| 20 | last_idx: usize, | ||
| 21 | buf: [u8; 256], | ||
| 22 | buf_count: usize, | ||
| 23 | } | ||
| 24 | |||
| 25 | #[cfg(feature = "firmware-logs")] | ||
| 26 | impl Default for LogState { | ||
| 27 | fn default() -> Self { | ||
| 28 | Self { | ||
| 29 | addr: Default::default(), | ||
| 30 | last_idx: Default::default(), | ||
| 31 | buf: [0; 256], | ||
| 32 | buf_count: Default::default(), | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | pub struct Runner<'a, PWR, SPI> { | ||
| 38 | ch: ch::Runner<'a, MTU>, | ||
| 39 | bus: Bus<PWR, SPI>, | ||
| 40 | |||
| 41 | ioctl_state: &'a IoctlState, | ||
| 42 | ioctl_id: u16, | ||
| 43 | sdpcm_seq: u8, | ||
| 44 | sdpcm_seq_max: u8, | ||
| 45 | |||
| 46 | events: &'a Events, | ||
| 47 | |||
| 48 | #[cfg(feature = "firmware-logs")] | ||
| 49 | log: LogState, | ||
| 50 | } | ||
| 51 | |||
| 52 | impl<'a, PWR, SPI> Runner<'a, PWR, SPI> | ||
| 53 | where | ||
| 54 | PWR: OutputPin, | ||
| 55 | SPI: SpiBusCyw43, | ||
| 56 | { | ||
| 57 | pub(crate) fn new( | ||
| 58 | ch: ch::Runner<'a, MTU>, | ||
| 59 | bus: Bus<PWR, SPI>, | ||
| 60 | ioctl_state: &'a IoctlState, | ||
| 61 | events: &'a Events, | ||
| 62 | ) -> Self { | ||
| 63 | Self { | ||
| 64 | ch, | ||
| 65 | bus, | ||
| 66 | ioctl_state, | ||
| 67 | ioctl_id: 0, | ||
| 68 | sdpcm_seq: 0, | ||
| 69 | sdpcm_seq_max: 1, | ||
| 70 | events, | ||
| 71 | #[cfg(feature = "firmware-logs")] | ||
| 72 | log: LogState::default(), | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | pub(crate) async fn init(&mut self, firmware: &[u8]) { | ||
| 77 | self.bus.init().await; | ||
| 78 | |||
| 79 | // Init ALP (Active Low Power) clock | ||
| 80 | self.bus | ||
| 81 | .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) | ||
| 82 | .await; | ||
| 83 | debug!("waiting for clock..."); | ||
| 84 | while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} | ||
| 85 | debug!("clock ok"); | ||
| 86 | |||
| 87 | let chip_id = self.bus.bp_read16(0x1800_0000).await; | ||
| 88 | debug!("chip ID: {}", chip_id); | ||
| 89 | |||
| 90 | // Upload firmware. | ||
| 91 | self.core_disable(Core::WLAN).await; | ||
| 92 | self.core_reset(Core::SOCSRAM).await; | ||
| 93 | self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; | ||
| 94 | self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; | ||
| 95 | |||
| 96 | let ram_addr = CHIP.atcm_ram_base_address; | ||
| 97 | |||
| 98 | debug!("loading fw"); | ||
| 99 | self.bus.bp_write(ram_addr, firmware).await; | ||
| 100 | |||
| 101 | debug!("loading nvram"); | ||
| 102 | // Round up to 4 bytes. | ||
| 103 | let nvram_len = (NVRAM.len() + 3) / 4 * 4; | ||
| 104 | self.bus | ||
| 105 | .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) | ||
| 106 | .await; | ||
| 107 | |||
| 108 | let nvram_len_words = nvram_len as u32 / 4; | ||
| 109 | let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; | ||
| 110 | self.bus | ||
| 111 | .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) | ||
| 112 | .await; | ||
| 113 | |||
| 114 | // Start core! | ||
| 115 | debug!("starting up core..."); | ||
| 116 | self.core_reset(Core::WLAN).await; | ||
| 117 | assert!(self.core_is_up(Core::WLAN).await); | ||
| 118 | |||
| 119 | while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} | ||
| 120 | |||
| 121 | // "Set up the interrupt mask and enable interrupts" | ||
| 122 | // self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; | ||
| 123 | |||
| 124 | self.bus | ||
| 125 | .write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, IRQ_F2_PACKET_AVAILABLE) | ||
| 126 | .await; | ||
| 127 | |||
| 128 | // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." | ||
| 129 | // Sounds scary... | ||
| 130 | self.bus | ||
| 131 | .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32) | ||
| 132 | .await; | ||
| 133 | |||
| 134 | // wait for wifi startup | ||
| 135 | debug!("waiting for wifi init..."); | ||
| 136 | while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} | ||
| 137 | |||
| 138 | // Some random configs related to sleep. | ||
| 139 | // These aren't needed if we don't want to sleep the bus. | ||
| 140 | // TODO do we need to sleep the bus to read the irq line, due to | ||
| 141 | // being on the same pin as MOSI/MISO? | ||
| 142 | |||
| 143 | /* | ||
| 144 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; | ||
| 145 | val |= 0x02; // WAKE_TILL_HT_AVAIL | ||
| 146 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; | ||
| 147 | self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 | ||
| 148 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT | ||
| 149 | |||
| 150 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; | ||
| 151 | val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON | ||
| 152 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; | ||
| 153 | */ | ||
| 154 | |||
| 155 | // clear pulls | ||
| 156 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; | ||
| 157 | let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; | ||
| 158 | |||
| 159 | // start HT clock | ||
| 160 | //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; | ||
| 161 | //debug!("waiting for HT clock..."); | ||
| 162 | //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} | ||
| 163 | //debug!("clock ok"); | ||
| 164 | |||
| 165 | #[cfg(feature = "firmware-logs")] | ||
| 166 | self.log_init().await; | ||
| 167 | |||
| 168 | debug!("wifi init done"); | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "firmware-logs")] | ||
| 172 | async fn log_init(&mut self) { | ||
| 173 | // Initialize shared memory for logging. | ||
| 174 | |||
| 175 | let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; | ||
| 176 | let shared_addr = self.bus.bp_read32(addr).await; | ||
| 177 | debug!("shared_addr {:08x}", shared_addr); | ||
| 178 | |||
| 179 | let mut shared = [0; SharedMemData::SIZE]; | ||
| 180 | self.bus.bp_read(shared_addr, &mut shared).await; | ||
| 181 | let shared = SharedMemData::from_bytes(&shared); | ||
| 182 | |||
| 183 | self.log.addr = shared.console_addr + 8; | ||
| 184 | } | ||
| 185 | |||
| 186 | #[cfg(feature = "firmware-logs")] | ||
| 187 | async fn log_read(&mut self) { | ||
| 188 | // Read log struct | ||
| 189 | let mut log = [0; SharedMemLog::SIZE]; | ||
| 190 | self.bus.bp_read(self.log.addr, &mut log).await; | ||
| 191 | let log = SharedMemLog::from_bytes(&log); | ||
| 192 | |||
| 193 | let idx = log.idx as usize; | ||
| 194 | |||
| 195 | // If pointer hasn't moved, no need to do anything. | ||
| 196 | if idx == self.log.last_idx { | ||
| 197 | return; | ||
| 198 | } | ||
| 199 | |||
| 200 | // Read entire buf for now. We could read only what we need, but then we | ||
| 201 | // run into annoying alignment issues in `bp_read`. | ||
| 202 | let mut buf = [0; 0x400]; | ||
| 203 | self.bus.bp_read(log.buf, &mut buf).await; | ||
| 204 | |||
| 205 | while self.log.last_idx != idx as usize { | ||
| 206 | let b = buf[self.log.last_idx]; | ||
| 207 | if b == b'\r' || b == b'\n' { | ||
| 208 | if self.log.buf_count != 0 { | ||
| 209 | let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; | ||
| 210 | debug!("LOGS: {}", s); | ||
| 211 | self.log.buf_count = 0; | ||
| 212 | } | ||
| 213 | } else if self.log.buf_count < self.log.buf.len() { | ||
| 214 | self.log.buf[self.log.buf_count] = b; | ||
| 215 | self.log.buf_count += 1; | ||
| 216 | } | ||
| 217 | |||
| 218 | self.log.last_idx += 1; | ||
| 219 | if self.log.last_idx == 0x400 { | ||
| 220 | self.log.last_idx = 0; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | pub async fn run(mut self) -> ! { | ||
| 226 | let mut buf = [0; 512]; | ||
| 227 | loop { | ||
| 228 | #[cfg(feature = "firmware-logs")] | ||
| 229 | self.log_read().await; | ||
| 230 | |||
| 231 | if self.has_credit() { | ||
| 232 | let ioctl = self.ioctl_state.wait_pending(); | ||
| 233 | let tx = self.ch.tx_buf(); | ||
| 234 | let ev = self.bus.wait_for_event(); | ||
| 235 | |||
| 236 | match select3(ioctl, tx, ev).await { | ||
| 237 | Either3::First(PendingIoctl { | ||
| 238 | buf: iobuf, | ||
| 239 | kind, | ||
| 240 | cmd, | ||
| 241 | iface, | ||
| 242 | }) => { | ||
| 243 | self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; | ||
| 244 | self.check_status(&mut buf).await; | ||
| 245 | } | ||
| 246 | Either3::Second(packet) => { | ||
| 247 | trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); | ||
| 248 | |||
| 249 | let mut buf = [0; 512]; | ||
| 250 | let buf8 = slice8_mut(&mut buf); | ||
| 251 | |||
| 252 | // There MUST be 2 bytes of padding between the SDPCM and BDC headers. | ||
| 253 | // And ONLY for data packets! | ||
| 254 | // No idea why, but the firmware will append two zero bytes to the tx'd packets | ||
| 255 | // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it | ||
| 256 | // be oversized and get dropped. | ||
| 257 | // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90 | ||
| 258 | // 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 | ||
| 259 | // ¯\_(ツ)_/¯ | ||
| 260 | const PADDING_SIZE: usize = 2; | ||
| 261 | let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len(); | ||
| 262 | |||
| 263 | let seq = self.sdpcm_seq; | ||
| 264 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 265 | |||
| 266 | let sdpcm_header = SdpcmHeader { | ||
| 267 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 268 | len_inv: !total_len as u16, | ||
| 269 | sequence: seq, | ||
| 270 | channel_and_flags: CHANNEL_TYPE_DATA, | ||
| 271 | next_length: 0, | ||
| 272 | header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _, | ||
| 273 | wireless_flow_control: 0, | ||
| 274 | bus_data_credit: 0, | ||
| 275 | reserved: [0, 0], | ||
| 276 | }; | ||
| 277 | |||
| 278 | let bdc_header = BdcHeader { | ||
| 279 | flags: BDC_VERSION << BDC_VERSION_SHIFT, | ||
| 280 | priority: 0, | ||
| 281 | flags2: 0, | ||
| 282 | data_offset: 0, | ||
| 283 | }; | ||
| 284 | trace!("tx {:?}", sdpcm_header); | ||
| 285 | trace!(" {:?}", bdc_header); | ||
| 286 | |||
| 287 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 288 | buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE] | ||
| 289 | .copy_from_slice(&bdc_header.to_bytes()); | ||
| 290 | buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()] | ||
| 291 | .copy_from_slice(packet); | ||
| 292 | |||
| 293 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 294 | |||
| 295 | trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); | ||
| 296 | |||
| 297 | self.bus.wlan_write(&buf[..(total_len / 4)]).await; | ||
| 298 | self.ch.tx_done(); | ||
| 299 | self.check_status(&mut buf).await; | ||
| 300 | } | ||
| 301 | Either3::Third(()) => { | ||
| 302 | self.handle_irq(&mut buf).await; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | } else { | ||
| 306 | warn!("TX stalled"); | ||
| 307 | self.bus.wait_for_event().await; | ||
| 308 | self.handle_irq(&mut buf).await; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Wait for IRQ on F2 packet available | ||
| 314 | async fn handle_irq(&mut self, buf: &mut [u32; 512]) { | ||
| 315 | // Receive stuff | ||
| 316 | let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; | ||
| 317 | trace!("irq{}", FormatInterrupt(irq)); | ||
| 318 | |||
| 319 | if irq & IRQ_F2_PACKET_AVAILABLE != 0 { | ||
| 320 | self.check_status(buf).await; | ||
| 321 | } | ||
| 322 | |||
| 323 | if irq & IRQ_DATA_UNAVAILABLE != 0 { | ||
| 324 | // TODO what should we do here? | ||
| 325 | warn!("IRQ DATA_UNAVAILABLE, clearing..."); | ||
| 326 | self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Handle F2 events while status register is set | ||
| 331 | async fn check_status(&mut self, buf: &mut [u32; 512]) { | ||
| 332 | loop { | ||
| 333 | let status = self.bus.status(); | ||
| 334 | trace!("check status{}", FormatStatus(status)); | ||
| 335 | |||
| 336 | if status & STATUS_F2_PKT_AVAILABLE != 0 { | ||
| 337 | let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; | ||
| 338 | self.bus.wlan_read(buf, len).await; | ||
| 339 | trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); | ||
| 340 | self.rx(&mut slice8_mut(buf)[..len as usize]); | ||
| 341 | } else { | ||
| 342 | break; | ||
| 343 | } | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | fn rx(&mut self, packet: &mut [u8]) { | ||
| 348 | let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return }; | ||
| 349 | |||
| 350 | self.update_credit(&sdpcm_header); | ||
| 351 | |||
| 352 | let channel = sdpcm_header.channel_and_flags & 0x0f; | ||
| 353 | |||
| 354 | match channel { | ||
| 355 | CHANNEL_TYPE_CONTROL => { | ||
| 356 | let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; }; | ||
| 357 | trace!(" {:?}", cdc_header); | ||
| 358 | |||
| 359 | if cdc_header.id == self.ioctl_id { | ||
| 360 | if cdc_header.status != 0 { | ||
| 361 | // TODO: propagate error instead | ||
| 362 | panic!("IOCTL error {}", cdc_header.status as i32); | ||
| 363 | } | ||
| 364 | |||
| 365 | self.ioctl_state.ioctl_done(response); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | CHANNEL_TYPE_EVENT => { | ||
| 369 | let Some((_, bdc_packet)) = BdcHeader::parse(payload) else { | ||
| 370 | warn!("BDC event, incomplete header"); | ||
| 371 | return; | ||
| 372 | }; | ||
| 373 | |||
| 374 | let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else { | ||
| 375 | warn!("BDC event, incomplete data"); | ||
| 376 | return; | ||
| 377 | }; | ||
| 378 | |||
| 379 | const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h | ||
| 380 | if event_packet.eth.ether_type != ETH_P_LINK_CTL { | ||
| 381 | warn!( | ||
| 382 | "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", | ||
| 383 | event_packet.eth.ether_type, ETH_P_LINK_CTL | ||
| 384 | ); | ||
| 385 | return; | ||
| 386 | } | ||
| 387 | const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; | ||
| 388 | if event_packet.hdr.oui != BROADCOM_OUI { | ||
| 389 | warn!( | ||
| 390 | "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", | ||
| 391 | Bytes(&event_packet.hdr.oui), | ||
| 392 | Bytes(BROADCOM_OUI) | ||
| 393 | ); | ||
| 394 | return; | ||
| 395 | } | ||
| 396 | const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; | ||
| 397 | if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { | ||
| 398 | warn!("unexpected subtype {}", event_packet.hdr.subtype); | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | |||
| 402 | const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; | ||
| 403 | if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { | ||
| 404 | warn!("unexpected user_subtype {}", event_packet.hdr.subtype); | ||
| 405 | return; | ||
| 406 | } | ||
| 407 | |||
| 408 | let evt_type = events::Event::from(event_packet.msg.event_type as u8); | ||
| 409 | debug!( | ||
| 410 | "=== EVENT {:?}: {:?} {:02x}", | ||
| 411 | evt_type, | ||
| 412 | event_packet.msg, | ||
| 413 | Bytes(evt_data) | ||
| 414 | ); | ||
| 415 | |||
| 416 | if self.events.mask.is_enabled(evt_type) { | ||
| 417 | let status = event_packet.msg.status; | ||
| 418 | let event_payload = match evt_type { | ||
| 419 | Event::ESCAN_RESULT if status == EStatus::PARTIAL => { | ||
| 420 | let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return }; | ||
| 421 | let Some(bss_info) = BssInfo::parse(bss_info) else { return }; | ||
| 422 | events::Payload::BssInfo(*bss_info) | ||
| 423 | } | ||
| 424 | Event::ESCAN_RESULT => events::Payload::None, | ||
| 425 | _ => events::Payload::None, | ||
| 426 | }; | ||
| 427 | |||
| 428 | // this intentionally uses the non-blocking publish immediate | ||
| 429 | // publish() is a deadlock risk in the current design as awaiting here prevents ioctls | ||
| 430 | // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event | ||
| 431 | // (if they are actively awaiting the queue) | ||
| 432 | self.events.queue.publish_immediate(events::Message::new( | ||
| 433 | Status { | ||
| 434 | event_type: evt_type, | ||
| 435 | status, | ||
| 436 | }, | ||
| 437 | event_payload, | ||
| 438 | )); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | CHANNEL_TYPE_DATA => { | ||
| 442 | let Some((_, packet)) = BdcHeader::parse(payload) else { return }; | ||
| 443 | trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); | ||
| 444 | |||
| 445 | match self.ch.try_rx_buf() { | ||
| 446 | Some(buf) => { | ||
| 447 | buf[..packet.len()].copy_from_slice(packet); | ||
| 448 | self.ch.rx_done(packet.len()) | ||
| 449 | } | ||
| 450 | None => warn!("failed to push rxd packet to the channel."), | ||
| 451 | } | ||
| 452 | } | ||
| 453 | _ => {} | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { | ||
| 458 | if sdpcm_header.channel_and_flags & 0xf < 3 { | ||
| 459 | let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; | ||
| 460 | if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { | ||
| 461 | sdpcm_seq_max = self.sdpcm_seq + 2; | ||
| 462 | } | ||
| 463 | self.sdpcm_seq_max = sdpcm_seq_max; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | fn has_credit(&self) -> bool { | ||
| 468 | self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 | ||
| 469 | } | ||
| 470 | |||
| 471 | async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { | ||
| 472 | let mut buf = [0; 512]; | ||
| 473 | let buf8 = slice8_mut(&mut buf); | ||
| 474 | |||
| 475 | let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); | ||
| 476 | |||
| 477 | let sdpcm_seq = self.sdpcm_seq; | ||
| 478 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 479 | self.ioctl_id = self.ioctl_id.wrapping_add(1); | ||
| 480 | |||
| 481 | let sdpcm_header = SdpcmHeader { | ||
| 482 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 483 | len_inv: !total_len as u16, | ||
| 484 | sequence: sdpcm_seq, | ||
| 485 | channel_and_flags: CHANNEL_TYPE_CONTROL, | ||
| 486 | next_length: 0, | ||
| 487 | header_length: SdpcmHeader::SIZE as _, | ||
| 488 | wireless_flow_control: 0, | ||
| 489 | bus_data_credit: 0, | ||
| 490 | reserved: [0, 0], | ||
| 491 | }; | ||
| 492 | |||
| 493 | let cdc_header = CdcHeader { | ||
| 494 | cmd: cmd, | ||
| 495 | len: data.len() as _, | ||
| 496 | flags: kind as u16 | (iface as u16) << 12, | ||
| 497 | id: self.ioctl_id, | ||
| 498 | status: 0, | ||
| 499 | }; | ||
| 500 | trace!("tx {:?}", sdpcm_header); | ||
| 501 | trace!(" {:?}", cdc_header); | ||
| 502 | |||
| 503 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 504 | buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); | ||
| 505 | buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); | ||
| 506 | |||
| 507 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 508 | |||
| 509 | trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); | ||
| 510 | |||
| 511 | self.bus.wlan_write(&buf[..total_len / 4]).await; | ||
| 512 | } | ||
| 513 | |||
| 514 | async fn core_disable(&mut self, core: Core) { | ||
| 515 | let base = core.base_addr(); | ||
| 516 | |||
| 517 | // Dummy read? | ||
| 518 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 519 | |||
| 520 | // Check it isn't already reset | ||
| 521 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 522 | if r & AI_RESETCTRL_BIT_RESET != 0 { | ||
| 523 | return; | ||
| 524 | } | ||
| 525 | |||
| 526 | self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; | ||
| 527 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 528 | |||
| 529 | block_for(Duration::from_millis(1)); | ||
| 530 | |||
| 531 | self.bus | ||
| 532 | .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) | ||
| 533 | .await; | ||
| 534 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 535 | } | ||
| 536 | |||
| 537 | async fn core_reset(&mut self, core: Core) { | ||
| 538 | self.core_disable(core).await; | ||
| 539 | |||
| 540 | let base = core.base_addr(); | ||
| 541 | self.bus | ||
| 542 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) | ||
| 543 | .await; | ||
| 544 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 545 | |||
| 546 | self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; | ||
| 547 | |||
| 548 | Timer::after(Duration::from_millis(1)).await; | ||
| 549 | |||
| 550 | self.bus | ||
| 551 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) | ||
| 552 | .await; | ||
| 553 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 554 | |||
| 555 | Timer::after(Duration::from_millis(1)).await; | ||
| 556 | } | ||
| 557 | |||
| 558 | async fn core_is_up(&mut self, core: Core) -> bool { | ||
| 559 | let base = core.base_addr(); | ||
| 560 | |||
| 561 | let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 562 | if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { | ||
| 563 | debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); | ||
| 564 | return false; | ||
| 565 | } | ||
| 566 | |||
| 567 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 568 | if r & (AI_RESETCTRL_BIT_RESET) != 0 { | ||
| 569 | debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); | ||
| 570 | return false; | ||
| 571 | } | ||
| 572 | |||
| 573 | true | ||
| 574 | } | ||
| 575 | } | ||
