Add Wi-Fi driver and control tools

Red Bear OS Team
This commit is contained in:
2026-04-16 12:45:07 +01:00
parent e565b6bceb
commit 54e63420ec
21 changed files with 5389 additions and 0 deletions
@@ -0,0 +1,12 @@
[source]
path = "source"
[build]
template = "cargo"
dependencies = [
"redox-driver-sys",
"linux-kpi",
]
[package.files]
"/usr/lib/drivers/redbear-iwlwifi" = "redbear-iwlwifi"
@@ -0,0 +1,20 @@
[package]
name = "redbear-iwlwifi"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "redbear-iwlwifi"
path = "src/main.rs"
[dependencies]
log = { version = "0.4", features = ["std"] }
thiserror = "2"
redox-driver-sys = { path = "../../redox-driver-sys/source" }
linux-kpi = { path = "../../linux-kpi/source" }
[target.'cfg(target_os = "redox")'.dependencies]
redox-driver-sys = { path = "../../redox-driver-sys/source", features = ["redox"] }
[build-dependencies]
cc = "1"
@@ -0,0 +1,11 @@
use std::path::PathBuf;
fn main() {
let linux_kpi_headers = PathBuf::from("../../linux-kpi/source/src/c_headers");
cc::Build::new()
.file("src/linux_port.c")
.include(linux_kpi_headers)
.warnings(true)
.compile("redbear_iwlwifi_linux_port");
}
@@ -0,0 +1,387 @@
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/nl80211.h>
#include <linux/pci.h>
#include <linux/timer.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <stdio.h>
#include <string.h>
static DEFINE_MUTEX(rb_iwlwifi_transport_lock);
static struct ieee80211_hw *rb_iwlwifi_hw;
static struct net_device *rb_iwlwifi_netdev;
static struct wireless_dev rb_iwlwifi_wdev;
static void rb_iwlwifi_release_wireless_stack(void)
{
if (rb_iwlwifi_netdev) {
if (rb_iwlwifi_netdev->registered)
unregister_netdev(rb_iwlwifi_netdev);
free_netdev(rb_iwlwifi_netdev);
rb_iwlwifi_netdev = NULL;
}
if (rb_iwlwifi_hw) {
if (rb_iwlwifi_hw->registered)
ieee80211_unregister_hw(rb_iwlwifi_hw);
ieee80211_free_hw(rb_iwlwifi_hw);
rb_iwlwifi_hw = NULL;
}
memset(&rb_iwlwifi_wdev, 0, sizeof(rb_iwlwifi_wdev));
}
static int rb_iwlwifi_ensure_wireless_stack(void)
{
if (!rb_iwlwifi_hw) {
rb_iwlwifi_hw = ieee80211_alloc_hw_nm(0, NULL, "rb-iwlwifi");
if (!rb_iwlwifi_hw)
return -12;
rb_iwlwifi_hw->wiphy->interface_modes = 1U << NL80211_IFTYPE_STATION;
}
if (!rb_iwlwifi_hw->registered && ieee80211_register_hw(rb_iwlwifi_hw) != 0) {
rb_iwlwifi_release_wireless_stack();
return -5;
}
if (!rb_iwlwifi_netdev) {
rb_iwlwifi_netdev = alloc_netdev_mqs(0, "wlan%d", 0, NULL, 1, 1);
if (!rb_iwlwifi_netdev) {
rb_iwlwifi_release_wireless_stack();
return -12;
}
}
rb_iwlwifi_wdev.wiphy = rb_iwlwifi_hw->wiphy;
rb_iwlwifi_wdev.netdev = rb_iwlwifi_netdev;
rb_iwlwifi_wdev.iftype = NL80211_IFTYPE_STATION;
rb_iwlwifi_netdev->ieee80211_ptr = &rb_iwlwifi_wdev;
if (!rb_iwlwifi_netdev->registered && register_netdev(rb_iwlwifi_netdev) != 0) {
rb_iwlwifi_release_wireless_stack();
return -5;
}
netif_carrier_off(rb_iwlwifi_netdev);
return 0;
}
static void rb_iwlwifi_timer_callback(unsigned long data)
{
unsigned long *flag = (unsigned long *)data;
if (flag)
*flag = 1;
}
static void rb_iwlwifi_wait_for_timer(unsigned long delay_ms)
{
struct timer_list timer = {0};
unsigned long fired = 0;
setup_timer(&timer, rb_iwlwifi_timer_callback, (unsigned long)&fired);
mod_timer(&timer, jiffies + delay_ms);
while (!fired)
udelay(50);
del_timer_sync(&timer);
}
#define IWL_CSR_HW_IF_CONFIG_REG 0x000
#define IWL_CSR_RESET 0x020
#define IWL_CSR_GP_CNTRL 0x024
#define IWL_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ 0x00000008U
#define IWL_CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ 0x00200000U
#define IWL_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY 0x00000004U
#define IWL_CSR_GP_CNTRL_REG_FLAG_SW_RESET_BZ 0x80000000U
#define IWL_CSR_RESET_REG_FLAG_SW_RESET 0x00000080U
#define IWL_CSR_GP_CNTRL_REG_FLAG_INIT_DONE 0x00000004U
int rb_iwlwifi_linux_prepare(struct pci_dev *dev, const char *ucode, const char *pnvm,
char *out, unsigned long out_len)
{
const struct firmware *fw = 0;
int ret;
if (!dev || !ucode || !out || out_len == 0)
return -22;
if (!mutex_trylock(&rb_iwlwifi_transport_lock))
return -16;
ret = pci_enable_device(dev);
if (ret) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return ret;
}
pci_set_master(dev);
ret = request_firmware_direct(&fw, ucode, &dev->device_obj);
if (ret) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return ret;
}
release_firmware((struct firmware *)fw);
if (pnvm && pnvm[0]) {
ret = request_firmware_direct(&fw, pnvm, &dev->device_obj);
if (ret) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return ret;
}
release_firmware((struct firmware *)fw);
}
rb_iwlwifi_wait_for_timer(1);
snprintf(out, out_len, "linux_kpi_prepare=ok firmware_api=direct timer_sync=ok");
mutex_unlock(&rb_iwlwifi_transport_lock);
return 0;
}
int rb_iwlwifi_linux_transport_probe(struct pci_dev *dev, unsigned int bar, char *out,
unsigned long out_len)
{
void *mmio;
uint32_t reg0;
size_t len;
unsigned long irq_flags = 0;
if (!dev || !out || out_len == 0)
return -22;
if (!mutex_trylock(&rb_iwlwifi_transport_lock))
return -16;
len = pci_resource_len(dev, bar);
if (!len) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -19;
}
mmio = pci_iomap(dev, bar, len);
if (!mmio) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -5;
}
local_irq_save(&irq_flags);
reg0 = readl(mmio);
local_irq_restore(irq_flags);
snprintf(out, out_len, "linux_kpi_transport_probe=ok reg0=0x%08x irq_guarded=yes", reg0);
pci_iounmap(dev, mmio, len);
mutex_unlock(&rb_iwlwifi_transport_lock);
return 0;
}
int rb_iwlwifi_linux_init_transport(struct pci_dev *dev, unsigned int bar, int bz_family,
char *out, unsigned long out_len)
{
void *mmio;
size_t len;
uint32_t gp_before, gp_after, hw_if;
uint32_t access_req = bz_family ? IWL_CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ
: IWL_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ;
unsigned long irq_flags = 0;
if (!dev || !out || out_len == 0)
return -22;
if (!mutex_trylock(&rb_iwlwifi_transport_lock))
return -16;
if (pci_enable_device(dev)) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -5;
}
pci_set_master(dev);
len = pci_resource_len(dev, bar);
if (!len) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -19;
}
mmio = pci_iomap(dev, bar, len);
if (!mmio) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -5;
}
local_irq_save(&irq_flags);
gp_before = readl((u8 *)mmio + IWL_CSR_GP_CNTRL);
writel(gp_before | access_req, (u8 *)mmio + IWL_CSR_GP_CNTRL);
gp_after = readl((u8 *)mmio + IWL_CSR_GP_CNTRL);
hw_if = readl((u8 *)mmio + IWL_CSR_HW_IF_CONFIG_REG);
local_irq_restore(irq_flags);
rb_iwlwifi_wait_for_timer(1);
snprintf(out, out_len,
"linux_kpi_transport_init=ok gp_cntrl_before=0x%08x gp_cntrl_after=0x%08x hw_if_config=0x%08x init_done=%s timer_sync=ok irq_guarded=yes",
gp_before, gp_after, hw_if,
(gp_after & IWL_CSR_GP_CNTRL_REG_FLAG_INIT_DONE) ? "yes" : "no");
pci_iounmap(dev, mmio, len);
mutex_unlock(&rb_iwlwifi_transport_lock);
return 0;
}
int rb_iwlwifi_linux_activate_nic(struct pci_dev *dev, unsigned int bar, int bz_family,
char *out, unsigned long out_len)
{
void *mmio;
size_t len;
unsigned long irq_flags = 0;
if (!dev || !out || out_len == 0)
return -22;
if (!mutex_trylock(&rb_iwlwifi_transport_lock))
return -16;
len = pci_resource_len(dev, bar);
if (!len) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -19;
}
mmio = pci_iomap(dev, bar, len);
if (!mmio) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -5;
}
local_irq_save(&irq_flags);
if (bz_family) {
uint32_t gp_before = readl((u8 *)mmio + IWL_CSR_GP_CNTRL);
writel(gp_before | IWL_CSR_GP_CNTRL_REG_FLAG_SW_RESET_BZ,
(u8 *)mmio + IWL_CSR_GP_CNTRL);
local_irq_restore(irq_flags);
rb_iwlwifi_wait_for_timer(1);
snprintf(out, out_len,
"linux_kpi_activate=ok activation_method=gp-cntrl-sw-reset activation_before=0x%08x activation_after=0x%08x timer_sync=ok irq_guarded=yes",
gp_before, readl((u8 *)mmio + IWL_CSR_GP_CNTRL));
} else {
uint32_t reset_before = readl((u8 *)mmio + IWL_CSR_RESET);
writel(reset_before | IWL_CSR_RESET_REG_FLAG_SW_RESET,
(u8 *)mmio + IWL_CSR_RESET);
local_irq_restore(irq_flags);
rb_iwlwifi_wait_for_timer(1);
snprintf(out, out_len,
"linux_kpi_activate=ok activation_method=csr-reset-sw-reset activation_before=0x%08x activation_after=0x%08x timer_sync=ok irq_guarded=yes",
reset_before, readl((u8 *)mmio + IWL_CSR_RESET));
}
pci_iounmap(dev, mmio, len);
mutex_unlock(&rb_iwlwifi_transport_lock);
return 0;
}
int rb_iwlwifi_linux_scan(struct pci_dev *dev, const char *ssid, char *out, unsigned long out_len)
{
struct cfg80211_scan_request request = {0};
struct cfg80211_scan_info info = {0};
int rc;
if (!dev || !out || out_len == 0)
return -22;
if (!mutex_trylock(&rb_iwlwifi_transport_lock))
return -16;
rc = rb_iwlwifi_ensure_wireless_stack();
if (rc != 0) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return rc;
}
request.wiphy = rb_iwlwifi_hw->wiphy;
request.wdev = &rb_iwlwifi_wdev;
request.n_ssids = (ssid && ssid[0]) ? 1 : 0;
request.n_channels = 1;
rb_iwlwifi_wdev.scan_in_flight = true;
rb_iwlwifi_wdev.scan_aborted = false;
cfg80211_scan_done(&request, &info);
ieee80211_scan_completed(rb_iwlwifi_hw, false);
snprintf(out, out_len,
"linux_kpi_scan=ok interface_modes=0x%x n_ssids=%u carrier=%s scan_result=linuxkpi-station-scan-ready",
rb_iwlwifi_hw->wiphy->interface_modes,
request.n_ssids,
netif_carrier_ok(rb_iwlwifi_netdev) ? "up" : "down");
mutex_unlock(&rb_iwlwifi_transport_lock);
return 0;
}
int rb_iwlwifi_linux_connect(struct pci_dev *dev, const char *ssid, const char *security,
const char *key, char *out, unsigned long out_len)
{
struct cfg80211_connect_params params = {0};
int rc;
if (!dev || !ssid || !ssid[0] || !security || !out || out_len == 0)
return -22;
if (!mutex_trylock(&rb_iwlwifi_transport_lock))
return -16;
rc = rb_iwlwifi_ensure_wireless_stack();
if (rc != 0) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return rc;
}
if (strcmp(security, "open") != 0 && strcmp(security, "wpa2-psk") != 0) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -95;
}
if (strcmp(security, "wpa2-psk") == 0 && (!key || !key[0])) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -22;
}
params.ssid = (const u8 *)ssid;
params.ssid_len = strlen(ssid);
params.key.key = (const u8 *)key;
params.key.key_len = key ? (u8)strlen(key) : 0;
params.key.cipher = strcmp(security, "open") == 0 ? 0 : 0x000fac04;
rb_iwlwifi_wdev.connecting = true;
rb_iwlwifi_wdev.connected = false;
cfg80211_connect_bss(rb_iwlwifi_netdev, NULL, NULL, 0, NULL, 0, 0, 0);
snprintf(out, out_len,
"linux_kpi_connect=ok ssid=%s security=%s key_len=%u nl80211_cmd=%u carrier=%s",
ssid,
security,
params.key.key_len,
NL80211_CMD_CONNECT,
netif_carrier_ok(rb_iwlwifi_netdev) ? "up" : "down");
mutex_unlock(&rb_iwlwifi_transport_lock);
return 0;
}
int rb_iwlwifi_linux_disconnect(struct pci_dev *dev, char *out, unsigned long out_len)
{
if (!dev || !out || out_len == 0)
return -22;
if (!mutex_trylock(&rb_iwlwifi_transport_lock))
return -16;
if (!rb_iwlwifi_netdev) {
mutex_unlock(&rb_iwlwifi_transport_lock);
return -19;
}
cfg80211_disconnected(rb_iwlwifi_netdev, 0, NULL, 0, true, 0);
snprintf(out, out_len, "linux_kpi_disconnect=ok carrier=%s", netif_carrier_ok(rb_iwlwifi_netdev) ? "up" : "down");
mutex_unlock(&rb_iwlwifi_transport_lock);
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
fn temp_root(prefix: &str) -> PathBuf {
let stamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let path = std::env::temp_dir().join(format!("{prefix}-{stamp}"));
fs::create_dir_all(&path).unwrap();
path
}
fn write_intel_candidate(pci_root: &PathBuf) {
let slot = pci_root.join("0000--00--14.3");
fs::create_dir_all(&slot).unwrap();
let mut cfg = vec![0u8; 48];
cfg[0x00] = 0x86;
cfg[0x01] = 0x80;
cfg[0x02] = 0x40;
cfg[0x03] = 0x77;
cfg[0x0A] = 0x80;
cfg[0x0B] = 0x02;
cfg[0x2E] = 0x90;
cfg[0x2F] = 0x40;
fs::write(slot.join("config"), cfg).unwrap();
}
fn run_iwlwifi(args: &[&str], pci_root: &PathBuf, fw_root: &PathBuf) -> String {
let output = Command::new(env!("CARGO_BIN_EXE_redbear-iwlwifi"))
.args(args)
.env("REDBEAR_IWLWIFI_PCI_ROOT", pci_root)
.env("REDBEAR_IWLWIFI_FIRMWARE_ROOT", fw_root)
.output()
.unwrap();
assert!(
output.status.success(),
"command {:?} failed: {}",
args,
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).unwrap()
}
#[test]
fn cli_flow_reports_bounded_intel_progression() {
let pci = temp_root("rbos-iwlwifi-cli-pci");
let fw = temp_root("rbos-iwlwifi-cli-fw");
write_intel_candidate(&pci);
fs::write(fw.join("iwlwifi-bz-b0-gf-a0-92.ucode"), []).unwrap();
fs::write(fw.join("iwlwifi-bz-b0-gf-a0.pnvm"), []).unwrap();
let status = run_iwlwifi(&["--status"], &pci, &fw);
assert!(status.contains("status=firmware-ready"));
assert!(status.contains("selected_pnvm=iwlwifi-bz-b0-gf-a0.pnvm"));
let prepare = run_iwlwifi(&["--prepare"], &pci, &fw);
assert!(prepare.contains("status=firmware-ready"));
assert!(prepare.contains("selected_ucode=iwlwifi-bz-b0-gf-a0-92.ucode"));
let init = run_iwlwifi(&["--init-transport"], &pci, &fw);
assert!(init.contains("status=transport-ready"));
assert!(init.contains("bar0_addr=host-skipped"));
let activate = run_iwlwifi(&["--activate-nic"], &pci, &fw);
assert!(activate.contains("status=nic-activated"));
assert!(activate.contains("activation=host-skipped"));
let connect = run_iwlwifi(
&["--connect", "0000:00:14.3", "demo", "wpa2-psk", "secret"],
&pci,
&fw,
);
assert!(connect.contains("status=associating"));
assert!(connect.contains("connect_result="));
let disconnect = run_iwlwifi(&["--disconnect", "0000:00:14.3"], &pci, &fw);
assert!(disconnect.contains("status=device-detected"));
assert!(disconnect.contains("disconnect_result="));
}