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,410 @@
mod backend;
mod scheme;
use std::env;
#[cfg(target_os = "redox")]
use std::os::fd::RawFd;
use std::path::Path;
use std::process;
use backend::{Backend, IntelBackend, NoDeviceBackend, StubBackend};
#[cfg(target_os = "redox")]
use log::info;
use log::LevelFilter;
#[cfg(target_os = "redox")]
use redox_scheme::{scheme::SchemeSync, SignalBehavior, Socket};
#[cfg(target_os = "redox")]
use scheme::WifiCtlScheme;
fn init_logging(level: LevelFilter) {
log::set_max_level(level);
}
#[cfg(target_os = "redox")]
unsafe fn get_init_notify_fd() -> RawFd {
let fd: RawFd = env::var("INIT_NOTIFY")
.expect("redbear-wifictl: INIT_NOTIFY not set")
.parse()
.expect("redbear-wifictl: INIT_NOTIFY is not a valid fd");
unsafe {
libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC);
}
fd
}
#[cfg(target_os = "redox")]
fn notify_scheme_ready(notify_fd: RawFd, socket: &Socket, scheme: &mut WifiCtlScheme) {
let cap_id = scheme
.scheme_root()
.expect("redbear-wifictl: scheme_root failed");
let cap_fd = socket
.create_this_scheme_fd(0, cap_id, 0, 0)
.expect("redbear-wifictl: create_this_scheme_fd failed");
syscall::call_wo(
notify_fd as usize,
&libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(),
syscall::CallFlags::FD,
&[],
)
.expect("redbear-wifictl: failed to notify init that scheme is ready");
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum BackendMode {
Intel,
NoDevice,
Stub,
}
fn iwlwifi_command_path() -> std::path::PathBuf {
env::var_os("REDBEAR_IWLWIFI_CMD")
.map(std::path::PathBuf::from)
.unwrap_or_else(|| std::path::PathBuf::from("/usr/lib/drivers/redbear-iwlwifi"))
}
fn select_backend_mode(
explicit: Option<&str>,
intel_driver_present: bool,
intel_interfaces_present: bool,
redox_runtime: bool,
) -> BackendMode {
match explicit {
Some("intel") => BackendMode::Intel,
Some("stub") => BackendMode::Stub,
_ if redox_runtime && intel_driver_present && intel_interfaces_present => {
BackendMode::Intel
}
_ if redox_runtime && intel_driver_present => BackendMode::NoDevice,
_ => BackendMode::Stub,
}
}
fn build_backend() -> Box<dyn Backend> {
let explicit = env::var("REDBEAR_WIFICTL_BACKEND").ok();
let intel_driver_present = Path::new(&iwlwifi_command_path()).exists();
let intel_interfaces_present = if cfg!(target_os = "redox") && intel_driver_present {
!IntelBackend::from_env().interfaces().is_empty()
} else {
false
};
let mode = select_backend_mode(
explicit.as_deref(),
intel_driver_present,
intel_interfaces_present,
cfg!(target_os = "redox"),
);
match mode {
BackendMode::Intel => Box::new(IntelBackend::from_env()),
BackendMode::NoDevice => Box::new(NoDeviceBackend::new()),
BackendMode::Stub => Box::new(StubBackend::from_env()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn explicit_backend_selection_wins() {
assert_eq!(
select_backend_mode(Some("intel"), false, false, false),
BackendMode::Intel
);
assert_eq!(
select_backend_mode(Some("stub"), true, true, true),
BackendMode::Stub
);
}
#[test]
fn redox_runtime_prefers_intel_when_driver_present() {
assert_eq!(
select_backend_mode(None, true, true, true),
BackendMode::Intel
);
assert_eq!(
select_backend_mode(None, false, false, true),
BackendMode::Stub
);
}
#[test]
fn redox_runtime_uses_no_device_backend_without_detected_intel_interfaces() {
assert_eq!(
select_backend_mode(None, true, false, true),
BackendMode::NoDevice
);
}
#[test]
fn host_runtime_stays_stub_without_explicit_override() {
assert_eq!(
select_backend_mode(None, true, true, false),
BackendMode::Stub
);
assert_eq!(
select_backend_mode(None, false, false, false),
BackendMode::Stub
);
}
}
fn main() {
let log_level = match env::var("REDBEAR_WIFICTL_LOG").as_deref() {
Ok("debug") => LevelFilter::Debug,
Ok("trace") => LevelFilter::Trace,
Ok("warn") => LevelFilter::Warn,
Ok("error") => LevelFilter::Error,
_ => LevelFilter::Info,
};
init_logging(log_level);
let mut args = env::args().skip(1);
match args.next().as_deref() {
Some("--probe") => {
let backend = build_backend();
println!("interfaces={}", backend.interfaces().join(","));
println!("capabilities={}", backend.capabilities().join(","));
return;
}
Some("--prepare") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.prepare(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("transport_init_status=transport_init=not-run");
return;
}
Err(err) => {
eprintln!("redbear-wifictl: prepare failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--status") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let backend = build_backend();
println!("interface={}", iface);
println!("status={}", backend.initial_status(&iface).as_str());
println!("link_state={}", backend.initial_link_state(&iface));
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("transport_init_status=transport_init=unknown");
println!("connect_result={}", backend.connect_result(&iface));
return;
}
Some("--scan") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.scan(&iface) {
Ok(results) => {
println!("interface={}", iface);
println!("status=scanning");
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("scan_results={}", results.join(","));
return;
}
Err(err) => {
eprintln!("redbear-wifictl: scan failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--transport-probe") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.transport_probe(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("transport_status={}", status);
return;
}
Err(err) => {
eprintln!(
"redbear-wifictl: transport probe failed for {}: {}",
iface, err
);
process::exit(1);
}
}
}
Some("--init-transport") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.init_transport(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("transport_init_status={}", status);
println!("transport_status={}", backend.transport_status(&iface));
return;
}
Err(err) => {
eprintln!(
"redbear-wifictl: transport init failed for {}: {}",
iface, err
);
process::exit(1);
}
}
}
Some("--activate-nic") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.activate(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("activation_status={}", status);
println!("transport_status={}", backend.transport_status(&iface));
return;
}
Err(err) => {
eprintln!(
"redbear-wifictl: activate-nic failed for {}: {}",
iface, err
);
process::exit(1);
}
}
}
Some("--retry") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.retry(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("link_state=link=retrying");
return;
}
Err(err) => {
eprintln!("redbear-wifictl: retry failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--connect") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let ssid = args.next().unwrap_or_default();
let security = args.next().unwrap_or_else(|| "open".to_string());
let key = args.next().unwrap_or_default();
let mut backend = build_backend();
if let Err(err) = backend.prepare(&iface) {
eprintln!("redbear-wifictl: prepare failed for {}: {}", iface, err);
process::exit(1);
}
if let Err(err) = backend.init_transport(&iface) {
eprintln!(
"redbear-wifictl: transport init failed for {}: {}",
iface, err
);
process::exit(1);
}
if let Err(err) = backend.activate(&iface) {
eprintln!(
"redbear-wifictl: activate-nic failed for {}: {}",
iface, err
);
process::exit(1);
}
let state = backend::InterfaceState {
ssid,
security,
key,
..Default::default()
};
match backend.connect(&iface, &state) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("connect_result={}", backend.connect_result(&iface));
return;
}
Err(err) => {
eprintln!("redbear-wifictl: connect failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--disconnect") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
if let Err(err) = backend.prepare(&iface) {
eprintln!("redbear-wifictl: prepare failed for {}: {}", iface, err);
process::exit(1);
}
if let Err(err) = backend.init_transport(&iface) {
eprintln!(
"redbear-wifictl: transport init failed for {}: {}",
iface, err
);
process::exit(1);
}
if let Err(err) = backend.activate(&iface) {
eprintln!(
"redbear-wifictl: activate-nic failed for {}: {}",
iface, err
);
process::exit(1);
}
match backend.disconnect(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("disconnect_result={}", backend.disconnect_result(&iface));
return;
}
Err(err) => {
eprintln!("redbear-wifictl: disconnect failed for {}: {}", iface, err);
process::exit(1);
}
}
}
_ => {}
}
#[cfg(not(target_os = "redox"))]
{
eprintln!("redbear-wifictl: daemon mode is only supported on Redox; use --probe on host");
process::exit(1);
}
#[cfg(target_os = "redox")]
{
let notify_fd = unsafe { get_init_notify_fd() };
let socket = Socket::create().expect("redbear-wifictl: failed to create scheme socket");
let mut scheme = WifiCtlScheme::new(build_backend());
let mut state = redox_scheme::scheme::SchemeState::new();
notify_scheme_ready(notify_fd, &socket, &mut scheme);
libredox::call::setrens(0, 0).expect("redbear-wifictl: failed to enter null namespace");
info!("redbear-wifictl: registered scheme:wifictl");
while let Some(request) = socket
.next_request(SignalBehavior::Restart)
.expect("redbear-wifictl: failed to read scheme request")
{
match request.kind() {
redox_scheme::RequestKind::Call(request) => {
let response = request.handle_sync(&mut scheme, &mut state);
socket
.write_response(response, SignalBehavior::Restart)
.expect("redbear-wifictl: failed to write response");
}
_ => {}
}
}
process::exit(0);
}
}