1#![allow(dead_code)]
4
5extern crate alloc;
6use crate::kernel::sdio;
7use alloc::vec::Vec;
8use spin::Mutex;
9
10const SDPCM_HDR_LEN: usize = 12; const SDPCM_CHAN_CONTROL: u8 = 0; const SDPCM_CHAN_EVENT: u8 = 1; const SDPCM_CHAN_DATA: u8 = 2; const CDC_HDR_LEN: usize = 16;
18const CDC_FLAG_SET: u32 = 0x0000; const CDC_FLAG_GET: u32 = 0x0002; const CDC_ID_SHIFT: u32 = 16; const BRCMF_C_GET_REVINFO: u32 = 202;
24const BRCMF_C_SET_INFRA: u32 = 20;
25const BRCMF_C_SET_AUTH: u32 = 22;
26const BRCMF_C_SET_WSEC: u32 = 134;
27const BRCMF_C_SET_SSID: u32 = 26;
28const BRCMF_C_DISASSOC: u32 = 52;
29const BRCMF_C_SET_PM: u32 = 86;
30const BRCMF_C_SET_VAR: u32 = 263; const BRCMF_C_GET_VAR: u32 = 262; const BRCMF_C_SET_SCAN_CHANNEL_TIME: u32 = 185;
33const BRCMF_C_SET_SCAN_UNASSOC_TIME: u32 = 187;
34
35const BRCMF_E_SET_SSID: u32 = 0;
37const BRCMF_E_AUTH: u32 = 3;
38const BRCMF_E_DEAUTH: u32 = 5;
39const BRCMF_E_ASSOC: u32 = 7;
40const BRCMF_E_DISASSOC: u32 = 11;
41const BRCMF_E_LINK: u32 = 16;
42const BRCMF_E_PSK_SUP: u32 = 46;
43const BRCMF_E_STATUS_SUCCESS: u32 = 0;
44
45const WSEC_AES: u32 = 4; const WSEC_PASSPHRASE: u16 = 0x0200; const SBSDIO_FUNC1_CHIPCLKCSR: u32 = 0x1000E;
51const SBSDIO_HT_AVAIL: u8 = 0x80; const SBSDIO_HT_AVAIL_REQ: u8 = 0x10; const SBSDIO_ALP_AVAIL_REQ: u8 = 0x08;
54const SBSDIO_FUNC1_SBADDRLOW: u32 = 0x1000A;
55const SBSDIO_FUNC1_SBADDRMID: u32 = 0x1000B;
56const SBSDIO_FUNC1_SBADDRHIGH: u32 = 0x1000C;
57
58const BRCMF_BACKPLANE_ARM: u32 = 0x18003000;
60
61#[derive(Copy, Clone, PartialEq, Eq)]
63pub enum WifiState {
64 Uninitialized,
65 FirmwareLoaded,
66 Idle,
67 Scanning,
68 Connecting,
69 Connected,
70 Error,
71}
72
73pub struct WifiNetwork {
74 pub ssid: [u8; 32],
75 pub ssid_len: usize,
76 pub rssi: i8,
77 pub channel: u8,
78 pub security: WifiSecurity,
79 pub bssid: [u8; 6],
80}
81
82#[derive(Copy, Clone, PartialEq, Eq)]
83pub enum WifiSecurity {
84 Open,
85 Wpa2Personal,
86 Unknown,
87}
88
89static WIFI_STATE: Mutex<WifiState> = Mutex::new(WifiState::Uninitialized);
91static mut WIFI_IP: [u8; 4] = [0; 4];
92static mut WIFI_SSID: [u8; 32] = [0; 32];
93static mut WIFI_SSID_LEN: usize = 0;
94static mut TX_SEQ: u8 = 0;
95static mut TX_ID: u16 = 0;
96
97fn delay_ms(ms: u32) {
100 for _ in 0..(ms * 50_000) {
101 unsafe { core::arch::asm!("nop") };
102 }
103}
104
105unsafe fn set_backplane_window(addr: u32) -> bool {
110 let a0 = ((addr >> 8) & 0xFF) as u8;
111 let a1 = ((addr >> 16) & 0xFF) as u8;
112 let a2 = ((addr >> 24) & 0xFF) as u8;
113 sdio::cmd52(sdio::SDIO_FUNC_BACKPLANE, true, SBSDIO_FUNC1_SBADDRLOW, a0).is_some()
114 && sdio::cmd52(sdio::SDIO_FUNC_BACKPLANE, true, SBSDIO_FUNC1_SBADDRMID, a1).is_some()
115 && sdio::cmd52(sdio::SDIO_FUNC_BACKPLANE, true, SBSDIO_FUNC1_SBADDRHIGH, a2).is_some()
116}
117
118unsafe fn backplane_read32(addr: u32) -> Option<u32> {
119 let window = addr & !0x7FFF;
120 if !set_backplane_window(window) {
121 return None;
122 }
123 let offset = (addr & 0x7FFF) | 0x08000; let mut buf = [0u8; 4];
125 if !sdio::cmd53(sdio::SDIO_FUNC_BACKPLANE, false, offset, &mut buf) {
126 return None;
127 }
128 Some(u32::from_le_bytes(buf))
129}
130
131unsafe fn backplane_write32(addr: u32, val: u32) -> bool {
132 let window = addr & !0x7FFF;
133 if !set_backplane_window(window) {
134 return false;
135 }
136 let offset = (addr & 0x7FFF) | 0x08000;
137 sdio::cmd53(
138 sdio::SDIO_FUNC_BACKPLANE,
139 true,
140 offset,
141 &mut val.to_le_bytes().clone(),
142 )
143}
144
145unsafe fn upload_firmware(firmware: &[u8]) -> bool {
150 crate::info!("[NET] WiFi: Uploading firmware ({} bytes)...", firmware.len());
151 const CHUNK: usize = 64;
152 let mut offset = 0usize;
153 while offset < firmware.len() {
154 let chunk_len = (firmware.len() - offset).min(CHUNK);
155 let addr = offset as u32;
156 if !set_backplane_window(addr & !0x7FFF) {
157 return false;
158 }
159 let reg = addr & 0x7FFF;
160 let mut buf = [0u8; CHUNK];
161 buf[..chunk_len].copy_from_slice(&firmware[offset..offset + chunk_len]);
162 if !sdio::cmd53(sdio::SDIO_FUNC_BACKPLANE, true, reg, &mut buf[..chunk_len]) {
163 crate::info!("[NET] WiFi: firmware write failed at offset 0x{:X}", offset);
164 return false;
165 }
166 offset += chunk_len;
167 if offset.is_multiple_of(64 * 1024) {
168 crate::info!(
169 "WiFi: firmware {}KB / {}KB",
170 offset / 1024,
171 firmware.len() / 1024
172 );
173 }
174 }
175 crate::info!("[NET] WiFi: Firmware uploaded.");
176 true
177}
178
179unsafe fn start_firmware() -> bool {
180 if !backplane_write32(BRCMF_BACKPLANE_ARM + 0x800, 0) {
182 crate::info!("[NET] WiFi: ARM reset write failed");
183 return false;
184 }
185 delay_ms(50);
186 crate::info!("[NET] WiFi: ARM core reset released.");
187 true
188}
189
190unsafe fn wait_ht_clock() -> bool {
195 sdio::cmd52(
197 sdio::SDIO_FUNC_BACKPLANE,
198 true,
199 SBSDIO_FUNC1_CHIPCLKCSR,
200 SBSDIO_HT_AVAIL_REQ | SBSDIO_ALP_AVAIL_REQ,
201 );
202 for _ in 0..1000 {
204 let clk =
205 sdio::cmd52(sdio::SDIO_FUNC_BACKPLANE, false, SBSDIO_FUNC1_CHIPCLKCSR, 0).unwrap_or(0);
206 if (clk & SBSDIO_HT_AVAIL) != 0 {
207 crate::info!("[NET] WiFi: HT clock available (CSR=0x{:02X})", clk);
208 return true;
209 }
210 delay_ms(1);
211 }
212 crate::info!("[NET] WiFi: HT clock timeout");
213 false
214}
215
216unsafe fn sdpcm_send(channel: u8, payload: &[u8]) -> bool {
223 let total = SDPCM_HDR_LEN + payload.len();
224 if total > 512 {
225 crate::info!("[NET] WiFi: sdpcm_send frame too large: {}", total);
226 return false;
227 }
228 let mut frame = [0u8; 512];
229 let len = total as u16;
231 frame[0] = (len & 0xFF) as u8;
232 frame[1] = (len >> 8) as u8;
233 frame[2] = !(len as u8);
234 frame[3] = !((len >> 8) as u8);
235 frame[4] = TX_SEQ;
236 TX_SEQ = TX_SEQ.wrapping_add(1);
237 frame[5] = channel & 0x0F;
238 frame[6] = 0; frame[7] = SDPCM_HDR_LEN as u8; frame[8] = 0; frame[9] = 0; frame[10] = 0;
243 frame[11] = 0;
244 frame[SDPCM_HDR_LEN..total].copy_from_slice(payload);
245 sdio::cmd53(sdio::SDIO_FUNC_WLAN, true, 0, &mut frame[..total])
247}
248
249unsafe fn sdpcm_recv() -> Option<(u8, usize, Vec<u8>)> {
252 let int_pending = sdio::cmd52(sdio::SDIO_FUNC_BUS, false, 0x05, 0)?;
254 if (int_pending & 0x04) == 0 {
255 return None;
256 }
257
258 let mut hdr4 = [0u8; 4];
260 if !sdio::cmd53(sdio::SDIO_FUNC_WLAN, false, 0, &mut hdr4) {
261 return None;
262 }
263 let frame_len = u16::from_le_bytes([hdr4[0], hdr4[1]]) as usize;
264 let frame_chk = u16::from_le_bytes([hdr4[2], hdr4[3]]) as usize;
265 if frame_len == 0 || (frame_len ^ frame_chk) != 0xFFFF || frame_len < SDPCM_HDR_LEN {
267 return None;
268 }
269
270 let mut frame = alloc::vec![0u8; frame_len];
271 frame[..4].copy_from_slice(&hdr4);
272
273 let remaining = frame_len - 4;
275 if remaining > 0 {
276 let mut offset = 4usize;
277 while offset < frame_len {
278 let chunk = (frame_len - offset).min(508);
279 if !sdio::cmd53(
280 sdio::SDIO_FUNC_WLAN,
281 false,
282 0,
283 &mut frame[offset..offset + chunk],
284 ) {
285 return None;
286 }
287 offset += chunk;
288 }
289 }
290
291 let channel = frame[5] & 0x0F;
292 let data_off = frame[7] as usize;
293 if data_off > frame_len {
294 return None;
295 }
296 Some((channel, data_off, frame))
297}
298
299unsafe fn ioctl(cmd: u32, get: bool, payload_buf: &mut [u8]) -> Option<usize> {
308 TX_ID = TX_ID.wrapping_add(1);
309 let id = TX_ID as u32;
310 let flags = if get { CDC_FLAG_GET } else { CDC_FLAG_SET } | (id << CDC_ID_SHIFT);
311 let plen = payload_buf.len();
312
313 let mut cdc = alloc::vec![0u8; CDC_HDR_LEN + plen];
315 cdc[0..4].copy_from_slice(&cmd.to_le_bytes());
316 cdc[4..8].copy_from_slice(&(plen as u32).to_le_bytes());
317 cdc[8..12].copy_from_slice(&flags.to_le_bytes());
318 cdc[12..16].copy_from_slice(&0u32.to_le_bytes()); if plen > 0 && !get {
320 cdc[CDC_HDR_LEN..].copy_from_slice(payload_buf);
321 }
322
323 if !sdpcm_send(SDPCM_CHAN_CONTROL, &cdc) {
324 crate::info!("[NET] WiFi: ioctl cmd={} send failed", cmd);
325 return None;
326 }
327
328 for _ in 0..2000 {
330 if let Some((chan, data_off, frame)) = sdpcm_recv() {
331 if chan == SDPCM_CHAN_CONTROL {
332 if data_off + CDC_HDR_LEN > frame.len() {
333 continue;
334 }
335 let cdc_resp = &frame[data_off..];
336 let resp_flags =
337 u32::from_le_bytes([cdc_resp[8], cdc_resp[9], cdc_resp[10], cdc_resp[11]]);
338 let resp_id = resp_flags >> CDC_ID_SHIFT;
339 if resp_id != (id & 0xFFFF) {
340 continue;
341 } let status =
343 u32::from_le_bytes([cdc_resp[12], cdc_resp[13], cdc_resp[14], cdc_resp[15]]);
344 if status != 0 {
345 crate::info!("[NET] WiFi: ioctl cmd={} status={}", cmd, status);
346 return None;
347 }
348 if get {
349 let resp_plen =
350 u32::from_le_bytes([cdc_resp[4], cdc_resp[5], cdc_resp[6], cdc_resp[7]])
351 as usize;
352 let copy_len = resp_plen.min(plen);
353 let payload_start = data_off + CDC_HDR_LEN;
354 if payload_start + copy_len <= frame.len() {
355 payload_buf[..copy_len]
356 .copy_from_slice(&frame[payload_start..payload_start + copy_len]);
357 return Some(copy_len);
358 }
359 }
360 return Some(0);
361 }
362 }
364 delay_ms(1);
365 }
366 crate::info!("[NET] WiFi: ioctl cmd={} timeout", cmd);
367 None
368}
369
370unsafe fn ioctl_set_u32(cmd: u32, val: u32) -> bool {
371 ioctl(cmd, false, &mut val.to_le_bytes().clone()).is_some()
372}
373
374fn build_iovar(name: &str, data: &[u8]) -> Vec<u8> {
378 let nb = name.as_bytes();
379 let mut v = alloc::vec![0u8; nb.len() + 1 + data.len()];
380 v[..nb.len()].copy_from_slice(nb);
381 v[nb.len() + 1..].copy_from_slice(data);
383 v
384}
385
386unsafe fn iovar_set(name: &str, data: &[u8]) -> bool {
387 let mut buf = build_iovar(name, data);
388 ioctl(BRCMF_C_SET_VAR, false, &mut buf).is_some()
389}
390
391unsafe fn iovar_set_u32(name: &str, val: u32) -> bool {
392 iovar_set(name, &val.to_le_bytes())
393}
394
395unsafe fn iovar_set_bsscfg(name: &str, bsscfg: u32, data: &[u8]) -> bool {
397 let nb = name.as_bytes();
398 let mut buf = alloc::vec![0u8; nb.len() + 1 + 4 + data.len()];
399 buf[..nb.len()].copy_from_slice(nb);
400 buf[nb.len() + 1..nb.len() + 5].copy_from_slice(&bsscfg.to_le_bytes());
401 buf[nb.len() + 5..].copy_from_slice(data);
402 ioctl(BRCMF_C_SET_VAR, false, &mut buf).is_some()
403}
404
405unsafe fn wait_event(want_event: u32, timeout_ms: u32) -> Option<u32> {
412 for _ in 0..timeout_ms {
413 if let Some((chan, data_off, frame)) = sdpcm_recv() {
414 if chan == SDPCM_CHAN_EVENT {
415 let msg_off = data_off + 4 + 14; if msg_off + 20 > frame.len() {
420 continue;
421 }
422 let evt = u32::from_be_bytes([
424 frame[msg_off + 4],
425 frame[msg_off + 5],
426 frame[msg_off + 6],
427 frame[msg_off + 7],
428 ]);
429 let status = u32::from_be_bytes([
430 frame[msg_off + 8],
431 frame[msg_off + 9],
432 frame[msg_off + 10],
433 frame[msg_off + 11],
434 ]);
435 crate::info!("[NET] WiFi: event={} status={}", evt, status);
436 if evt == want_event {
437 return Some(status);
438 }
439 }
440 }
442 delay_ms(1);
443 }
444 None
445}
446
447unsafe fn enable_events() -> bool {
449 let mut mask = [0u8; 16];
450 let events = [0u32, 3, 5, 7, 11, 16, 46];
452 for e in &events {
453 let byte = (*e / 8) as usize;
454 let bit = *e % 8;
455 if byte < 16 {
456 mask[byte] |= 1 << bit;
457 }
458 }
459 iovar_set("event_msgs", &mask)
460}
461
462unsafe fn hlp_init() -> bool {
467 if !wait_ht_clock() {
469 return false;
470 }
471
472 let mut revinfo = [0u8; 48];
474 if ioctl(BRCMF_C_GET_REVINFO, true, &mut revinfo).is_some() {
475 let chip_id = u32::from_le_bytes([revinfo[0], revinfo[1], revinfo[2], revinfo[3]]);
476 crate::info!("[NET] WiFi: chip_id=0x{:04X}", chip_id);
477 } else {
478 crate::info!("[NET] WiFi: GET_REVINFO failed (firmware may still be booting)");
479 }
481
482 let mut country = [0u8; 12];
485 country[0] = b'U';
486 country[1] = b'S'; country[8] = b'U';
489 country[9] = b'S';
490 if !iovar_set("country", &country) {
491 crate::info!("[NET] WiFi: country set failed (non-fatal)");
492 }
493
494 if !ioctl_set_u32(BRCMF_C_SET_PM, 0) {
496 crate::info!("[NET] WiFi: SET_PM failed (non-fatal)");
497 }
498
499 if !enable_events() {
501 crate::info!("[NET] WiFi: enable_events failed (non-fatal)");
502 }
503
504 crate::info!("[NET] WiFi: HLP protocol initialized.");
505 true
506}
507
508pub fn init() -> bool {
514 if !sdio::is_initialized() {
515 return false;
516 }
517 crate::info!("[NET] WiFi: Initializing CYW43455...");
518
519 let fw_bytes = match crate::kernel::fs::read_file("/boot/brcmfmac43455-sdio.bin") {
521 Some(d) => d,
522 None => {
523 crate::info!("[NET] WiFi: firmware not found at /boot/brcmfmac43455-sdio.bin");
524 crate::info!("[NET] WiFi: copy brcmfmac43455-sdio.bin to SD card /boot/ folder.");
525 return false;
526 }
527 };
528 crate::info!("[NET] WiFi: firmware {} bytes loaded.", fw_bytes.len());
529
530 unsafe {
531 sdio::cmd52(
533 sdio::SDIO_FUNC_BACKPLANE,
534 true,
535 SBSDIO_FUNC1_CHIPCLKCSR,
536 SBSDIO_ALP_AVAIL_REQ,
537 );
538 delay_ms(20);
539
540 if !upload_firmware(&fw_bytes) {
541 return false;
542 }
543 if !start_firmware() {
544 return false;
545 }
546 }
547
548 delay_ms(500); *WIFI_STATE.lock() = WifiState::FirmwareLoaded;
551
552 unsafe {
553 if !hlp_init() {
554 crate::info!("[NET] WiFi: HLP init failed");
555 *WIFI_STATE.lock() = WifiState::Error;
556 return false;
557 }
558 }
559
560 *WIFI_STATE.lock() = WifiState::Idle;
561 crate::info!("[NET] WiFi: CYW43455 ready.");
562 true
563}
564
565pub fn connect(ssid: &str, password: &str) -> bool {
568 {
569 let state = *WIFI_STATE.lock();
570 if state != WifiState::Idle {
571 return false;
572 }
573 }
574 if ssid.is_empty() || ssid.len() > 32 {
575 return false;
576 }
577
578 crate::info!("[NET] WiFi: Connecting to '{}'...", ssid);
579 *WIFI_STATE.lock() = WifiState::Connecting;
580
581 unsafe {
582 WIFI_SSID_LEN = ssid.len();
583 WIFI_SSID[..ssid.len()].copy_from_slice(ssid.as_bytes());
584 }
585
586 unsafe {
587 if !ioctl_set_u32(BRCMF_C_SET_WSEC, WSEC_AES) {
589 crate::info!("[NET] WiFi: SET_WSEC failed");
590 return fail_connect();
591 }
592
593 if !iovar_set_bsscfg("sup_wpa", 0, &1u32.to_le_bytes()) {
595 crate::info!("[NET] WiFi: sup_wpa failed (non-fatal)");
596 }
597 if !iovar_set_bsscfg("sup_wpa2_eapver", 0, &0xFFFF_FFFFu32.to_le_bytes()) {
599 crate::info!("[NET] WiFi: sup_wpa2_eapver failed (non-fatal)");
600 }
601 if !iovar_set_bsscfg("sup_wpa_tmo", 0, &2500u32.to_le_bytes()) {
603 crate::info!("[NET] WiFi: sup_wpa_tmo failed (non-fatal)");
604 }
605
606 {
608 let pw = password.as_bytes();
609 let pw_len = pw.len().min(64);
610 let mut pmk = [0u8; 68]; pmk[0] = pw_len as u8;
612 pmk[1] = (pw_len >> 8) as u8;
613 pmk[2] = (WSEC_PASSPHRASE & 0xFF) as u8;
614 pmk[3] = (WSEC_PASSPHRASE >> 8) as u8;
615 pmk[4..4 + pw_len].copy_from_slice(&pw[..pw_len]);
616 if !iovar_set("wsec_pmk", &pmk) {
617 crate::info!("[NET] WiFi: wsec_pmk failed");
618 return fail_connect();
619 }
620 }
621
622 if !ioctl_set_u32(BRCMF_C_SET_INFRA, 1) {
624 crate::info!("[NET] WiFi: SET_INFRA failed");
625 return fail_connect();
626 }
627
628 if !ioctl_set_u32(BRCMF_C_SET_AUTH, 0) {
630 crate::info!("[NET] WiFi: SET_AUTH failed");
631 return fail_connect();
632 }
633
634 {
636 let mut ssid_buf = [0u8; 36];
637 let slen = ssid.len().min(32);
638 ssid_buf[0] = slen as u8;
639 ssid_buf[4..4 + slen].copy_from_slice(&ssid.as_bytes()[..slen]);
640 if ioctl(BRCMF_C_SET_SSID, false, &mut ssid_buf).is_none() {
641 crate::info!("[NET] WiFi: SET_SSID failed");
642 return fail_connect();
643 }
644 }
645
646 crate::info!("[NET] WiFi: Waiting for association (up to 15s)...");
648 match wait_event(BRCMF_E_SET_SSID, 15_000) {
649 Some(BRCMF_E_STATUS_SUCCESS) => {
650 crate::info!("[NET] WiFi: Associated with '{}'!", ssid);
651 *WIFI_STATE.lock() = WifiState::Connected;
652 true
653 }
654 Some(status) => {
655 crate::info!("[NET] WiFi: Association failed (status={})", status);
656 fail_connect()
657 }
658 None => {
659 crate::info!("[NET] WiFi: Association timeout");
660 fail_connect()
661 }
662 }
663 }
664}
665
666fn fail_connect() -> bool {
667 *WIFI_STATE.lock() = WifiState::Error;
668 false
669}
670
671pub fn disconnect() -> bool {
673 if *WIFI_STATE.lock() != WifiState::Connected {
674 return false;
675 }
676 unsafe {
677 ioctl_set_u32(BRCMF_C_DISASSOC, 0);
678 }
679 *WIFI_STATE.lock() = WifiState::Idle;
680 true
681}
682
683pub fn set_ip(ip: [u8; 4]) {
685 unsafe {
686 WIFI_IP = ip;
687 }
688}
689
690pub fn state() -> WifiState {
692 *WIFI_STATE.lock()
693}
694pub fn is_connected() -> bool {
695 *WIFI_STATE.lock() == WifiState::Connected
696}
697pub fn get_ip() -> [u8; 4] {
698 unsafe { WIFI_IP }
699}
700pub fn get_ssid() -> &'static str {
701 unsafe { core::str::from_utf8(&WIFI_SSID[..WIFI_SSID_LEN]).unwrap_or("") }
702}