diff options
| author | kbleeke <[email protected]> | 2023-03-02 12:10:13 +0100 |
|---|---|---|
| committer | kbleeke <[email protected]> | 2023-03-22 15:35:02 +0100 |
| commit | 20923080e6ea313278b5f3aa8ec21055c6208527 (patch) | |
| tree | b30e9545302fa9ef68dfa25748602877f838f722 /src | |
| parent | 8b24fe3df02862991b2574f9d5c9ada7bd27706b (diff) | |
split lib.rs into multiple files
Diffstat (limited to 'src')
| -rw-r--r-- | src/control.rs | 299 | ||||
| -rw-r--r-- | src/lib.rs | 888 | ||||
| -rw-r--r-- | src/nvram.rs | 54 | ||||
| -rw-r--r-- | src/runner.rs | 564 |
4 files changed, 925 insertions, 880 deletions
diff --git a/src/control.rs b/src/control.rs new file mode 100644 index 000000000..7f1c9fe86 --- /dev/null +++ b/src/control.rs | |||
| @@ -0,0 +1,299 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::cmp::{max, min}; | ||
| 3 | |||
| 4 | use ch::driver::LinkState; | ||
| 5 | use embassy_futures::yield_now; | ||
| 6 | use embassy_net_driver_channel as ch; | ||
| 7 | use embassy_time::{Duration, Timer}; | ||
| 8 | |||
| 9 | pub use crate::bus::SpiBusCyw43; | ||
| 10 | use crate::consts::*; | ||
| 11 | use crate::events::{Event, EventQueue}; | ||
| 12 | use crate::structs::*; | ||
| 13 | use crate::{countries, IoctlState, IoctlType, PowerManagementMode}; | ||
| 14 | |||
| 15 | pub struct Control<'a> { | ||
| 16 | state_ch: ch::StateRunner<'a>, | ||
| 17 | event_sub: &'a EventQueue, | ||
| 18 | ioctl_state: &'a Cell<IoctlState>, | ||
| 19 | } | ||
| 20 | |||
| 21 | impl<'a> Control<'a> { | ||
| 22 | pub(crate) fn new( | ||
| 23 | state_ch: ch::StateRunner<'a>, | ||
| 24 | event_sub: &'a EventQueue, | ||
| 25 | ioctl_state: &'a Cell<IoctlState>, | ||
| 26 | ) -> Self { | ||
| 27 | Self { | ||
| 28 | state_ch, | ||
| 29 | event_sub, | ||
| 30 | ioctl_state, | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | pub async fn init(&mut self, clm: &[u8]) { | ||
| 35 | const CHUNK_SIZE: usize = 1024; | ||
| 36 | |||
| 37 | info!("Downloading CLM..."); | ||
| 38 | |||
| 39 | let mut offs = 0; | ||
| 40 | for chunk in clm.chunks(CHUNK_SIZE) { | ||
| 41 | let mut flag = DOWNLOAD_FLAG_HANDLER_VER; | ||
| 42 | if offs == 0 { | ||
| 43 | flag |= DOWNLOAD_FLAG_BEGIN; | ||
| 44 | } | ||
| 45 | offs += chunk.len(); | ||
| 46 | if offs == clm.len() { | ||
| 47 | flag |= DOWNLOAD_FLAG_END; | ||
| 48 | } | ||
| 49 | |||
| 50 | let header = DownloadHeader { | ||
| 51 | flag, | ||
| 52 | dload_type: DOWNLOAD_TYPE_CLM, | ||
| 53 | len: chunk.len() as _, | ||
| 54 | crc: 0, | ||
| 55 | }; | ||
| 56 | let mut buf = [0; 8 + 12 + CHUNK_SIZE]; | ||
| 57 | buf[0..8].copy_from_slice(b"clmload\x00"); | ||
| 58 | buf[8..20].copy_from_slice(&header.to_bytes()); | ||
| 59 | buf[20..][..chunk.len()].copy_from_slice(&chunk); | ||
| 60 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) | ||
| 61 | .await; | ||
| 62 | } | ||
| 63 | |||
| 64 | // check clmload ok | ||
| 65 | assert_eq!(self.get_iovar_u32("clmload_status").await, 0); | ||
| 66 | |||
| 67 | info!("Configuring misc stuff..."); | ||
| 68 | |||
| 69 | // Disable tx gloming which transfers multiple packets in one request. | ||
| 70 | // 'glom' is short for "conglomerate" which means "gather together into | ||
| 71 | // a compact mass". | ||
| 72 | self.set_iovar_u32("bus:txglom", 0).await; | ||
| 73 | self.set_iovar_u32("apsta", 1).await; | ||
| 74 | |||
| 75 | // read MAC addr. | ||
| 76 | let mut mac_addr = [0; 6]; | ||
| 77 | assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); | ||
| 78 | info!("mac addr: {:02x}", mac_addr); | ||
| 79 | |||
| 80 | let country = countries::WORLD_WIDE_XX; | ||
| 81 | let country_info = CountryInfo { | ||
| 82 | country_abbrev: [country.code[0], country.code[1], 0, 0], | ||
| 83 | country_code: [country.code[0], country.code[1], 0, 0], | ||
| 84 | rev: if country.rev == 0 { -1 } else { country.rev as _ }, | ||
| 85 | }; | ||
| 86 | self.set_iovar("country", &country_info.to_bytes()).await; | ||
| 87 | |||
| 88 | // set country takes some time, next ioctls fail if we don't wait. | ||
| 89 | Timer::after(Duration::from_millis(100)).await; | ||
| 90 | |||
| 91 | // Set antenna to chip antenna | ||
| 92 | self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; | ||
| 93 | |||
| 94 | self.set_iovar_u32("bus:txglom", 0).await; | ||
| 95 | Timer::after(Duration::from_millis(100)).await; | ||
| 96 | //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? | ||
| 97 | //Timer::after(Duration::from_millis(100)).await; | ||
| 98 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 99 | Timer::after(Duration::from_millis(100)).await; | ||
| 100 | self.set_iovar_u32("ampdu_mpdu", 4).await; | ||
| 101 | Timer::after(Duration::from_millis(100)).await; | ||
| 102 | //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes | ||
| 103 | |||
| 104 | //Timer::after(Duration::from_millis(100)).await; | ||
| 105 | |||
| 106 | // evts | ||
| 107 | let mut evts = EventMask { | ||
| 108 | iface: 0, | ||
| 109 | events: [0xFF; 24], | ||
| 110 | }; | ||
| 111 | |||
| 112 | // Disable spammy uninteresting events. | ||
| 113 | evts.unset(Event::RADIO); | ||
| 114 | evts.unset(Event::IF); | ||
| 115 | evts.unset(Event::PROBREQ_MSG); | ||
| 116 | evts.unset(Event::PROBREQ_MSG_RX); | ||
| 117 | evts.unset(Event::PROBRESP_MSG); | ||
| 118 | evts.unset(Event::PROBRESP_MSG); | ||
| 119 | evts.unset(Event::ROAM); | ||
| 120 | |||
| 121 | self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; | ||
| 122 | |||
| 123 | Timer::after(Duration::from_millis(100)).await; | ||
| 124 | |||
| 125 | // set wifi up | ||
| 126 | self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; | ||
| 127 | |||
| 128 | Timer::after(Duration::from_millis(100)).await; | ||
| 129 | |||
| 130 | self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto | ||
| 131 | self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any | ||
| 132 | |||
| 133 | Timer::after(Duration::from_millis(100)).await; | ||
| 134 | |||
| 135 | self.state_ch.set_ethernet_address(mac_addr); | ||
| 136 | self.state_ch.set_link_state(LinkState::Up); // TODO do on join/leave | ||
| 137 | |||
| 138 | info!("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) { | ||
| 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 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) | ||
| 167 | .await; // set_ssid | ||
| 168 | |||
| 169 | info!("JOINED"); | ||
| 170 | } | ||
| 171 | |||
| 172 | pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) { | ||
| 173 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 174 | |||
| 175 | self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 | ||
| 176 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; | ||
| 177 | self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; | ||
| 178 | self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; | ||
| 179 | |||
| 180 | Timer::after(Duration::from_millis(100)).await; | ||
| 181 | |||
| 182 | let mut pfi = PassphraseInfo { | ||
| 183 | len: passphrase.len() as _, | ||
| 184 | flags: 1, | ||
| 185 | passphrase: [0; 64], | ||
| 186 | }; | ||
| 187 | pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); | ||
| 188 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) | ||
| 189 | .await; // WLC_SET_WSEC_PMK | ||
| 190 | |||
| 191 | self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 | ||
| 192 | self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) | ||
| 193 | self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth | ||
| 194 | |||
| 195 | let mut i = SsidInfo { | ||
| 196 | len: ssid.len() as _, | ||
| 197 | ssid: [0; 32], | ||
| 198 | }; | ||
| 199 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); | ||
| 200 | |||
| 201 | let mut subscriber = self.event_sub.subscriber().unwrap(); | ||
| 202 | self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; // set_ssid | ||
| 203 | |||
| 204 | loop { | ||
| 205 | let msg = subscriber.next_message_pure().await; | ||
| 206 | if msg.event_type == Event::AUTH && msg.status != 0 { | ||
| 207 | // retry | ||
| 208 | defmt::warn!("JOIN failed with status={}", msg.status); | ||
| 209 | self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; | ||
| 210 | } else if msg.event_type == Event::JOIN && msg.status == 0 { | ||
| 211 | // successful join | ||
| 212 | break; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | info!("JOINED"); | ||
| 217 | } | ||
| 218 | |||
| 219 | pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { | ||
| 220 | assert!(gpio_n < 3); | ||
| 221 | self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) | ||
| 222 | .await | ||
| 223 | } | ||
| 224 | |||
| 225 | async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { | ||
| 226 | let mut buf = [0; 8]; | ||
| 227 | buf[0..4].copy_from_slice(&val1.to_le_bytes()); | ||
| 228 | buf[4..8].copy_from_slice(&val2.to_le_bytes()); | ||
| 229 | self.set_iovar(name, &buf).await | ||
| 230 | } | ||
| 231 | |||
| 232 | async fn set_iovar_u32(&mut self, name: &str, val: u32) { | ||
| 233 | self.set_iovar(name, &val.to_le_bytes()).await | ||
| 234 | } | ||
| 235 | |||
| 236 | async fn get_iovar_u32(&mut self, name: &str) -> u32 { | ||
| 237 | let mut buf = [0; 4]; | ||
| 238 | let len = self.get_iovar(name, &mut buf).await; | ||
| 239 | assert_eq!(len, 4); | ||
| 240 | u32::from_le_bytes(buf) | ||
| 241 | } | ||
| 242 | |||
| 243 | async fn set_iovar(&mut self, name: &str, val: &[u8]) { | ||
| 244 | info!("set {} = {:02x}", name, val); | ||
| 245 | |||
| 246 | let mut buf = [0; 64]; | ||
| 247 | buf[..name.len()].copy_from_slice(name.as_bytes()); | ||
| 248 | buf[name.len()] = 0; | ||
| 249 | buf[name.len() + 1..][..val.len()].copy_from_slice(val); | ||
| 250 | |||
| 251 | let total_len = name.len() + 1 + val.len(); | ||
| 252 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) | ||
| 253 | .await; | ||
| 254 | } | ||
| 255 | |||
| 256 | // TODO this is not really working, it always returns all zeros. | ||
| 257 | async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { | ||
| 258 | info!("get {}", name); | ||
| 259 | |||
| 260 | let mut buf = [0; 64]; | ||
| 261 | buf[..name.len()].copy_from_slice(name.as_bytes()); | ||
| 262 | buf[name.len()] = 0; | ||
| 263 | |||
| 264 | let total_len = max(name.len() + 1, res.len()); | ||
| 265 | let res_len = self | ||
| 266 | .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) | ||
| 267 | .await; | ||
| 268 | |||
| 269 | let out_len = min(res.len(), res_len); | ||
| 270 | res[..out_len].copy_from_slice(&buf[..out_len]); | ||
| 271 | out_len | ||
| 272 | } | ||
| 273 | |||
| 274 | async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { | ||
| 275 | let mut buf = val.to_le_bytes(); | ||
| 276 | self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; | ||
| 277 | } | ||
| 278 | |||
| 279 | async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { | ||
| 280 | // TODO cancel ioctl on future drop. | ||
| 281 | |||
| 282 | while !matches!(self.ioctl_state.get(), IoctlState::Idle) { | ||
| 283 | yield_now().await; | ||
| 284 | } | ||
| 285 | |||
| 286 | self.ioctl_state.set(IoctlState::Pending { kind, cmd, iface, buf }); | ||
| 287 | |||
| 288 | let resp_len = loop { | ||
| 289 | if let IoctlState::Done { resp_len } = self.ioctl_state.get() { | ||
| 290 | break resp_len; | ||
| 291 | } | ||
| 292 | yield_now().await; | ||
| 293 | }; | ||
| 294 | |||
| 295 | self.ioctl_state.set(IoctlState::Idle); | ||
| 296 | |||
| 297 | resp_len | ||
| 298 | } | ||
| 299 | } | ||
diff --git a/src/lib.rs b/src/lib.rs index 1b7d603d7..af8f74a6d 100644 --- a/src/lib.rs +++ b/src/lib.rs | |||
| @@ -13,23 +13,20 @@ mod countries; | |||
| 13 | mod events; | 13 | mod events; |
| 14 | mod structs; | 14 | mod structs; |
| 15 | 15 | ||
| 16 | mod control; | ||
| 17 | mod nvram; | ||
| 18 | mod runner; | ||
| 19 | |||
| 16 | use core::cell::Cell; | 20 | use core::cell::Cell; |
| 17 | use core::cmp::{max, min}; | ||
| 18 | use core::slice; | ||
| 19 | 21 | ||
| 20 | use ch::driver::LinkState; | ||
| 21 | use embassy_futures::yield_now; | ||
| 22 | use embassy_net_driver_channel as ch; | 22 | use embassy_net_driver_channel as ch; |
| 23 | use embassy_sync::pubsub::PubSubBehavior; | ||
| 24 | use embassy_time::{block_for, Duration, Timer}; | ||
| 25 | use embedded_hal_1::digital::OutputPin; | 23 | use embedded_hal_1::digital::OutputPin; |
| 26 | use events::EventQueue; | 24 | use events::EventQueue; |
| 27 | 25 | ||
| 28 | use crate::bus::Bus; | 26 | use crate::bus::Bus; |
| 29 | pub use crate::bus::SpiBusCyw43; | 27 | pub use crate::bus::SpiBusCyw43; |
| 30 | use crate::consts::*; | 28 | pub use crate::control::Control; |
| 31 | use crate::events::{Event, EventStatus}; | 29 | pub use crate::runner::Runner; |
| 32 | use crate::structs::*; | ||
| 33 | 30 | ||
| 34 | const MTU: usize = 1514; | 31 | const MTU: usize = 1514; |
| 35 | 32 | ||
| @@ -143,12 +140,6 @@ impl State { | |||
| 143 | } | 140 | } |
| 144 | } | 141 | } |
| 145 | 142 | ||
| 146 | pub struct Control<'a> { | ||
| 147 | state_ch: ch::StateRunner<'a>, | ||
| 148 | event_sub: &'a EventQueue, | ||
| 149 | ioctl_state: &'a Cell<IoctlState>, | ||
| 150 | } | ||
| 151 | |||
| 152 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 143 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 153 | pub enum PowerManagementMode { | 144 | pub enum PowerManagementMode { |
| 154 | /// Custom, officially unsupported mode. Use at your own risk. | 145 | /// Custom, officially unsupported mode. Use at your own risk. |
| @@ -233,297 +224,6 @@ impl PowerManagementMode { | |||
| 233 | } | 224 | } |
| 234 | } | 225 | } |
| 235 | 226 | ||
| 236 | impl<'a> Control<'a> { | ||
| 237 | pub async fn init(&mut self, clm: &[u8]) { | ||
| 238 | const CHUNK_SIZE: usize = 1024; | ||
| 239 | |||
| 240 | info!("Downloading CLM..."); | ||
| 241 | |||
| 242 | let mut offs = 0; | ||
| 243 | for chunk in clm.chunks(CHUNK_SIZE) { | ||
| 244 | let mut flag = DOWNLOAD_FLAG_HANDLER_VER; | ||
| 245 | if offs == 0 { | ||
| 246 | flag |= DOWNLOAD_FLAG_BEGIN; | ||
| 247 | } | ||
| 248 | offs += chunk.len(); | ||
| 249 | if offs == clm.len() { | ||
| 250 | flag |= DOWNLOAD_FLAG_END; | ||
| 251 | } | ||
| 252 | |||
| 253 | let header = DownloadHeader { | ||
| 254 | flag, | ||
| 255 | dload_type: DOWNLOAD_TYPE_CLM, | ||
| 256 | len: chunk.len() as _, | ||
| 257 | crc: 0, | ||
| 258 | }; | ||
| 259 | let mut buf = [0; 8 + 12 + CHUNK_SIZE]; | ||
| 260 | buf[0..8].copy_from_slice(b"clmload\x00"); | ||
| 261 | buf[8..20].copy_from_slice(&header.to_bytes()); | ||
| 262 | buf[20..][..chunk.len()].copy_from_slice(&chunk); | ||
| 263 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) | ||
| 264 | .await; | ||
| 265 | } | ||
| 266 | |||
| 267 | // check clmload ok | ||
| 268 | assert_eq!(self.get_iovar_u32("clmload_status").await, 0); | ||
| 269 | |||
| 270 | info!("Configuring misc stuff..."); | ||
| 271 | |||
| 272 | // Disable tx gloming which transfers multiple packets in one request. | ||
| 273 | // 'glom' is short for "conglomerate" which means "gather together into | ||
| 274 | // a compact mass". | ||
| 275 | self.set_iovar_u32("bus:txglom", 0).await; | ||
| 276 | self.set_iovar_u32("apsta", 1).await; | ||
| 277 | |||
| 278 | // read MAC addr. | ||
| 279 | let mut mac_addr = [0; 6]; | ||
| 280 | assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); | ||
| 281 | info!("mac addr: {:02x}", mac_addr); | ||
| 282 | |||
| 283 | let country = countries::WORLD_WIDE_XX; | ||
| 284 | let country_info = CountryInfo { | ||
| 285 | country_abbrev: [country.code[0], country.code[1], 0, 0], | ||
| 286 | country_code: [country.code[0], country.code[1], 0, 0], | ||
| 287 | rev: if country.rev == 0 { -1 } else { country.rev as _ }, | ||
| 288 | }; | ||
| 289 | self.set_iovar("country", &country_info.to_bytes()).await; | ||
| 290 | |||
| 291 | // set country takes some time, next ioctls fail if we don't wait. | ||
| 292 | Timer::after(Duration::from_millis(100)).await; | ||
| 293 | |||
| 294 | // Set antenna to chip antenna | ||
| 295 | self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; | ||
| 296 | |||
| 297 | self.set_iovar_u32("bus:txglom", 0).await; | ||
| 298 | Timer::after(Duration::from_millis(100)).await; | ||
| 299 | //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? | ||
| 300 | //Timer::after(Duration::from_millis(100)).await; | ||
| 301 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 302 | Timer::after(Duration::from_millis(100)).await; | ||
| 303 | self.set_iovar_u32("ampdu_mpdu", 4).await; | ||
| 304 | Timer::after(Duration::from_millis(100)).await; | ||
| 305 | //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes | ||
| 306 | |||
| 307 | //Timer::after(Duration::from_millis(100)).await; | ||
| 308 | |||
| 309 | // evts | ||
| 310 | let mut evts = EventMask { | ||
| 311 | iface: 0, | ||
| 312 | events: [0xFF; 24], | ||
| 313 | }; | ||
| 314 | |||
| 315 | // Disable spammy uninteresting events. | ||
| 316 | evts.unset(Event::RADIO); | ||
| 317 | evts.unset(Event::IF); | ||
| 318 | evts.unset(Event::PROBREQ_MSG); | ||
| 319 | evts.unset(Event::PROBREQ_MSG_RX); | ||
| 320 | evts.unset(Event::PROBRESP_MSG); | ||
| 321 | evts.unset(Event::PROBRESP_MSG); | ||
| 322 | evts.unset(Event::ROAM); | ||
| 323 | |||
| 324 | self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; | ||
| 325 | |||
| 326 | Timer::after(Duration::from_millis(100)).await; | ||
| 327 | |||
| 328 | // set wifi up | ||
| 329 | self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; | ||
| 330 | |||
| 331 | Timer::after(Duration::from_millis(100)).await; | ||
| 332 | |||
| 333 | self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto | ||
| 334 | self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any | ||
| 335 | |||
| 336 | Timer::after(Duration::from_millis(100)).await; | ||
| 337 | |||
| 338 | self.state_ch.set_ethernet_address(mac_addr); | ||
| 339 | self.state_ch.set_link_state(LinkState::Up); // TODO do on join/leave | ||
| 340 | |||
| 341 | info!("INIT DONE"); | ||
| 342 | } | ||
| 343 | |||
| 344 | pub async fn set_power_management(&mut self, mode: PowerManagementMode) { | ||
| 345 | // power save mode | ||
| 346 | let mode_num = mode.mode(); | ||
| 347 | if mode_num == 2 { | ||
| 348 | self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; | ||
| 349 | self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; | ||
| 350 | self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; | ||
| 351 | self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; | ||
| 352 | } | ||
| 353 | self.ioctl_set_u32(86, 0, mode_num).await; | ||
| 354 | } | ||
| 355 | |||
| 356 | pub async fn join_open(&mut self, ssid: &str) { | ||
| 357 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 358 | |||
| 359 | self.ioctl_set_u32(134, 0, 0).await; // wsec = open | ||
| 360 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; | ||
| 361 | self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 | ||
| 362 | self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) | ||
| 363 | |||
| 364 | let mut i = SsidInfo { | ||
| 365 | len: ssid.len() as _, | ||
| 366 | ssid: [0; 32], | ||
| 367 | }; | ||
| 368 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); | ||
| 369 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) | ||
| 370 | .await; // set_ssid | ||
| 371 | |||
| 372 | info!("JOINED"); | ||
| 373 | } | ||
| 374 | |||
| 375 | pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) { | ||
| 376 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 377 | |||
| 378 | self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 | ||
| 379 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; | ||
| 380 | self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; | ||
| 381 | self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; | ||
| 382 | |||
| 383 | Timer::after(Duration::from_millis(100)).await; | ||
| 384 | |||
| 385 | let mut pfi = PassphraseInfo { | ||
| 386 | len: passphrase.len() as _, | ||
| 387 | flags: 1, | ||
| 388 | passphrase: [0; 64], | ||
| 389 | }; | ||
| 390 | pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); | ||
| 391 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) | ||
| 392 | .await; // WLC_SET_WSEC_PMK | ||
| 393 | |||
| 394 | self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 | ||
| 395 | self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) | ||
| 396 | self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth | ||
| 397 | |||
| 398 | let mut i = SsidInfo { | ||
| 399 | len: ssid.len() as _, | ||
| 400 | ssid: [0; 32], | ||
| 401 | }; | ||
| 402 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); | ||
| 403 | |||
| 404 | let mut subscriber = self.event_sub.subscriber().unwrap(); | ||
| 405 | self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; // set_ssid | ||
| 406 | |||
| 407 | loop { | ||
| 408 | let msg = subscriber.next_message_pure().await; | ||
| 409 | if msg.event_type == Event::AUTH && msg.status != 0 { | ||
| 410 | // retry | ||
| 411 | defmt::warn!("JOIN failed with status={}", msg.status); | ||
| 412 | self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; | ||
| 413 | } else if msg.event_type == Event::JOIN && msg.status == 0 { | ||
| 414 | // successful join | ||
| 415 | break; | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | info!("JOINED"); | ||
| 420 | } | ||
| 421 | |||
| 422 | pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { | ||
| 423 | assert!(gpio_n < 3); | ||
| 424 | self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) | ||
| 425 | .await | ||
| 426 | } | ||
| 427 | |||
| 428 | async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { | ||
| 429 | let mut buf = [0; 8]; | ||
| 430 | buf[0..4].copy_from_slice(&val1.to_le_bytes()); | ||
| 431 | buf[4..8].copy_from_slice(&val2.to_le_bytes()); | ||
| 432 | self.set_iovar(name, &buf).await | ||
| 433 | } | ||
| 434 | |||
| 435 | async fn set_iovar_u32(&mut self, name: &str, val: u32) { | ||
| 436 | self.set_iovar(name, &val.to_le_bytes()).await | ||
| 437 | } | ||
| 438 | |||
| 439 | async fn get_iovar_u32(&mut self, name: &str) -> u32 { | ||
| 440 | let mut buf = [0; 4]; | ||
| 441 | let len = self.get_iovar(name, &mut buf).await; | ||
| 442 | assert_eq!(len, 4); | ||
| 443 | u32::from_le_bytes(buf) | ||
| 444 | } | ||
| 445 | |||
| 446 | async fn set_iovar(&mut self, name: &str, val: &[u8]) { | ||
| 447 | info!("set {} = {:02x}", name, val); | ||
| 448 | |||
| 449 | let mut buf = [0; 64]; | ||
| 450 | buf[..name.len()].copy_from_slice(name.as_bytes()); | ||
| 451 | buf[name.len()] = 0; | ||
| 452 | buf[name.len() + 1..][..val.len()].copy_from_slice(val); | ||
| 453 | |||
| 454 | let total_len = name.len() + 1 + val.len(); | ||
| 455 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) | ||
| 456 | .await; | ||
| 457 | } | ||
| 458 | |||
| 459 | // TODO this is not really working, it always returns all zeros. | ||
| 460 | async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { | ||
| 461 | info!("get {}", name); | ||
| 462 | |||
| 463 | let mut buf = [0; 64]; | ||
| 464 | buf[..name.len()].copy_from_slice(name.as_bytes()); | ||
| 465 | buf[name.len()] = 0; | ||
| 466 | |||
| 467 | let total_len = max(name.len() + 1, res.len()); | ||
| 468 | let res_len = self | ||
| 469 | .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) | ||
| 470 | .await; | ||
| 471 | |||
| 472 | let out_len = min(res.len(), res_len); | ||
| 473 | res[..out_len].copy_from_slice(&buf[..out_len]); | ||
| 474 | out_len | ||
| 475 | } | ||
| 476 | |||
| 477 | async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { | ||
| 478 | let mut buf = val.to_le_bytes(); | ||
| 479 | self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; | ||
| 480 | } | ||
| 481 | |||
| 482 | async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { | ||
| 483 | // TODO cancel ioctl on future drop. | ||
| 484 | |||
| 485 | while !matches!(self.ioctl_state.get(), IoctlState::Idle) { | ||
| 486 | yield_now().await; | ||
| 487 | } | ||
| 488 | |||
| 489 | self.ioctl_state.set(IoctlState::Pending { kind, cmd, iface, buf }); | ||
| 490 | |||
| 491 | let resp_len = loop { | ||
| 492 | if let IoctlState::Done { resp_len } = self.ioctl_state.get() { | ||
| 493 | break resp_len; | ||
| 494 | } | ||
| 495 | yield_now().await; | ||
| 496 | }; | ||
| 497 | |||
| 498 | self.ioctl_state.set(IoctlState::Idle); | ||
| 499 | |||
| 500 | resp_len | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | pub struct Runner<'a, PWR, SPI> { | ||
| 505 | ch: ch::Runner<'a, MTU>, | ||
| 506 | bus: Bus<PWR, SPI>, | ||
| 507 | |||
| 508 | ioctl_state: &'a Cell<IoctlState>, | ||
| 509 | ioctl_id: u16, | ||
| 510 | sdpcm_seq: u8, | ||
| 511 | sdpcm_seq_max: u8, | ||
| 512 | |||
| 513 | events: &'a EventQueue, | ||
| 514 | |||
| 515 | #[cfg(feature = "firmware-logs")] | ||
| 516 | log: LogState, | ||
| 517 | } | ||
| 518 | |||
| 519 | #[cfg(feature = "firmware-logs")] | ||
| 520 | struct LogState { | ||
| 521 | addr: u32, | ||
| 522 | last_idx: usize, | ||
| 523 | buf: [u8; 256], | ||
| 524 | buf_count: usize, | ||
| 525 | } | ||
| 526 | |||
| 527 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | 227 | pub type NetDriver<'a> = ch::Device<'a, MTU>; |
| 528 | 228 | ||
| 529 | pub async fn new<'a, PWR, SPI>( | 229 | pub async fn new<'a, PWR, SPI>( |
| @@ -539,585 +239,13 @@ where | |||
| 539 | let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); | 239 | let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); |
| 540 | let state_ch = ch_runner.state_runner(); | 240 | let state_ch = ch_runner.state_runner(); |
| 541 | 241 | ||
| 542 | let mut runner = Runner { | 242 | let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); |
| 543 | ch: ch_runner, | ||
| 544 | bus: Bus::new(pwr, spi), | ||
| 545 | |||
| 546 | ioctl_state: &state.ioctl_state, | ||
| 547 | ioctl_id: 0, | ||
| 548 | sdpcm_seq: 0, | ||
| 549 | sdpcm_seq_max: 1, | ||
| 550 | |||
| 551 | events: &state.events, | ||
| 552 | |||
| 553 | #[cfg(feature = "firmware-logs")] | ||
| 554 | log: LogState { | ||
| 555 | addr: 0, | ||
| 556 | last_idx: 0, | ||
| 557 | buf: [0; 256], | ||
| 558 | buf_count: 0, | ||
| 559 | }, | ||
| 560 | }; | ||
| 561 | 243 | ||
| 562 | runner.init(firmware).await; | 244 | runner.init(firmware).await; |
| 563 | 245 | ||
| 564 | ( | 246 | ( |
| 565 | device, | 247 | device, |
| 566 | Control { | 248 | Control::new(state_ch, &state.events, &state.ioctl_state), |
| 567 | state_ch, | ||
| 568 | event_sub: &&state.events, | ||
| 569 | ioctl_state: &state.ioctl_state, | ||
| 570 | }, | ||
| 571 | runner, | 249 | runner, |
| 572 | ) | 250 | ) |
| 573 | } | 251 | } |
| 574 | |||
| 575 | impl<'a, PWR, SPI> Runner<'a, PWR, SPI> | ||
| 576 | where | ||
| 577 | PWR: OutputPin, | ||
| 578 | SPI: SpiBusCyw43, | ||
| 579 | { | ||
| 580 | async fn init(&mut self, firmware: &[u8]) { | ||
| 581 | self.bus.init().await; | ||
| 582 | |||
| 583 | // Init ALP (Active Low Power) clock | ||
| 584 | self.bus | ||
| 585 | .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) | ||
| 586 | .await; | ||
| 587 | info!("waiting for clock..."); | ||
| 588 | while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} | ||
| 589 | info!("clock ok"); | ||
| 590 | |||
| 591 | let chip_id = self.bus.bp_read16(0x1800_0000).await; | ||
| 592 | info!("chip ID: {}", chip_id); | ||
| 593 | |||
| 594 | // Upload firmware. | ||
| 595 | self.core_disable(Core::WLAN).await; | ||
| 596 | self.core_reset(Core::SOCSRAM).await; | ||
| 597 | self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; | ||
| 598 | self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; | ||
| 599 | |||
| 600 | let ram_addr = CHIP.atcm_ram_base_address; | ||
| 601 | |||
| 602 | info!("loading fw"); | ||
| 603 | self.bus.bp_write(ram_addr, firmware).await; | ||
| 604 | |||
| 605 | info!("loading nvram"); | ||
| 606 | // Round up to 4 bytes. | ||
| 607 | let nvram_len = (NVRAM.len() + 3) / 4 * 4; | ||
| 608 | self.bus | ||
| 609 | .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) | ||
| 610 | .await; | ||
| 611 | |||
| 612 | let nvram_len_words = nvram_len as u32 / 4; | ||
| 613 | let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; | ||
| 614 | self.bus | ||
| 615 | .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) | ||
| 616 | .await; | ||
| 617 | |||
| 618 | // Start core! | ||
| 619 | info!("starting up core..."); | ||
| 620 | self.core_reset(Core::WLAN).await; | ||
| 621 | assert!(self.core_is_up(Core::WLAN).await); | ||
| 622 | |||
| 623 | while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} | ||
| 624 | |||
| 625 | // "Set up the interrupt mask and enable interrupts" | ||
| 626 | self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; | ||
| 627 | |||
| 628 | // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." | ||
| 629 | // Sounds scary... | ||
| 630 | self.bus | ||
| 631 | .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32) | ||
| 632 | .await; | ||
| 633 | |||
| 634 | // wait for wifi startup | ||
| 635 | info!("waiting for wifi init..."); | ||
| 636 | while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} | ||
| 637 | |||
| 638 | // Some random configs related to sleep. | ||
| 639 | // These aren't needed if we don't want to sleep the bus. | ||
| 640 | // TODO do we need to sleep the bus to read the irq line, due to | ||
| 641 | // being on the same pin as MOSI/MISO? | ||
| 642 | |||
| 643 | /* | ||
| 644 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; | ||
| 645 | val |= 0x02; // WAKE_TILL_HT_AVAIL | ||
| 646 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; | ||
| 647 | self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 | ||
| 648 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT | ||
| 649 | |||
| 650 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; | ||
| 651 | val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON | ||
| 652 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; | ||
| 653 | */ | ||
| 654 | |||
| 655 | // clear pulls | ||
| 656 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; | ||
| 657 | let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; | ||
| 658 | |||
| 659 | // start HT clock | ||
| 660 | //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; | ||
| 661 | //info!("waiting for HT clock..."); | ||
| 662 | //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} | ||
| 663 | //info!("clock ok"); | ||
| 664 | |||
| 665 | #[cfg(feature = "firmware-logs")] | ||
| 666 | self.log_init().await; | ||
| 667 | |||
| 668 | info!("init done "); | ||
| 669 | } | ||
| 670 | |||
| 671 | #[cfg(feature = "firmware-logs")] | ||
| 672 | async fn log_init(&mut self) { | ||
| 673 | // Initialize shared memory for logging. | ||
| 674 | |||
| 675 | let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; | ||
| 676 | let shared_addr = self.bus.bp_read32(addr).await; | ||
| 677 | info!("shared_addr {:08x}", shared_addr); | ||
| 678 | |||
| 679 | let mut shared = [0; SharedMemData::SIZE]; | ||
| 680 | self.bus.bp_read(shared_addr, &mut shared).await; | ||
| 681 | let shared = SharedMemData::from_bytes(&shared); | ||
| 682 | info!("shared: {:08x}", shared); | ||
| 683 | |||
| 684 | self.log.addr = shared.console_addr + 8; | ||
| 685 | } | ||
| 686 | |||
| 687 | #[cfg(feature = "firmware-logs")] | ||
| 688 | async fn log_read(&mut self) { | ||
| 689 | // Read log struct | ||
| 690 | let mut log = [0; SharedMemLog::SIZE]; | ||
| 691 | self.bus.bp_read(self.log.addr, &mut log).await; | ||
| 692 | let log = SharedMemLog::from_bytes(&log); | ||
| 693 | |||
| 694 | let idx = log.idx as usize; | ||
| 695 | |||
| 696 | // If pointer hasn't moved, no need to do anything. | ||
| 697 | if idx == self.log.last_idx { | ||
| 698 | return; | ||
| 699 | } | ||
| 700 | |||
| 701 | // Read entire buf for now. We could read only what we need, but then we | ||
| 702 | // run into annoying alignment issues in `bp_read`. | ||
| 703 | let mut buf = [0; 0x400]; | ||
| 704 | self.bus.bp_read(log.buf, &mut buf).await; | ||
| 705 | |||
| 706 | while self.log.last_idx != idx as usize { | ||
| 707 | let b = buf[self.log.last_idx]; | ||
| 708 | if b == b'\r' || b == b'\n' { | ||
| 709 | if self.log.buf_count != 0 { | ||
| 710 | let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; | ||
| 711 | debug!("LOGS: {}", s); | ||
| 712 | self.log.buf_count = 0; | ||
| 713 | } | ||
| 714 | } else if self.log.buf_count < self.log.buf.len() { | ||
| 715 | self.log.buf[self.log.buf_count] = b; | ||
| 716 | self.log.buf_count += 1; | ||
| 717 | } | ||
| 718 | |||
| 719 | self.log.last_idx += 1; | ||
| 720 | if self.log.last_idx == 0x400 { | ||
| 721 | self.log.last_idx = 0; | ||
| 722 | } | ||
| 723 | } | ||
| 724 | } | ||
| 725 | |||
| 726 | pub async fn run(mut self) -> ! { | ||
| 727 | let mut buf = [0; 512]; | ||
| 728 | loop { | ||
| 729 | #[cfg(feature = "firmware-logs")] | ||
| 730 | self.log_read().await; | ||
| 731 | |||
| 732 | // Send stuff | ||
| 733 | // TODO flow control not yet complete | ||
| 734 | if !self.has_credit() { | ||
| 735 | warn!("TX stalled"); | ||
| 736 | } else { | ||
| 737 | if let IoctlState::Pending { kind, cmd, iface, buf } = self.ioctl_state.get() { | ||
| 738 | self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; | ||
| 739 | self.ioctl_state.set(IoctlState::Sent { buf }); | ||
| 740 | } | ||
| 741 | if !self.has_credit() { | ||
| 742 | warn!("TX stalled"); | ||
| 743 | } else { | ||
| 744 | if let Some(packet) = self.ch.try_tx_buf() { | ||
| 745 | trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); | ||
| 746 | |||
| 747 | let mut buf = [0; 512]; | ||
| 748 | let buf8 = slice8_mut(&mut buf); | ||
| 749 | |||
| 750 | let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); | ||
| 751 | |||
| 752 | let seq = self.sdpcm_seq; | ||
| 753 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 754 | |||
| 755 | let sdpcm_header = SdpcmHeader { | ||
| 756 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 757 | len_inv: !total_len as u16, | ||
| 758 | sequence: seq, | ||
| 759 | channel_and_flags: CHANNEL_TYPE_DATA, | ||
| 760 | next_length: 0, | ||
| 761 | header_length: SdpcmHeader::SIZE as _, | ||
| 762 | wireless_flow_control: 0, | ||
| 763 | bus_data_credit: 0, | ||
| 764 | reserved: [0, 0], | ||
| 765 | }; | ||
| 766 | |||
| 767 | let bcd_header = BcdHeader { | ||
| 768 | flags: BDC_VERSION << BDC_VERSION_SHIFT, | ||
| 769 | priority: 0, | ||
| 770 | flags2: 0, | ||
| 771 | data_offset: 0, | ||
| 772 | }; | ||
| 773 | trace!("tx {:?}", sdpcm_header); | ||
| 774 | trace!(" {:?}", bcd_header); | ||
| 775 | |||
| 776 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 777 | buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); | ||
| 778 | buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); | ||
| 779 | |||
| 780 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 781 | |||
| 782 | trace!(" {:02x}", &buf8[..total_len.min(48)]); | ||
| 783 | |||
| 784 | self.bus.wlan_write(&buf[..(total_len / 4)]).await; | ||
| 785 | self.ch.tx_done(); | ||
| 786 | } | ||
| 787 | } | ||
| 788 | } | ||
| 789 | |||
| 790 | // Receive stuff | ||
| 791 | let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; | ||
| 792 | |||
| 793 | if irq & IRQ_F2_PACKET_AVAILABLE != 0 { | ||
| 794 | let mut status = 0xFFFF_FFFF; | ||
| 795 | while status == 0xFFFF_FFFF { | ||
| 796 | status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; | ||
| 797 | } | ||
| 798 | |||
| 799 | if status & STATUS_F2_PKT_AVAILABLE != 0 { | ||
| 800 | let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; | ||
| 801 | self.bus.wlan_read(&mut buf, len).await; | ||
| 802 | trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); | ||
| 803 | self.rx(&slice8_mut(&mut buf)[..len as usize]); | ||
| 804 | } | ||
| 805 | } | ||
| 806 | |||
| 807 | // TODO use IRQs | ||
| 808 | yield_now().await; | ||
| 809 | } | ||
| 810 | } | ||
| 811 | |||
| 812 | fn rx(&mut self, packet: &[u8]) { | ||
| 813 | if packet.len() < SdpcmHeader::SIZE { | ||
| 814 | warn!("packet too short, len={}", packet.len()); | ||
| 815 | return; | ||
| 816 | } | ||
| 817 | |||
| 818 | let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); | ||
| 819 | trace!("rx {:?}", sdpcm_header); | ||
| 820 | if sdpcm_header.len != !sdpcm_header.len_inv { | ||
| 821 | warn!("len inv mismatch"); | ||
| 822 | return; | ||
| 823 | } | ||
| 824 | if sdpcm_header.len as usize != packet.len() { | ||
| 825 | // TODO: is this guaranteed?? | ||
| 826 | warn!("len from header doesn't match len from spi"); | ||
| 827 | return; | ||
| 828 | } | ||
| 829 | |||
| 830 | self.update_credit(&sdpcm_header); | ||
| 831 | |||
| 832 | let channel = sdpcm_header.channel_and_flags & 0x0f; | ||
| 833 | |||
| 834 | let payload = &packet[sdpcm_header.header_length as _..]; | ||
| 835 | |||
| 836 | match channel { | ||
| 837 | CHANNEL_TYPE_CONTROL => { | ||
| 838 | if payload.len() < CdcHeader::SIZE { | ||
| 839 | warn!("payload too short, len={}", payload.len()); | ||
| 840 | return; | ||
| 841 | } | ||
| 842 | |||
| 843 | let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); | ||
| 844 | trace!(" {:?}", cdc_header); | ||
| 845 | |||
| 846 | if let IoctlState::Sent { buf } = self.ioctl_state.get() { | ||
| 847 | if cdc_header.id == self.ioctl_id { | ||
| 848 | if cdc_header.status != 0 { | ||
| 849 | // TODO: propagate error instead | ||
| 850 | panic!("IOCTL error {=i32}", cdc_header.status as i32); | ||
| 851 | } | ||
| 852 | |||
| 853 | let resp_len = cdc_header.len as usize; | ||
| 854 | info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); | ||
| 855 | |||
| 856 | (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); | ||
| 857 | self.ioctl_state.set(IoctlState::Done { resp_len }); | ||
| 858 | } | ||
| 859 | } | ||
| 860 | } | ||
| 861 | CHANNEL_TYPE_EVENT => { | ||
| 862 | let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); | ||
| 863 | trace!(" {:?}", bcd_header); | ||
| 864 | |||
| 865 | let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; | ||
| 866 | |||
| 867 | if packet_start + EventPacket::SIZE > payload.len() { | ||
| 868 | warn!("BCD event, incomplete header"); | ||
| 869 | return; | ||
| 870 | } | ||
| 871 | let bcd_packet = &payload[packet_start..]; | ||
| 872 | trace!(" {:02x}", &bcd_packet[..(bcd_packet.len() as usize).min(36)]); | ||
| 873 | |||
| 874 | let mut event_packet = EventPacket::from_bytes(&bcd_packet[..EventPacket::SIZE].try_into().unwrap()); | ||
| 875 | event_packet.byteswap(); | ||
| 876 | |||
| 877 | const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h | ||
| 878 | if event_packet.eth.ether_type != ETH_P_LINK_CTL { | ||
| 879 | warn!( | ||
| 880 | "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", | ||
| 881 | event_packet.eth.ether_type, ETH_P_LINK_CTL | ||
| 882 | ); | ||
| 883 | return; | ||
| 884 | } | ||
| 885 | const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; | ||
| 886 | if event_packet.hdr.oui != BROADCOM_OUI { | ||
| 887 | warn!( | ||
| 888 | "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", | ||
| 889 | event_packet.hdr.oui, BROADCOM_OUI | ||
| 890 | ); | ||
| 891 | return; | ||
| 892 | } | ||
| 893 | const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; | ||
| 894 | if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { | ||
| 895 | warn!("unexpected subtype {}", event_packet.hdr.subtype); | ||
| 896 | return; | ||
| 897 | } | ||
| 898 | |||
| 899 | const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; | ||
| 900 | if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { | ||
| 901 | warn!("unexpected user_subtype {}", event_packet.hdr.subtype); | ||
| 902 | return; | ||
| 903 | } | ||
| 904 | |||
| 905 | if event_packet.msg.datalen as usize >= (bcd_packet.len() - EventMessage::SIZE) { | ||
| 906 | warn!("BCD event, incomplete data"); | ||
| 907 | return; | ||
| 908 | } | ||
| 909 | |||
| 910 | let evt_type = events::Event::from(event_packet.msg.event_type as u8); | ||
| 911 | let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; | ||
| 912 | debug!("=== EVENT {}: {} {:02x}", evt_type, event_packet.msg, evt_data); | ||
| 913 | |||
| 914 | if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { | ||
| 915 | self.events.publish_immediate(EventStatus { | ||
| 916 | status: event_packet.msg.status, | ||
| 917 | event_type: evt_type, | ||
| 918 | }); | ||
| 919 | } | ||
| 920 | } | ||
| 921 | CHANNEL_TYPE_DATA => { | ||
| 922 | let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); | ||
| 923 | trace!(" {:?}", bcd_header); | ||
| 924 | |||
| 925 | let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; | ||
| 926 | if packet_start > payload.len() { | ||
| 927 | warn!("packet start out of range."); | ||
| 928 | return; | ||
| 929 | } | ||
| 930 | let packet = &payload[packet_start..]; | ||
| 931 | trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); | ||
| 932 | |||
| 933 | match self.ch.try_rx_buf() { | ||
| 934 | Some(buf) => { | ||
| 935 | buf[..packet.len()].copy_from_slice(packet); | ||
| 936 | self.ch.rx_done(packet.len()) | ||
| 937 | } | ||
| 938 | None => warn!("failed to push rxd packet to the channel."), | ||
| 939 | } | ||
| 940 | } | ||
| 941 | _ => {} | ||
| 942 | } | ||
| 943 | } | ||
| 944 | |||
| 945 | fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { | ||
| 946 | if sdpcm_header.channel_and_flags & 0xf < 3 { | ||
| 947 | let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; | ||
| 948 | if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { | ||
| 949 | sdpcm_seq_max = self.sdpcm_seq + 2; | ||
| 950 | } | ||
| 951 | self.sdpcm_seq_max = sdpcm_seq_max; | ||
| 952 | } | ||
| 953 | } | ||
| 954 | |||
| 955 | fn has_credit(&self) -> bool { | ||
| 956 | self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 | ||
| 957 | } | ||
| 958 | |||
| 959 | async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { | ||
| 960 | let mut buf = [0; 512]; | ||
| 961 | let buf8 = slice8_mut(&mut buf); | ||
| 962 | |||
| 963 | let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); | ||
| 964 | |||
| 965 | let sdpcm_seq = self.sdpcm_seq; | ||
| 966 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 967 | self.ioctl_id = self.ioctl_id.wrapping_add(1); | ||
| 968 | |||
| 969 | let sdpcm_header = SdpcmHeader { | ||
| 970 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 971 | len_inv: !total_len as u16, | ||
| 972 | sequence: sdpcm_seq, | ||
| 973 | channel_and_flags: CHANNEL_TYPE_CONTROL, | ||
| 974 | next_length: 0, | ||
| 975 | header_length: SdpcmHeader::SIZE as _, | ||
| 976 | wireless_flow_control: 0, | ||
| 977 | bus_data_credit: 0, | ||
| 978 | reserved: [0, 0], | ||
| 979 | }; | ||
| 980 | |||
| 981 | let cdc_header = CdcHeader { | ||
| 982 | cmd: cmd, | ||
| 983 | len: data.len() as _, | ||
| 984 | flags: kind as u16 | (iface as u16) << 12, | ||
| 985 | id: self.ioctl_id, | ||
| 986 | status: 0, | ||
| 987 | }; | ||
| 988 | trace!("tx {:?}", sdpcm_header); | ||
| 989 | trace!(" {:?}", cdc_header); | ||
| 990 | |||
| 991 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 992 | buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); | ||
| 993 | buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); | ||
| 994 | |||
| 995 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 996 | |||
| 997 | trace!(" {:02x}", &buf8[..total_len.min(48)]); | ||
| 998 | |||
| 999 | self.bus.wlan_write(&buf[..total_len / 4]).await; | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | async fn core_disable(&mut self, core: Core) { | ||
| 1003 | let base = core.base_addr(); | ||
| 1004 | |||
| 1005 | // Dummy read? | ||
| 1006 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 1007 | |||
| 1008 | // Check it isn't already reset | ||
| 1009 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 1010 | if r & AI_RESETCTRL_BIT_RESET != 0 { | ||
| 1011 | return; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; | ||
| 1015 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 1016 | |||
| 1017 | block_for(Duration::from_millis(1)); | ||
| 1018 | |||
| 1019 | self.bus | ||
| 1020 | .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) | ||
| 1021 | .await; | ||
| 1022 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | async fn core_reset(&mut self, core: Core) { | ||
| 1026 | self.core_disable(core).await; | ||
| 1027 | |||
| 1028 | let base = core.base_addr(); | ||
| 1029 | self.bus | ||
| 1030 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) | ||
| 1031 | .await; | ||
| 1032 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 1033 | |||
| 1034 | self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; | ||
| 1035 | |||
| 1036 | Timer::after(Duration::from_millis(1)).await; | ||
| 1037 | |||
| 1038 | self.bus | ||
| 1039 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) | ||
| 1040 | .await; | ||
| 1041 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 1042 | |||
| 1043 | Timer::after(Duration::from_millis(1)).await; | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | async fn core_is_up(&mut self, core: Core) -> bool { | ||
| 1047 | let base = core.base_addr(); | ||
| 1048 | |||
| 1049 | let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 1050 | if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { | ||
| 1051 | debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); | ||
| 1052 | return false; | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 1056 | if r & (AI_RESETCTRL_BIT_RESET) != 0 { | ||
| 1057 | debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); | ||
| 1058 | return false; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | true | ||
| 1062 | } | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | fn slice8_mut(x: &mut [u32]) -> &mut [u8] { | ||
| 1066 | let len = x.len() * 4; | ||
| 1067 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | macro_rules! nvram { | ||
| 1071 | ($($s:literal,)*) => { | ||
| 1072 | concat_bytes!($($s, b"\x00",)* b"\x00\x00") | ||
| 1073 | }; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | static NVRAM: &'static [u8] = &*nvram!( | ||
| 1077 | b"NVRAMRev=$Rev$", | ||
| 1078 | b"manfid=0x2d0", | ||
| 1079 | b"prodid=0x0727", | ||
| 1080 | b"vendid=0x14e4", | ||
| 1081 | b"devid=0x43e2", | ||
| 1082 | b"boardtype=0x0887", | ||
| 1083 | b"boardrev=0x1100", | ||
| 1084 | b"boardnum=22", | ||
| 1085 | b"macaddr=00:A0:50:b5:59:5e", | ||
| 1086 | b"sromrev=11", | ||
| 1087 | b"boardflags=0x00404001", | ||
| 1088 | b"boardflags3=0x04000000", | ||
| 1089 | b"xtalfreq=37400", | ||
| 1090 | b"nocrc=1", | ||
| 1091 | b"ag0=255", | ||
| 1092 | b"aa2g=1", | ||
| 1093 | b"ccode=ALL", | ||
| 1094 | b"pa0itssit=0x20", | ||
| 1095 | b"extpagain2g=0", | ||
| 1096 | b"pa2ga0=-168,6649,-778", | ||
| 1097 | b"AvVmid_c0=0x0,0xc8", | ||
| 1098 | b"cckpwroffset0=5", | ||
| 1099 | b"maxp2ga0=84", | ||
| 1100 | b"txpwrbckof=6", | ||
| 1101 | b"cckbw202gpo=0", | ||
| 1102 | b"legofdmbw202gpo=0x66111111", | ||
| 1103 | b"mcsbw202gpo=0x77711111", | ||
| 1104 | b"propbw202gpo=0xdd", | ||
| 1105 | b"ofdmdigfilttype=18", | ||
| 1106 | b"ofdmdigfilttypebe=18", | ||
| 1107 | b"papdmode=1", | ||
| 1108 | b"papdvalidtest=1", | ||
| 1109 | b"pacalidx2g=45", | ||
| 1110 | b"papdepsoffset=-30", | ||
| 1111 | b"papdendidx=58", | ||
| 1112 | b"ltecxmux=0", | ||
| 1113 | b"ltecxpadnum=0x0102", | ||
| 1114 | b"ltecxfnsel=0x44", | ||
| 1115 | b"ltecxgcigpio=0x01", | ||
| 1116 | b"il0macaddr=00:90:4c:c5:12:38", | ||
| 1117 | b"wl0id=0x431b", | ||
| 1118 | b"deadman_to=0xffffffff", | ||
| 1119 | b"muxenab=0x100", | ||
| 1120 | b"spurconfig=0x3", | ||
| 1121 | b"glitch_based_crsmin=1", | ||
| 1122 | b"btc_mode=1", | ||
| 1123 | ); | ||
diff --git a/src/nvram.rs b/src/nvram.rs new file mode 100644 index 000000000..964a3128d --- /dev/null +++ b/src/nvram.rs | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | macro_rules! nvram { | ||
| 2 | ($($s:literal,)*) => { | ||
| 3 | concat_bytes!($($s, b"\x00",)* b"\x00\x00") | ||
| 4 | }; | ||
| 5 | } | ||
| 6 | |||
| 7 | pub static NVRAM: &'static [u8] = &*nvram!( | ||
| 8 | b"NVRAMRev=$Rev$", | ||
| 9 | b"manfid=0x2d0", | ||
| 10 | b"prodid=0x0727", | ||
| 11 | b"vendid=0x14e4", | ||
| 12 | b"devid=0x43e2", | ||
| 13 | b"boardtype=0x0887", | ||
| 14 | b"boardrev=0x1100", | ||
| 15 | b"boardnum=22", | ||
| 16 | b"macaddr=00:A0:50:b5:59:5e", | ||
| 17 | b"sromrev=11", | ||
| 18 | b"boardflags=0x00404001", | ||
| 19 | b"boardflags3=0x04000000", | ||
| 20 | b"xtalfreq=37400", | ||
| 21 | b"nocrc=1", | ||
| 22 | b"ag0=255", | ||
| 23 | b"aa2g=1", | ||
| 24 | b"ccode=ALL", | ||
| 25 | b"pa0itssit=0x20", | ||
| 26 | b"extpagain2g=0", | ||
| 27 | b"pa2ga0=-168,6649,-778", | ||
| 28 | b"AvVmid_c0=0x0,0xc8", | ||
| 29 | b"cckpwroffset0=5", | ||
| 30 | b"maxp2ga0=84", | ||
| 31 | b"txpwrbckof=6", | ||
| 32 | b"cckbw202gpo=0", | ||
| 33 | b"legofdmbw202gpo=0x66111111", | ||
| 34 | b"mcsbw202gpo=0x77711111", | ||
| 35 | b"propbw202gpo=0xdd", | ||
| 36 | b"ofdmdigfilttype=18", | ||
| 37 | b"ofdmdigfilttypebe=18", | ||
| 38 | b"papdmode=1", | ||
| 39 | b"papdvalidtest=1", | ||
| 40 | b"pacalidx2g=45", | ||
| 41 | b"papdepsoffset=-30", | ||
| 42 | b"papdendidx=58", | ||
| 43 | b"ltecxmux=0", | ||
| 44 | b"ltecxpadnum=0x0102", | ||
| 45 | b"ltecxfnsel=0x44", | ||
| 46 | b"ltecxgcigpio=0x01", | ||
| 47 | b"il0macaddr=00:90:4c:c5:12:38", | ||
| 48 | b"wl0id=0x431b", | ||
| 49 | b"deadman_to=0xffffffff", | ||
| 50 | b"muxenab=0x100", | ||
| 51 | b"spurconfig=0x3", | ||
| 52 | b"glitch_based_crsmin=1", | ||
| 53 | b"btc_mode=1", | ||
| 54 | ); | ||
diff --git a/src/runner.rs b/src/runner.rs new file mode 100644 index 000000000..5d840bc59 --- /dev/null +++ b/src/runner.rs | |||
| @@ -0,0 +1,564 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::slice; | ||
| 3 | |||
| 4 | use embassy_futures::yield_now; | ||
| 5 | use embassy_net_driver_channel as ch; | ||
| 6 | use embassy_sync::pubsub::PubSubBehavior; | ||
| 7 | use embassy_time::{block_for, Duration, Timer}; | ||
| 8 | use embedded_hal_1::digital::OutputPin; | ||
| 9 | |||
| 10 | use crate::bus::Bus; | ||
| 11 | pub use crate::bus::SpiBusCyw43; | ||
| 12 | use crate::consts::*; | ||
| 13 | use crate::events::{EventQueue, EventStatus}; | ||
| 14 | use crate::nvram::NVRAM; | ||
| 15 | use crate::structs::*; | ||
| 16 | use crate::{events, Core, IoctlState, IoctlType, CHIP, MTU}; | ||
| 17 | |||
| 18 | #[cfg(feature = "firmware-logs")] | ||
| 19 | struct LogState { | ||
| 20 | addr: u32, | ||
| 21 | last_idx: usize, | ||
| 22 | buf: [u8; 256], | ||
| 23 | buf_count: usize, | ||
| 24 | } | ||
| 25 | |||
| 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 Cell<IoctlState>, | ||
| 42 | ioctl_id: u16, | ||
| 43 | sdpcm_seq: u8, | ||
| 44 | sdpcm_seq_max: u8, | ||
| 45 | |||
| 46 | events: &'a EventQueue, | ||
| 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 Cell<IoctlState>, | ||
| 61 | events: &'a EventQueue, | ||
| 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 | info!("waiting for clock..."); | ||
| 84 | while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} | ||
| 85 | info!("clock ok"); | ||
| 86 | |||
| 87 | let chip_id = self.bus.bp_read16(0x1800_0000).await; | ||
| 88 | info!("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 | info!("loading fw"); | ||
| 99 | self.bus.bp_write(ram_addr, firmware).await; | ||
| 100 | |||
| 101 | info!("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 | info!("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 | // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." | ||
| 125 | // Sounds scary... | ||
| 126 | self.bus | ||
| 127 | .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32) | ||
| 128 | .await; | ||
| 129 | |||
| 130 | // wait for wifi startup | ||
| 131 | info!("waiting for wifi init..."); | ||
| 132 | while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} | ||
| 133 | |||
| 134 | // Some random configs related to sleep. | ||
| 135 | // These aren't needed if we don't want to sleep the bus. | ||
| 136 | // TODO do we need to sleep the bus to read the irq line, due to | ||
| 137 | // being on the same pin as MOSI/MISO? | ||
| 138 | |||
| 139 | /* | ||
| 140 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; | ||
| 141 | val |= 0x02; // WAKE_TILL_HT_AVAIL | ||
| 142 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; | ||
| 143 | self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 | ||
| 144 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT | ||
| 145 | |||
| 146 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; | ||
| 147 | val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON | ||
| 148 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; | ||
| 149 | */ | ||
| 150 | |||
| 151 | // clear pulls | ||
| 152 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; | ||
| 153 | let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; | ||
| 154 | |||
| 155 | // start HT clock | ||
| 156 | //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; | ||
| 157 | //info!("waiting for HT clock..."); | ||
| 158 | //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} | ||
| 159 | //info!("clock ok"); | ||
| 160 | |||
| 161 | #[cfg(feature = "firmware-logs")] | ||
| 162 | self.log_init().await; | ||
| 163 | |||
| 164 | info!("init done "); | ||
| 165 | } | ||
| 166 | |||
| 167 | #[cfg(feature = "firmware-logs")] | ||
| 168 | async fn log_init(&mut self) { | ||
| 169 | // Initialize shared memory for logging. | ||
| 170 | |||
| 171 | let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; | ||
| 172 | let shared_addr = self.bus.bp_read32(addr).await; | ||
| 173 | info!("shared_addr {:08x}", shared_addr); | ||
| 174 | |||
| 175 | let mut shared = [0; SharedMemData::SIZE]; | ||
| 176 | self.bus.bp_read(shared_addr, &mut shared).await; | ||
| 177 | let shared = SharedMemData::from_bytes(&shared); | ||
| 178 | info!("shared: {:08x}", shared); | ||
| 179 | |||
| 180 | self.log.addr = shared.console_addr + 8; | ||
| 181 | } | ||
| 182 | |||
| 183 | #[cfg(feature = "firmware-logs")] | ||
| 184 | async fn log_read(&mut self) { | ||
| 185 | // Read log struct | ||
| 186 | let mut log = [0; SharedMemLog::SIZE]; | ||
| 187 | self.bus.bp_read(self.log.addr, &mut log).await; | ||
| 188 | let log = SharedMemLog::from_bytes(&log); | ||
| 189 | |||
| 190 | let idx = log.idx as usize; | ||
| 191 | |||
| 192 | // If pointer hasn't moved, no need to do anything. | ||
| 193 | if idx == self.log.last_idx { | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | |||
| 197 | // Read entire buf for now. We could read only what we need, but then we | ||
| 198 | // run into annoying alignment issues in `bp_read`. | ||
| 199 | let mut buf = [0; 0x400]; | ||
| 200 | self.bus.bp_read(log.buf, &mut buf).await; | ||
| 201 | |||
| 202 | while self.log.last_idx != idx as usize { | ||
| 203 | let b = buf[self.log.last_idx]; | ||
| 204 | if b == b'\r' || b == b'\n' { | ||
| 205 | if self.log.buf_count != 0 { | ||
| 206 | let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; | ||
| 207 | debug!("LOGS: {}", s); | ||
| 208 | self.log.buf_count = 0; | ||
| 209 | } | ||
| 210 | } else if self.log.buf_count < self.log.buf.len() { | ||
| 211 | self.log.buf[self.log.buf_count] = b; | ||
| 212 | self.log.buf_count += 1; | ||
| 213 | } | ||
| 214 | |||
| 215 | self.log.last_idx += 1; | ||
| 216 | if self.log.last_idx == 0x400 { | ||
| 217 | self.log.last_idx = 0; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | pub async fn run(mut self) -> ! { | ||
| 223 | let mut buf = [0; 512]; | ||
| 224 | loop { | ||
| 225 | #[cfg(feature = "firmware-logs")] | ||
| 226 | self.log_read().await; | ||
| 227 | |||
| 228 | // Send stuff | ||
| 229 | // TODO flow control not yet complete | ||
| 230 | if !self.has_credit() { | ||
| 231 | warn!("TX stalled"); | ||
| 232 | } else { | ||
| 233 | if let IoctlState::Pending { kind, cmd, iface, buf } = self.ioctl_state.get() { | ||
| 234 | self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; | ||
| 235 | self.ioctl_state.set(IoctlState::Sent { buf }); | ||
| 236 | } | ||
| 237 | if !self.has_credit() { | ||
| 238 | warn!("TX stalled"); | ||
| 239 | } else { | ||
| 240 | if let Some(packet) = self.ch.try_tx_buf() { | ||
| 241 | trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); | ||
| 242 | |||
| 243 | let mut buf = [0; 512]; | ||
| 244 | let buf8 = slice8_mut(&mut buf); | ||
| 245 | |||
| 246 | let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); | ||
| 247 | |||
| 248 | let seq = self.sdpcm_seq; | ||
| 249 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 250 | |||
| 251 | let sdpcm_header = SdpcmHeader { | ||
| 252 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 253 | len_inv: !total_len as u16, | ||
| 254 | sequence: seq, | ||
| 255 | channel_and_flags: CHANNEL_TYPE_DATA, | ||
| 256 | next_length: 0, | ||
| 257 | header_length: SdpcmHeader::SIZE as _, | ||
| 258 | wireless_flow_control: 0, | ||
| 259 | bus_data_credit: 0, | ||
| 260 | reserved: [0, 0], | ||
| 261 | }; | ||
| 262 | |||
| 263 | let bcd_header = BcdHeader { | ||
| 264 | flags: BDC_VERSION << BDC_VERSION_SHIFT, | ||
| 265 | priority: 0, | ||
| 266 | flags2: 0, | ||
| 267 | data_offset: 0, | ||
| 268 | }; | ||
| 269 | trace!("tx {:?}", sdpcm_header); | ||
| 270 | trace!(" {:?}", bcd_header); | ||
| 271 | |||
| 272 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 273 | buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); | ||
| 274 | buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); | ||
| 275 | |||
| 276 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 277 | |||
| 278 | trace!(" {:02x}", &buf8[..total_len.min(48)]); | ||
| 279 | |||
| 280 | self.bus.wlan_write(&buf[..(total_len / 4)]).await; | ||
| 281 | self.ch.tx_done(); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | // Receive stuff | ||
| 287 | let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; | ||
| 288 | |||
| 289 | if irq & IRQ_F2_PACKET_AVAILABLE != 0 { | ||
| 290 | let mut status = 0xFFFF_FFFF; | ||
| 291 | while status == 0xFFFF_FFFF { | ||
| 292 | status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; | ||
| 293 | } | ||
| 294 | |||
| 295 | if status & STATUS_F2_PKT_AVAILABLE != 0 { | ||
| 296 | let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; | ||
| 297 | self.bus.wlan_read(&mut buf, len).await; | ||
| 298 | trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); | ||
| 299 | self.rx(&slice8_mut(&mut buf)[..len as usize]); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | // TODO use IRQs | ||
| 304 | yield_now().await; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | fn rx(&mut self, packet: &[u8]) { | ||
| 309 | if packet.len() < SdpcmHeader::SIZE { | ||
| 310 | warn!("packet too short, len={}", packet.len()); | ||
| 311 | return; | ||
| 312 | } | ||
| 313 | |||
| 314 | let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); | ||
| 315 | trace!("rx {:?}", sdpcm_header); | ||
| 316 | if sdpcm_header.len != !sdpcm_header.len_inv { | ||
| 317 | warn!("len inv mismatch"); | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | if sdpcm_header.len as usize != packet.len() { | ||
| 321 | // TODO: is this guaranteed?? | ||
| 322 | warn!("len from header doesn't match len from spi"); | ||
| 323 | return; | ||
| 324 | } | ||
| 325 | |||
| 326 | self.update_credit(&sdpcm_header); | ||
| 327 | |||
| 328 | let channel = sdpcm_header.channel_and_flags & 0x0f; | ||
| 329 | |||
| 330 | let payload = &packet[sdpcm_header.header_length as _..]; | ||
| 331 | |||
| 332 | match channel { | ||
| 333 | CHANNEL_TYPE_CONTROL => { | ||
| 334 | if payload.len() < CdcHeader::SIZE { | ||
| 335 | warn!("payload too short, len={}", payload.len()); | ||
| 336 | return; | ||
| 337 | } | ||
| 338 | |||
| 339 | let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); | ||
| 340 | trace!(" {:?}", cdc_header); | ||
| 341 | |||
| 342 | if let IoctlState::Sent { buf } = self.ioctl_state.get() { | ||
| 343 | if cdc_header.id == self.ioctl_id { | ||
| 344 | if cdc_header.status != 0 { | ||
| 345 | // TODO: propagate error instead | ||
| 346 | panic!("IOCTL error {=i32}", cdc_header.status as i32); | ||
| 347 | } | ||
| 348 | |||
| 349 | let resp_len = cdc_header.len as usize; | ||
| 350 | info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); | ||
| 351 | |||
| 352 | (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); | ||
| 353 | self.ioctl_state.set(IoctlState::Done { resp_len }); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
| 357 | CHANNEL_TYPE_EVENT => { | ||
| 358 | let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); | ||
| 359 | trace!(" {:?}", bcd_header); | ||
| 360 | |||
| 361 | let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; | ||
| 362 | |||
| 363 | if packet_start + EventPacket::SIZE > payload.len() { | ||
| 364 | warn!("BCD event, incomplete header"); | ||
| 365 | return; | ||
| 366 | } | ||
| 367 | let bcd_packet = &payload[packet_start..]; | ||
| 368 | trace!(" {:02x}", &bcd_packet[..(bcd_packet.len() as usize).min(36)]); | ||
| 369 | |||
| 370 | let mut event_packet = EventPacket::from_bytes(&bcd_packet[..EventPacket::SIZE].try_into().unwrap()); | ||
| 371 | event_packet.byteswap(); | ||
| 372 | |||
| 373 | const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h | ||
| 374 | if event_packet.eth.ether_type != ETH_P_LINK_CTL { | ||
| 375 | warn!( | ||
| 376 | "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", | ||
| 377 | event_packet.eth.ether_type, ETH_P_LINK_CTL | ||
| 378 | ); | ||
| 379 | return; | ||
| 380 | } | ||
| 381 | const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; | ||
| 382 | if event_packet.hdr.oui != BROADCOM_OUI { | ||
| 383 | warn!( | ||
| 384 | "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", | ||
| 385 | event_packet.hdr.oui, BROADCOM_OUI | ||
| 386 | ); | ||
| 387 | return; | ||
| 388 | } | ||
| 389 | const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; | ||
| 390 | if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { | ||
| 391 | warn!("unexpected subtype {}", event_packet.hdr.subtype); | ||
| 392 | return; | ||
| 393 | } | ||
| 394 | |||
| 395 | const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; | ||
| 396 | if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { | ||
| 397 | warn!("unexpected user_subtype {}", event_packet.hdr.subtype); | ||
| 398 | return; | ||
| 399 | } | ||
| 400 | |||
| 401 | if event_packet.msg.datalen as usize >= (bcd_packet.len() - EventMessage::SIZE) { | ||
| 402 | warn!("BCD event, incomplete data"); | ||
| 403 | return; | ||
| 404 | } | ||
| 405 | |||
| 406 | let evt_type = events::Event::from(event_packet.msg.event_type as u8); | ||
| 407 | let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; | ||
| 408 | debug!("=== EVENT {}: {} {:02x}", evt_type, event_packet.msg, evt_data); | ||
| 409 | |||
| 410 | if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { | ||
| 411 | self.events.publish_immediate(EventStatus { | ||
| 412 | status: event_packet.msg.status, | ||
| 413 | event_type: evt_type, | ||
| 414 | }); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | CHANNEL_TYPE_DATA => { | ||
| 418 | let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); | ||
| 419 | trace!(" {:?}", bcd_header); | ||
| 420 | |||
| 421 | let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; | ||
| 422 | if packet_start > payload.len() { | ||
| 423 | warn!("packet start out of range."); | ||
| 424 | return; | ||
| 425 | } | ||
| 426 | let packet = &payload[packet_start..]; | ||
| 427 | trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); | ||
| 428 | |||
| 429 | match self.ch.try_rx_buf() { | ||
| 430 | Some(buf) => { | ||
| 431 | buf[..packet.len()].copy_from_slice(packet); | ||
| 432 | self.ch.rx_done(packet.len()) | ||
| 433 | } | ||
| 434 | None => warn!("failed to push rxd packet to the channel."), | ||
| 435 | } | ||
| 436 | } | ||
| 437 | _ => {} | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { | ||
| 442 | if sdpcm_header.channel_and_flags & 0xf < 3 { | ||
| 443 | let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; | ||
| 444 | if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { | ||
| 445 | sdpcm_seq_max = self.sdpcm_seq + 2; | ||
| 446 | } | ||
| 447 | self.sdpcm_seq_max = sdpcm_seq_max; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | fn has_credit(&self) -> bool { | ||
| 452 | self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 | ||
| 453 | } | ||
| 454 | |||
| 455 | async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { | ||
| 456 | let mut buf = [0; 512]; | ||
| 457 | let buf8 = slice8_mut(&mut buf); | ||
| 458 | |||
| 459 | let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); | ||
| 460 | |||
| 461 | let sdpcm_seq = self.sdpcm_seq; | ||
| 462 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 463 | self.ioctl_id = self.ioctl_id.wrapping_add(1); | ||
| 464 | |||
| 465 | let sdpcm_header = SdpcmHeader { | ||
| 466 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 467 | len_inv: !total_len as u16, | ||
| 468 | sequence: sdpcm_seq, | ||
| 469 | channel_and_flags: CHANNEL_TYPE_CONTROL, | ||
| 470 | next_length: 0, | ||
| 471 | header_length: SdpcmHeader::SIZE as _, | ||
| 472 | wireless_flow_control: 0, | ||
| 473 | bus_data_credit: 0, | ||
| 474 | reserved: [0, 0], | ||
| 475 | }; | ||
| 476 | |||
| 477 | let cdc_header = CdcHeader { | ||
| 478 | cmd: cmd, | ||
| 479 | len: data.len() as _, | ||
| 480 | flags: kind as u16 | (iface as u16) << 12, | ||
| 481 | id: self.ioctl_id, | ||
| 482 | status: 0, | ||
| 483 | }; | ||
| 484 | trace!("tx {:?}", sdpcm_header); | ||
| 485 | trace!(" {:?}", cdc_header); | ||
| 486 | |||
| 487 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 488 | buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); | ||
| 489 | buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); | ||
| 490 | |||
| 491 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 492 | |||
| 493 | trace!(" {:02x}", &buf8[..total_len.min(48)]); | ||
| 494 | |||
| 495 | self.bus.wlan_write(&buf[..total_len / 4]).await; | ||
| 496 | } | ||
| 497 | |||
| 498 | async fn core_disable(&mut self, core: Core) { | ||
| 499 | let base = core.base_addr(); | ||
| 500 | |||
| 501 | // Dummy read? | ||
| 502 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 503 | |||
| 504 | // Check it isn't already reset | ||
| 505 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 506 | if r & AI_RESETCTRL_BIT_RESET != 0 { | ||
| 507 | return; | ||
| 508 | } | ||
| 509 | |||
| 510 | self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; | ||
| 511 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 512 | |||
| 513 | block_for(Duration::from_millis(1)); | ||
| 514 | |||
| 515 | self.bus | ||
| 516 | .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) | ||
| 517 | .await; | ||
| 518 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 519 | } | ||
| 520 | |||
| 521 | async fn core_reset(&mut self, core: Core) { | ||
| 522 | self.core_disable(core).await; | ||
| 523 | |||
| 524 | let base = core.base_addr(); | ||
| 525 | self.bus | ||
| 526 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) | ||
| 527 | .await; | ||
| 528 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 529 | |||
| 530 | self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; | ||
| 531 | |||
| 532 | Timer::after(Duration::from_millis(1)).await; | ||
| 533 | |||
| 534 | self.bus | ||
| 535 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) | ||
| 536 | .await; | ||
| 537 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 538 | |||
| 539 | Timer::after(Duration::from_millis(1)).await; | ||
| 540 | } | ||
| 541 | |||
| 542 | async fn core_is_up(&mut self, core: Core) -> bool { | ||
| 543 | let base = core.base_addr(); | ||
| 544 | |||
| 545 | let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 546 | if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { | ||
| 547 | debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); | ||
| 548 | return false; | ||
| 549 | } | ||
| 550 | |||
| 551 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 552 | if r & (AI_RESETCTRL_BIT_RESET) != 0 { | ||
| 553 | debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); | ||
| 554 | return false; | ||
| 555 | } | ||
| 556 | |||
| 557 | true | ||
| 558 | } | ||
| 559 | } | ||
| 560 | |||
| 561 | fn slice8_mut(x: &mut [u32]) -> &mut [u8] { | ||
| 562 | let len = x.len() * 4; | ||
| 563 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } | ||
| 564 | } | ||
