use core::ptr::NonNull;
use log::{error, info};
use rd_net::WifiLinkPolicy;
use rdrive::{probe::OnProbeError, register::ProbeFdt};
use sdhci_cv1800::{
CviSdhci,
hw_init::{Sdio1HwConfig, sdio1_hw_init},
};
use sdio_host::SdioHost;
use crate::net::PlatformDeviceNet;
const SYSCON_PADDR: usize = 0x0300_0000;
const CRG_PADDR: usize = 0x0300_2000;
const RTCSYS_CTRL_PADDR: usize = 0x0502_5000;
const RTCSYS_IO_PADDR: usize = 0x0502_7000;
const MMIO_PAGE: usize = 0x1000;
const SYSCON_SIZE: usize = 0x2000;
const AP_SSID: &[u8] = b"PicoClaw-Car";
const AP_CHANNEL: u8 = 6;
const AP_SERVER_IP: [u8; 4] = [192, 168, 50, 1];
const AP_CLIENT_IP: [u8; 4] = [192, 168, 50, 2];
const AP_PREFIX_LEN: u8 = 24;
crate::model_register!(
name: "AIC8800 WiFi (CV181x SDIO)",
level: ProbeLevel::PostKernel,
priority: ProbePriority::DEFAULT,
probe_kinds: &[ProbeKind::Fdt {
compatibles: &["cvitek,cv181x-sdio"],
on_probe: probe
}],
);
fn map_mmio(paddr: usize, size: usize) -> Result<usize, OnProbeError> {
crate::mmio::iomap(paddr, size).map(|p| p.as_ptr() as usize)
}
fn decode_fdt_irq(interrupts: &[rdrive::probe::fdt::InterruptRef]) -> Option<usize> {
let interrupt = interrupts.first()?;
match &*interrupt.specifier {
[irq] | [irq, _] => Some(*irq as usize),
_ => None,
}
}
unsafe fn sdio1_raw_irq_handler(
_ctx: axklib::irq::IrqContext,
_data: NonNull<()>,
) -> axklib::irq::IrqReturn {
sdhci_cv1800::irq::sdhci_irq_handler(0);
axklib::irq::IrqReturn::Handled
}
fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> {
let (info, plat_dev) = probe.into_parts();
let sdio1_reg =
info.node.regs().into_iter().next().ok_or_else(|| {
OnProbeError::other(alloc::format!("[{}] has no reg", info.node.name()))
})?;
let sdio1_paddr = sdio1_reg.address as usize;
let cfg = Sdio1HwConfig::new(
map_mmio(CRG_PADDR, MMIO_PAGE)?,
map_mmio(SYSCON_PADDR, SYSCON_SIZE)?,
map_mmio(RTCSYS_CTRL_PADDR, MMIO_PAGE)?,
map_mmio(RTCSYS_IO_PADDR, MMIO_PAGE)?,
map_mmio(sdio1_paddr, MMIO_PAGE)?,
0,
);
info!(
"[wifi] SDIO1 HW init (node={}, sdio1={:#x})",
info.node.name(),
sdio1_paddr
);
sdio1_hw_init(&cfg);
let irq = decode_fdt_irq(&info.interrupts())
.ok_or_else(|| OnProbeError::other(alloc::format!("[{}] has no irq", info.node.name())))?;
info!("[wifi] SDIO1 IRQ resolved to {irq}");
if let Err(e) = axklib::irq::request_shared(irq, sdio1_raw_irq_handler, NonNull::dangling()) {
return Err(OnProbeError::other(alloc::format!(
"[wifi] failed to register SDIO1 IRQ {irq}: {e:?}"
)));
}
let mut sdio = CviSdhci::new(cfg.sdio1_base_va);
sdio.init()
.map_err(|e| OnProbeError::other(alloc::format!("[wifi] SDIO1 init failed: {e:?}")))?;
let (vid, did) = sdio.vendor_device_id();
info!("[wifi] SDIO device: vendor={vid:#06x} device={did:#06x}");
sdio.prepare_first_data_xfer();
let mut wifi = aic8800::probe(sdio)
.map_err(|e| OnProbeError::other(alloc::format!("[wifi] chip probe failed: {e}")))?;
info!("[wifi] chip probe complete");
if let Err(e) = rd_net::WifiControl::start_ap_open(&mut wifi, AP_SSID, AP_CHANNEL) {
error!("[wifi] AP start failed: {e:?}");
return Err(OnProbeError::other(alloc::format!(
"[wifi] AP start failed: {e:?}"
)));
}
info!("[wifi] SoftAP started, channel {AP_CHANNEL}");
let wifi = wifi.with_link_policy(WifiLinkPolicy {
ip: AP_SERVER_IP,
prefix_len: AP_PREFIX_LEN,
dhcp_server_client_ip: Some(AP_CLIENT_IP),
});
plat_dev.register_net("wlan0", wifi);
info!("[wifi] wlan0 device registered (probe stage done)");
Ok(())
}