fix: pass driver-manager PCI targets to wifi

This commit is contained in:
2026-05-09 01:33:22 +01:00
parent 8ea2ac82ed
commit bf803aed94
2 changed files with 145 additions and 18 deletions
@@ -23,7 +23,9 @@
#define READ_ONCE(var) \
(*((volatile typeof(var) *)&(var)))
#ifndef offsetof
#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
#endif
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
@@ -2,17 +2,50 @@ use std::env;
use std::fs;
use std::path::PathBuf;
use log::{LevelFilter, Metadata, Record, debug, info, warn};
#[cfg(target_os = "redox")]
use redox_driver_sys::memory::{CacheType, MmioProt};
#[cfg(target_os = "redox")]
use redox_driver_sys::pci::PciDevice;
use redox_driver_sys::pci::{PciLocation, PCI_VENDOR_ID_INTEL};
use redox_driver_sys::pci::{PCI_VENDOR_ID_INTEL, PciLocation};
#[cfg(target_os = "redox")]
use std::ffi::CString;
use thiserror::Error;
struct StderrLogger;
impl log::Log for StderrLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log_level_from_env()
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("[{}] redbear-iwlwifi: {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
fn log_level_from_env() -> LevelFilter {
match env::var("REDBEAR_IWLWIFI_LOG").as_deref() {
Ok("trace") => LevelFilter::Trace,
Ok("debug") => LevelFilter::Debug,
Ok("warn") => LevelFilter::Warn,
Ok("error") => LevelFilter::Error,
_ => LevelFilter::Info,
}
}
fn init_logging() {
static LOGGER: StderrLogger = StderrLogger;
let _ = log::set_logger(&LOGGER);
log::set_max_level(log_level_from_env());
}
#[cfg(target_os = "redox")]
use linux_kpi::firmware::{release_firmware, request_firmware, Firmware};
use linux_kpi::firmware::{Firmware, release_firmware, request_firmware};
#[repr(C)]
#[derive(Default)]
@@ -159,6 +192,7 @@ const IWL_CSR_RESET_REG_FLAG_SW_RESET: u32 = 0x00000080;
const IWL_CSR_GP_CNTRL_REG_FLAG_INIT_DONE: u32 = 0x00000004;
fn main() {
init_logging();
let mut args = env::args().skip(1);
let firmware_root = env::var_os("REDBEAR_IWLWIFI_FIRMWARE_ROOT")
.map(PathBuf::from)
@@ -248,6 +282,7 @@ fn run_connect_action(
security: &str,
key: Option<&str>,
) {
let target = target.or_else(default_pcid_target);
match detect_candidates(firmware_root) {
Ok(candidates) => {
let candidate = match select_candidate(candidates, target.as_deref()) {
@@ -303,6 +338,7 @@ fn run_device_action(
action: fn(&Candidate, &PathBuf) -> Result<Vec<String>, DriverError>,
action_name: &str,
) {
let target = target.or_else(default_pcid_target);
match detect_candidates(firmware_root) {
Ok(candidates) => {
let candidate = match select_candidate(candidates, target.as_deref()) {
@@ -331,6 +367,25 @@ fn run_device_action(
}
}
fn default_pcid_target() -> Option<String> {
let raw = env::var("PCID_DEVICE_PATH").ok()?;
normalize_pcid_target(&raw)
}
fn normalize_pcid_target(raw: &str) -> Option<String> {
let entry = raw.strip_prefix("/scheme/pci/").unwrap_or(raw).trim();
if entry.is_empty() {
return None;
}
let parts = entry.splitn(3, "--").collect::<Vec<_>>();
if parts.len() != 3 {
return Some(entry.to_string());
}
Some(format!("{}:{}:{}", parts[0], parts[1], parts[2]))
}
fn select_candidate(
candidates: Vec<Candidate>,
target: Option<&str>,
@@ -393,6 +448,11 @@ fn detect_candidates(firmware_root: &PathBuf) -> Result<Vec<Candidate>, DriverEr
let pci_root = env::var_os("REDBEAR_IWLWIFI_PCI_ROOT")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("/scheme/pci"));
info!(
"scanning PCI root {} for Intel Wi-Fi devices; firmware_root={}",
pci_root.display(),
firmware_root.display()
);
let entries = fs::read_dir(&pci_root)
.map_err(|err| DriverError::Pci(format!("failed to read {}: {err}", pci_root.display())))?;
@@ -400,9 +460,18 @@ fn detect_candidates(firmware_root: &PathBuf) -> Result<Vec<Candidate>, DriverEr
for entry in entries.flatten() {
let config_path = entry.path().join("config");
let Ok(config) = fs::read(&config_path) else {
debug!(
"skipping PCI entry without readable config: {}",
config_path.display()
);
continue;
};
if config.len() < 48 {
debug!(
"skipping short PCI config {} bytes={}",
config_path.display(),
config.len()
);
continue;
}
let vendor_id = u16::from_le_bytes([config[0x00], config[0x01]]);
@@ -410,6 +479,14 @@ fn detect_candidates(firmware_root: &PathBuf) -> Result<Vec<Candidate>, DriverEr
let class_code = config[0x0B];
let subclass = config[0x0A];
if vendor_id != PCI_VENDOR_ID_INTEL || class_code != 0x02 || subclass != 0x80 {
debug!(
"skipping PCI config {} vendor={:04x} device={:04x} class={:02x} subclass={:02x}",
config_path.display(),
vendor_id,
device_id,
class_code,
subclass
);
continue;
}
let subsystem_id = u16::from_le_bytes([config[0x2E], config[0x2F]]);
@@ -425,6 +502,19 @@ fn detect_candidates(firmware_root: &PathBuf) -> Result<Vec<Candidate>, DriverEr
.filter(|candidate| firmware_root.join(candidate).exists())
.cloned();
info!(
"candidate {} device={:04x} subsystem={:04x} family={} selected_ucode={} pnvm={}",
location,
device_id,
subsystem_id,
family,
selected_ucode.as_deref().unwrap_or("missing"),
pnvm_found
.as_deref()
.or(pnvm_candidate.as_deref())
.unwrap_or("none")
);
out.push(Candidate {
location,
config_path,
@@ -570,11 +660,23 @@ fn prepare_candidate(
candidate: &Candidate,
firmware_root: &PathBuf,
) -> Result<Vec<String>, DriverError> {
info!(
"preparing firmware for {} family={} root={}",
candidate.location,
candidate.family,
firmware_root.display()
);
if let Ok(lines) = linux_kpi_prepare_candidate(candidate) {
return Ok(lines);
}
let selected = candidate.selected_ucode.clone().ok_or_else(|| {
warn!(
"missing firmware for {} family={} expected={}",
candidate.location,
candidate.family,
candidate.ucode_candidates.join(",")
);
DriverError::Unsupported(format!(
"missing firmware for {} (expected one of: {})",
candidate.family,
@@ -1338,6 +1440,19 @@ mod tests {
path
}
#[test]
fn normalizes_driver_manager_pci_target() {
assert_eq!(
normalize_pcid_target("/scheme/pci/0000--00--14.3").as_deref(),
Some("0000:00:14.3")
);
assert_eq!(
normalize_pcid_target("0000:00:14.3").as_deref(),
Some("0000:00:14.3")
);
assert_eq!(normalize_pcid_target(" "), None);
}
#[test]
fn detects_intel_candidate() {
let pci = temp_root("rbos-iwlwifi-pci");
@@ -1362,10 +1477,12 @@ mod tests {
let candidates = detect_candidates(&fw).unwrap();
assert_eq!(candidates.len(), 1);
assert_eq!(candidates[0].family, "intel-bz-arrow-lake");
assert!(candidates[0]
assert!(
candidates[0]
.ucode_candidates
.iter()
.any(|name| name.contains("iwlwifi-bz-b0-gf-a0-92.ucode")));
.any(|name| name.contains("iwlwifi-bz-b0-gf-a0-92.ucode"))
);
}
#[test]
@@ -1393,12 +1510,16 @@ mod tests {
let lines = prepare_candidate(&candidate, &fw).unwrap();
assert!(lines.iter().any(|line| line == "status=firmware-ready"));
assert!(lines
assert!(
lines
.iter()
.any(|line| line.contains("selected_pnvm=iwlwifi-bz-b0-gf-a0.pnvm")));
assert!(lines
.any(|line| line.contains("selected_pnvm=iwlwifi-bz-b0-gf-a0.pnvm"))
);
assert!(
lines
.iter()
.any(|line| line.contains("selected_ucode=iwlwifi-bz-b0-gf-a0-92.ucode")));
.any(|line| line.contains("selected_ucode=iwlwifi-bz-b0-gf-a0-92.ucode"))
);
}
#[test]
@@ -1426,9 +1547,11 @@ mod tests {
let lines = init_transport_candidate(&candidate, &fw).unwrap();
assert!(lines.iter().any(|line| line == "status=transport-ready"));
assert!(lines
assert!(
lines
.iter()
.any(|line| line.contains("bar0_addr=host-skipped")));
.any(|line| line.contains("bar0_addr=host-skipped"))
);
}
#[test]
@@ -1456,9 +1579,11 @@ mod tests {
let lines = activate_candidate(&candidate, &fw).unwrap();
assert!(lines.iter().any(|line| line == "status=nic-activated"));
assert!(lines
assert!(
lines
.iter()
.any(|line| line.contains("activation=host-skipped")));
.any(|line| line.contains("activation=host-skipped"))
);
}
#[test]