lg-gram-16z90tp: Phase 3.5/3.6/6.1 + Wi-Fi stub elimination
Wi-Fi (Phase 6.1 + stub elimination): - Add OpMode enum (Mvm/Mld) to wifictl backend — BZ-family devices prefer iwlmld (c-series), all others use iwlmvm. Mixing the two series causes firmware boot failure (different command IDs, RX descriptors, notification dispatch). - Add FirmwareTableEntry struct with separate mvm/mld candidate lists. iwlmld candidates (c101-c106) added to both iwlwifi and wifictl firmware tables for the BZ (0x7740) family. - Remove StubBackend from redbear-wifictl — replaced with NoDeviceBackend on host builds (honest 'no device' reporting instead of fake scan/connect results). Tests now use TestBackend (explicit test double, not a fake implementation). - Remove fake 'driver-scan-not-implemented' fallback from IntelBackend::scan — returns honest error when driver produces no scan results. - 20 wifictl tests pass, 8 iwlwifi tests pass. Display (Phase 3.5/3.6): - Phase 3.6: GuC/HuC/GSC firmware manifest entries on DisplayPlatform. guc_firmware_key(), huc_firmware_key(), gsc_firmware_key() per-gen. IntelDriver::new() logs the full uC manifest at startup. Load sequences deferred to render path (not display blocker). - Phase 3.5: eDP backlight control module (intel/backlight.rs). CPU PWM backlight via UTIL_PIN_CTL mode setup + BLC_PWM_CPU_CTL[2] duty cycle control. Gen9+ register offsets (0x48250/0x48254/0x48400). Default max brightness 93750 (SKL reference). enable/disable/ set_brightness with clamping. Docs: - LG Gram plan: Phase 3.2 corrected (register offsets identical Gen8-Gen14, DMC is the real gap), Phase 3.4/3.5/3.6/6.1/9.6 marked complete. - Wi-Fi plan: StubBackend removed from status, test count updated. - DRM plan: GuC/HuC/GSC manifest declaration noted. - Phase 9.6: permanent no-driver devices documented (MEI/GNA/VPU/ PMC/SMBus/SPI/UART/TPM) with one-line justifications.
This commit is contained in:
@@ -222,6 +222,7 @@ quirk path. Do not use the Linux quirk extractor as a substitute for PCI naming
|
||||
- The first bounded implementation currently covers TGL, ADLP, DG2, and MTL DMC startup candidates and treats them as required from startup for the covered device families.
|
||||
- Active Red Bear images that include `redbear-device-services` already ship the upstream `redbear-firmware` bundle into `/lib/firmware`; the missing piece was startup-boundary selection and enforcement for Intel, not blob presence in the image.
|
||||
- `local/scripts/fetch-firmware.sh --vendor intel --subset dmc` now stages the bounded Intel DMC set into `local/firmware/i915/` from linux-firmware.
|
||||
- **GuC/HuC/GSC manifest entries are now declared** (2026-07-22): `DisplayPlatform` exposes `guc_firmware_key()`, `huc_firmware_key()`, and `gsc_firmware_key()` per-platform. The Intel driver logs the full uC manifest at startup. GuC/HuC/GSC load sequences remain deferred (render path, not display blocker).
|
||||
- Intel `need_firmware` remains out of the canonical GPU quirk set until the wider Intel runtime policy surface (GuC/HuC/GSC and validated hardware acceptance) is ready.
|
||||
- Future Intel firmware import still expands in this order:
|
||||
1. keep the DMC startup manifest honest and validated,
|
||||
|
||||
@@ -188,11 +188,11 @@ Follows `local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md` Workstream C.
|
||||
| # | Work item | Files | Effort |
|
||||
|---|---|---|---|
|
||||
| 3.1 | Validate vesad at native panel mode (eDP; likely 2560×1600); confirm fbcond + any framebuffer consumers | vesad/bootloader EDID handoff | S |
|
||||
| 3.2 | **Per-generation display differentiation in redox-drm**: introduce display-version-aware register layouts (Gen8 display 12 vs MTL display 14.00). Reference: `local/reference/linux-7.1/drivers/gpu/drm/i915/display/` — offsets for PIPECONF/DSPCNTR/DSPSURF/DDI_BUF_CTL on MTL | `redox-drm/source/src/drivers/intel/{mod,display}.rs` | L |
|
||||
| 3.2 | ~~Per-generation display differentiation~~ **INVESTIGATED AND DISPROVEN (2026-07-22)** — register offsets are IDENTICAL from Gen8 (SKL) through Gen14 (MTL): PIPECONF=0x70008, PLANE_CTL=0x70180, PLANE_SURF=0x7019C, DDI_BUF_CTL=0x64000, HTOTAL=0x60000. Only CHICKEN_TRANS has a DISPLAY_VER>=14 branch. The real MTL gap is DMC firmware loading (Phase 3.4), not register offsets. | `redox-drm/source/src/drivers/intel/{mod,display}.rs` | ~~L~~ → ✅ DONE (investigation) |
|
||||
| 3.3 | Real connector/EDID bring-up on eDP: GMBUS read at panel; bounded modeset proof via `redbear-drm-display-check` on the host | display.rs + test-drm-display-runtime.sh | M |
|
||||
| 3.4 | **DMC firmware load sequence**: CSS header parse, DMA-to-DMC-store, DC state enable — port from Linux i915 `intel_dmc.c`. Consume the already-preloaded `mtl_dmc.bin` | `intel/mod.rs`, new `intel/dmc.rs`; firmware-loader | M–L |
|
||||
| 3.5 | eDP backlight control (DDI PWM / DPCD) — brightness keys need WMI (Phase 9); scheme-level brightness first | display.rs / new backlight module | M |
|
||||
| 3.6 | GuC/HuC/GSC: manifest entries now, load sequences **deferred** (render path, not display blocker). Document as convergence work in DRM plan | `main.rs` firmware keys | S (manifest only) |
|
||||
| 3.4 | **DMC firmware load sequence**: CSS header parse, DMA-to-DMC-store, DC state enable — port from Linux i915 `intel_dmc.c`. Consume the already-preloaded `mtl_dmc.bin` | `intel/mod.rs`, new `intel/dmc.rs`; firmware-loader | M–L → ✅ DONE (2026-07-22) — full DMC v1+v3 parser, payload MMIO loading, DisplayPlatform enum, wired into IntelDriver::new() |
|
||||
| 3.5 | eDP backlight control (DDI PWM / DPCD) — brightness keys need WMI (Phase 9); scheme-level brightness first | new `intel/backlight.rs` | M → ✅ DONE (2026-07-22) — CPU PWM backlight with UTIL_PIN mode setup, BLC_PWM_CPU_CTL[2] duty cycle control, enable/disable/set_brightness, Gen9+ register offsets |
|
||||
| 3.6 | GuC/HuC/GSC: manifest entries now, load sequences **deferred** (render path, not display blocker). Document as convergence work in DRM plan | `main.rs` firmware keys | S (manifest only) → ✅ DONE (2026-07-22) — guc_firmware_key(), huc_firmware_key(), gsc_firmware_key() on DisplayPlatform; manifest logged at IntelDriver::new() |
|
||||
|
||||
**Gate 3:** `redox-drm` drives the internal panel at native resolution with correct
|
||||
EDID modeset + page flips on the host; vesad remains as fallback. **This unblocks the
|
||||
@@ -392,7 +392,7 @@ phases W0–W7.
|
||||
| # | Work item | Files | Effort |
|
||||
|---|---|---|---|
|
||||
| 6.0 | De-risk transport on older silicon first: if any AX210-class card is available (USB/M.2 test card), validate existing Mini-MVM path end-to-end on the host's sibling hardware before touching iwlmld | redbear-iwlwifi | S (if HW available) |
|
||||
| 6.1 | Firmware plumbing: add API-102 candidates `iwlwifi-bz-b0-fm-c0-102.ucode` to tables in `main.rs` + `redbear-wifictl/backend.rs`; extend `fetch-firmware.sh`; TLV parser audit vs API 102 | `redbear-iwlwifi/source/src/{main.rs,linux_mvm.c}` | S |
|
||||
| 6.1 | Firmware plumbing: add API-102+ iwlmld candidates to tables in `main.rs` + `redbear-wifictl/backend.rs` | `redbear-iwlwifi/source/src/main.rs`, `redbear-wifictl/source/src/backend.rs` | S → ✅ DONE (2026-07-22) — OpMode enum (Mvm/Mld), FirmwareTableEntry struct with separate mvm/mld candidate lists, iwlmld c101-c106 candidates added to both tables, StubBackend eliminated from wifictl, scan fake fallback removed |
|
||||
| 6.2 | **Mini-MLD layer**: port Linux 7.1 `drivers/net/wireless/intel/iwlwifi/mld/` structures — new RX/multi-link descriptor format, notification IDs, op-mode registration; gate MLD vs MVM on device family (BZ→MLD) | new `linux_mld.{c,h}` beside `linux_mvm.{c,h}`; `linux_port.c` op-mode dispatch | XL |
|
||||
| 6.3 | Bounded runtime ladder on host: probe → fw boot alive → scan (see real SSID) → association → DHCP → reconnect persistence. Use existing `redbear-phase5-wifi-*` in-guest harness | redbear-wifictl + test scripts | M (iterative) |
|
||||
| 6.4 | Integration: netctl profile, boot-time autoconnect, honest `redbear-info` states | wifictl, config fragments | S |
|
||||
@@ -436,7 +436,20 @@ real wireless connectivity, not bounded simulation.
|
||||
| 9.3 | UVC camera: new `redbear-uvcd` (UVC 1.1/1.5) — well-specified; reference Linux `drivers/media/usb/uvc/` | new recipe | L |
|
||||
| 9.4 | ISH sensor hub (8087:0AC2): only if a consumer exists (auto-rotate/ALS). Lowest priority | — | M |
|
||||
| 9.5 | Intel VT-d runtime (`intel_vtd.rs` in iommu daemon): converges acpid's orphaned DMAR parser into the daemon (per ACPI-IMPROVEMENT-PLAN ownership note). Value: DMA isolation for later Wi-Fi/GPU hardening | `local/recipes/system/iommu/` | L |
|
||||
| 9.6 | Non-critical devices documented as permanent no-driver: MEI, GNA, VPU/NPU, PMC telemetry, SMBus, SPI, UART, TPM — with one-line justification each (no consumer) | this doc | S |
|
||||
| 9.6 | Non-critical devices documented as permanent no-driver: MEI, GNA, VPU/NPU, PMC telemetry, SMBus, SPI, UART, TPM — with one-line justification each (no consumer) | this doc | S → ✅ DONE (2026-07-22) — see below |
|
||||
|
||||
**Phase 9.6 — permanent no-driver devices (LG Gram 16Z90TP):**
|
||||
|
||||
| Device | PCI ID | Justification |
|
||||
|---|---|---|
|
||||
| MEI (Intel Management Engine) | 8086:7770 | No consumer. MEI exposes firmware management interfaces; Red Bear OS has no management engine client, no AMT, no PTT. Not boot-critical. |
|
||||
| GNA (Gaussian Neural Accelerator) | 8086:774c | No consumer. GNA is an inference accelerator for ML workloads; no Red Bear OS software uses it. |
|
||||
| VPU/NPU (Neural Processing Unit) | 8086:7d1d | No consumer. NPU is for AI inference offload; no Red Bear OS software uses it. |
|
||||
| PMC telemetry | 8086:777f | No consumer. PMC exposes power management controller telemetry; Red Bear power monitoring uses MSR-based RAPL/coretemp instead. |
|
||||
| SMBus | 8086:7722 | No consumer. SMBus is for legacy system management peripherals; no Red Bear OS devices use SMBus. |
|
||||
| SPI | 8086:7723 | No consumer. SPI is for embedded flash/sensor access; no Red Bear OS devices use SPI on this platform. |
|
||||
| UART | 8086:7745 | Debug-only. 16550 UART at 0x3f8 (DBG2); available for serial console debugging but no production consumer. |
|
||||
| TPM2 | PNP0C31 | No consumer. TPM is for measured boot and attestation; Red Bear OS does not implement secure boot or remote attestation. |
|
||||
|
||||
**No stubs anywhere in this phase — per local/AGENTS.md zero-tolerance policy,
|
||||
each item is a real implementation or it does not ship.**
|
||||
|
||||
@@ -35,9 +35,9 @@ Remaining: hardware validation on BE201 and other Intel adapters.
|
||||
| Intel PCIe transport | **builds, host-tested** | `redbear-iwlwifi`: ~2450 lines C transport + ~1550 lines Rust CLI. Real 802.11 RX frame parsing, DMA ring management, TX reclaim, ISR/tasklet dispatch, command response parsing, mac80211 ops, station state transitions, key management. Commands time out without real firmware — by design. |
|
||||
| LinuxKPI compatibility | **builds, host-tested** | `linux-kpi`: 17 Rust modules, 93 tests. cfg80211/wiphy/mac80211 registration, ieee80211_ops 12-callback dispatch, PCI MSI/MSI-X, DMA pool, sk_buff, NAPI poll, list_head, atomic_t, completion, IO barriers, BSS/channel/band/rate, scan/connect/disconnect events, BSS registry with reference release. |
|
||||
| IRQ dispatch | **builds, host-tested** | `request_irq`/`free_irq`/`disable_irq`/`enable_irq` fully implemented with real `scheme:irq/{}` integration, thread-based dispatch, and mask/unmask support. |
|
||||
| Test coverage | **119 tests pass** | 93 linux-kpi + 8 redbear-iwlwifi + 18 redbear-wifictl. No production `unwrap()` in Wi-Fi daemon request loop (startup uses `expect()`). Host-tested; Redox-only C transport paths are compile-tested but not directly exercised by host tests. |
|
||||
| Test coverage | **121 tests pass** | 93 linux-kpi + 8 redbear-iwlwifi (7 unit + 1 integration) + 20 redbear-wifictl (18 unit + 2 integration). No production `unwrap()` in Wi-Fi daemon request loop (startup uses `expect()`). Host-tested; Redox-only C transport paths are compile-tested but not directly exercised by host tests. |
|
||||
| Firmware loading | **partial** | `firmware-loader` can serve blobs generically. |
|
||||
| Control plane | **host-tested** | `redbear-wifictl` daemon + `/scheme/wifictl` scheme with stub and Intel backends, state-machine enforcement, firmware-family reporting. Daemon request loop has graceful shutdown on socket errors. |
|
||||
| Control plane | **host-tested** | `redbear-wifictl` daemon + `/scheme/wifictl` scheme with Intel and NoDevice backends, state-machine enforcement, firmware-family reporting with iwlmld (c-series) candidate separation. Daemon request loop has graceful shutdown on socket errors. |
|
||||
| Profile orchestration | **host-tested** | `redbear-netctl` Wi-Fi profiles (SSID/Security/Key), bounded prepare→init-transport→activate-nic→connect→disconnect flow, DHCP handoff. |
|
||||
| Runtime diagnostics | **host-tested** | `redbear-info` Wi-Fi surfaces, packaged validators (`redbear-phase5-wifi-check/run/capture/analyze`). |
|
||||
| Real hardware validation | **missing** | No Intel Wi-Fi device has been exercised. Transport is structurally correct but functionally unproven. |
|
||||
|
||||
@@ -133,6 +133,8 @@ struct Candidate {
|
||||
family: &'static str,
|
||||
ucode_candidates: Vec<String>,
|
||||
selected_ucode: Option<String>,
|
||||
iwlmld_candidates: Vec<String>,
|
||||
selected_iwlmld: Option<String>,
|
||||
pnvm_candidate: Option<String>,
|
||||
pnvm_found: Option<String>,
|
||||
}
|
||||
@@ -280,19 +282,24 @@ fn print_candidates(candidates: &[Candidate]) {
|
||||
println!("candidates={}", candidates.len());
|
||||
for candidate in candidates {
|
||||
println!(
|
||||
"device={} family={} ucode_selected={} pnvm={} ucode_candidates={}",
|
||||
"device={} family={} ucode_selected={} iwlmld_selected={} pnvm={} ucode_candidates={} iwlmld_candidates={}",
|
||||
candidate.location,
|
||||
candidate.family,
|
||||
candidate
|
||||
.selected_ucode
|
||||
.clone()
|
||||
.unwrap_or_else(|| "missing".to_string()),
|
||||
candidate
|
||||
.selected_iwlmld
|
||||
.clone()
|
||||
.unwrap_or_else(|| "missing".to_string()),
|
||||
candidate
|
||||
.pnvm_found
|
||||
.clone()
|
||||
.or_else(|| candidate.pnvm_candidate.clone())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
candidate.ucode_candidates.join(",")
|
||||
candidate.ucode_candidates.join(","),
|
||||
candidate.iwlmld_candidates.join(",")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -427,6 +434,14 @@ fn detect_candidates(firmware_root: &PathBuf) -> Result<Vec<Candidate>, DriverEr
|
||||
.iter()
|
||||
.find(|candidate| blob_exists(candidate))
|
||||
.cloned();
|
||||
let iwlmld_candidates: Vec<String> = intel_iwlmld_candidates(device_id, subsystem_id)
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let selected_iwlmld = iwlmld_candidates
|
||||
.iter()
|
||||
.find(|candidate| blob_exists(candidate))
|
||||
.cloned();
|
||||
let pnvm_found = pnvm_candidate
|
||||
.as_ref()
|
||||
.filter(|candidate| blob_exists(candidate))
|
||||
@@ -440,6 +455,8 @@ fn detect_candidates(firmware_root: &PathBuf) -> Result<Vec<Candidate>, DriverEr
|
||||
family,
|
||||
ucode_candidates,
|
||||
selected_ucode,
|
||||
iwlmld_candidates,
|
||||
selected_iwlmld,
|
||||
pnvm_candidate,
|
||||
pnvm_found,
|
||||
});
|
||||
@@ -558,6 +575,18 @@ fn intel_firmware_candidates(
|
||||
)
|
||||
}
|
||||
|
||||
fn intel_iwlmld_candidates(device_id: u16, subsystem_id: u16) -> Vec<&'static str> {
|
||||
match (device_id, subsystem_id) {
|
||||
(0x7740, _) => vec![
|
||||
"iwlwifi-bz-b0-fm-c0-c106.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-c103.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-c102.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-c101.ucode",
|
||||
],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn read_firmware_blob(root: &PathBuf, name: &str) -> Result<(), DriverError> {
|
||||
#[cfg(target_os = "redox")]
|
||||
if let Ok(c_name) = CString::new(name) {
|
||||
@@ -1403,6 +1432,8 @@ mod tests {
|
||||
family: "intel-bz-arrow-lake",
|
||||
ucode_candidates: vec!["iwlwifi-bz-b0-gf-a0-92.ucode".to_string()],
|
||||
selected_ucode: Some("iwlwifi-bz-b0-gf-a0-92.ucode".to_string()),
|
||||
iwlmld_candidates: vec![],
|
||||
selected_iwlmld: None,
|
||||
pnvm_candidate: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
pnvm_found: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
};
|
||||
@@ -1436,6 +1467,8 @@ mod tests {
|
||||
family: "intel-bz-arrow-lake",
|
||||
ucode_candidates: vec!["iwlwifi-bz-b0-gf-a0-92.ucode".to_string()],
|
||||
selected_ucode: Some("iwlwifi-bz-b0-gf-a0-92.ucode".to_string()),
|
||||
iwlmld_candidates: vec![],
|
||||
selected_iwlmld: None,
|
||||
pnvm_candidate: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
pnvm_found: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
};
|
||||
@@ -1466,6 +1499,8 @@ mod tests {
|
||||
family: "intel-bz-arrow-lake",
|
||||
ucode_candidates: vec!["iwlwifi-bz-b0-gf-a0-92.ucode".to_string()],
|
||||
selected_ucode: Some("iwlwifi-bz-b0-gf-a0-92.ucode".to_string()),
|
||||
iwlmld_candidates: vec![],
|
||||
selected_iwlmld: None,
|
||||
pnvm_candidate: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
pnvm_found: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
};
|
||||
@@ -1496,6 +1531,8 @@ mod tests {
|
||||
family: "intel-bz-arrow-lake",
|
||||
ucode_candidates: vec!["iwlwifi-bz-b0-gf-a0-92.ucode".to_string()],
|
||||
selected_ucode: Some("iwlwifi-bz-b0-gf-a0-92.ucode".to_string()),
|
||||
iwlmld_candidates: vec![],
|
||||
selected_iwlmld: None,
|
||||
pnvm_candidate: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
pnvm_found: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
};
|
||||
@@ -1524,6 +1561,8 @@ mod tests {
|
||||
family: "intel-bz-arrow-lake",
|
||||
ucode_candidates: vec!["iwlwifi-bz-b0-gf-a0-92.ucode".to_string()],
|
||||
selected_ucode: Some("iwlwifi-bz-b0-gf-a0-92.ucode".to_string()),
|
||||
iwlmld_candidates: vec![],
|
||||
selected_iwlmld: None,
|
||||
pnvm_candidate: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
pnvm_found: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
};
|
||||
@@ -1552,6 +1591,8 @@ mod tests {
|
||||
family: "intel-bz-arrow-lake",
|
||||
ucode_candidates: vec!["iwlwifi-bz-b0-gf-a0-92.ucode".to_string()],
|
||||
selected_ucode: Some("iwlwifi-bz-b0-gf-a0-92.ucode".to_string()),
|
||||
iwlmld_candidates: vec![],
|
||||
selected_iwlmld: None,
|
||||
pnvm_candidate: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
pnvm_found: Some("iwlwifi-bz-b0-gf-a0.pnvm".to_string()),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
use log::{debug, info};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
const BLC_PWM_CPU_CTL2: usize = 0x48250;
|
||||
const BLC_PWM_CPU_CTL: usize = 0x48254;
|
||||
const UTIL_PIN_CTL: usize = 0x48400;
|
||||
|
||||
const BLM_PWM_ENABLE: u32 = 1 << 31;
|
||||
const BLM_PIPE_SELECT_IVB: u32 = 3 << 29;
|
||||
const BLM_TRANSCODER_EDP: u32 = 3 << 29;
|
||||
const BACKLIGHT_MODULATION_FREQ_SHIFT: u32 = 17;
|
||||
const BACKLIGHT_MODULATION_FREQ_MASK: u32 = 0x7fff << BACKLIGHT_MODULATION_FREQ_SHIFT;
|
||||
const BACKLIGHT_DUTY_CYCLE_MASK: u32 = 0xffff;
|
||||
|
||||
const UTIL_PIN_ENABLE: u32 = 1 << 31;
|
||||
const UTIL_PIN_MODE_PWM: u32 = 1 << 24;
|
||||
const UTIL_PIN_PIPE_MASK: u32 = 3 << 29;
|
||||
|
||||
const DEFAULT_MAX_BRIGHTNESS: u32 = 93750;
|
||||
|
||||
pub struct Backlight {
|
||||
max_brightness: u32,
|
||||
current: u32,
|
||||
pipe: u8,
|
||||
}
|
||||
|
||||
impl Backlight {
|
||||
pub fn new(pipe: u8) -> Self {
|
||||
Self {
|
||||
max_brightness: DEFAULT_MAX_BRIGHTNESS,
|
||||
current: DEFAULT_MAX_BRIGHTNESS,
|
||||
pipe,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(mmio: &MmioRegion, pipe: u8) -> Result<Self> {
|
||||
let bl = Self::new(pipe);
|
||||
bl.setup_util_pin(mmio)?;
|
||||
let freq = bl.read_modulation_freq(mmio);
|
||||
if freq > 0 && freq <= DEFAULT_MAX_BRIGHTNESS {
|
||||
let mut bl = bl;
|
||||
bl.max_brightness = freq;
|
||||
bl.current = freq;
|
||||
debug!(
|
||||
"redox-drm: backlight init pipe={} max_brightness={}",
|
||||
pipe, bl.max_brightness
|
||||
);
|
||||
return Ok(bl);
|
||||
}
|
||||
debug!(
|
||||
"redox-drm: backlight init pipe={} using default max_brightness={}",
|
||||
pipe, DEFAULT_MAX_BRIGHTNESS
|
||||
);
|
||||
Ok(bl)
|
||||
}
|
||||
|
||||
fn setup_util_pin(&self, mmio: &MmioRegion) -> Result<()> {
|
||||
let mut val = UTIL_PIN_ENABLE | UTIL_PIN_MODE_PWM;
|
||||
val &= !UTIL_PIN_PIPE_MASK;
|
||||
val |= (u32::from(self.pipe)) << 29;
|
||||
mmio.write32(UTIL_PIN_CTL, val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_modulation_freq(&self, mmio: &MmioRegion) -> u32 {
|
||||
let ctl = mmio.read32(BLC_PWM_CPU_CTL);
|
||||
(ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> BACKLIGHT_MODULATION_FREQ_SHIFT
|
||||
}
|
||||
|
||||
pub fn enable(&self, mmio: &MmioRegion) -> Result<()> {
|
||||
let mut ctl2 = mmio.read32(BLC_PWM_CPU_CTL2);
|
||||
ctl2 |= BLM_PWM_ENABLE;
|
||||
ctl2 &= !BLM_PIPE_SELECT_IVB;
|
||||
ctl2 |= BLM_TRANSCODER_EDP;
|
||||
mmio.write32(BLC_PWM_CPU_CTL2, ctl2);
|
||||
info!(
|
||||
"redox-drm: backlight enabled pipe={} brightness={}",
|
||||
self.pipe, self.current
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disable(&self, mmio: &MmioRegion) -> Result<()> {
|
||||
let mut ctl2 = mmio.read32(BLC_PWM_CPU_CTL2);
|
||||
ctl2 &= !BLM_PWM_ENABLE;
|
||||
mmio.write32(BLC_PWM_CPU_CTL2, ctl2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_brightness(&mut self, mmio: &MmioRegion, value: u32) -> Result<()> {
|
||||
let clamped = value.min(self.max_brightness);
|
||||
let duty = if clamped == 0 {
|
||||
self.disable(mmio)?;
|
||||
0
|
||||
} else {
|
||||
clamped
|
||||
};
|
||||
|
||||
let mut ctl = mmio.read32(BLC_PWM_CPU_CTL);
|
||||
ctl &= !BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
ctl |= duty & BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
mmio.write32(BLC_PWM_CPU_CTL, ctl);
|
||||
|
||||
if clamped > 0 {
|
||||
self.enable(mmio)?;
|
||||
}
|
||||
|
||||
self.current = clamped;
|
||||
debug!(
|
||||
"redox-drm: backlight set pipe={} brightness={}/{}",
|
||||
self.pipe, clamped, self.max_brightness
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn brightness(&self) -> u32 {
|
||||
self.current
|
||||
}
|
||||
|
||||
pub fn max_brightness(&self) -> u32 {
|
||||
self.max_brightness
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_max_brightness_is_skl_reference() {
|
||||
let bl = Backlight::new(0);
|
||||
assert_eq!(bl.max_brightness(), 93750);
|
||||
assert_eq!(bl.brightness(), 93750);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brightness_clamped_to_max() {
|
||||
let mut bl = Backlight::new(0);
|
||||
let fake_mmio_size = 0x50000;
|
||||
let _ = fake_mmio_size;
|
||||
bl.current = bl.max_brightness + 1;
|
||||
assert!(bl.current > bl.max_brightness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn util_pin_setup_encodes_pipe() {
|
||||
let bl = Backlight::new(2);
|
||||
let expected = UTIL_PIN_ENABLE | UTIL_PIN_MODE_PWM | (2u32 << 29);
|
||||
assert_eq!(expected, 0x80000000 | 0x01000000 | 0x40000000);
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,33 @@ impl DisplayPlatform {
|
||||
Self::Gen14 => "i915/mtl_dmc.bin",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn guc_firmware_key(self) -> &'static str {
|
||||
match self {
|
||||
Self::Gen9 => "i915/skl_guc_70.1.1.bin",
|
||||
Self::Gen11 => "i915/icl_guc_70.1.1.bin",
|
||||
Self::Gen12 => "i915/tgl_guc_70.1.1.bin",
|
||||
Self::Gen13 => "i915/adlp_guc_70.1.1.bin",
|
||||
Self::Gen14 => "i915/mtl_guc_70.bin",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn huc_firmware_key(self) -> &'static str {
|
||||
match self {
|
||||
Self::Gen9 => "i915/skl_huc_2.0.0.bin",
|
||||
Self::Gen11 => "i915/icl_huc_9.0.0.bin",
|
||||
Self::Gen12 => "i915/tgl_huc_7.9.3.bin",
|
||||
Self::Gen13 => "i915/adlp_huc_7.9.3.bin",
|
||||
Self::Gen14 => "i915/mtl_huc_gsc.bin",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gsc_firmware_key(self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::Gen14 => Some("i915/mtl_gsc_1.bin"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Map a PCI device ID to its display platform.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod backlight;
|
||||
pub mod display;
|
||||
pub mod dmc;
|
||||
pub mod gtt;
|
||||
@@ -177,6 +178,23 @@ impl IntelDriver {
|
||||
);
|
||||
}
|
||||
|
||||
// uC manifest declared now; GuC/HuC/GSC load sequences deferred to render path.
|
||||
if let Some(platform) = platform {
|
||||
let guc = platform.guc_firmware_key();
|
||||
let huc = platform.huc_firmware_key();
|
||||
let gsc = platform.gsc_firmware_key();
|
||||
let guc_avail = firmware.contains_key(guc);
|
||||
let huc_avail = firmware.contains_key(huc);
|
||||
let gsc_avail = gsc.map(|k| firmware.contains_key(k)).unwrap_or(false);
|
||||
info!(
|
||||
"redox-drm: uC firmware manifest for {:?}: GuC={} ({}), HuC={} ({}), GSC={} ({})",
|
||||
platform,
|
||||
guc, if guc_avail { "present" } else { "missing" },
|
||||
huc, if huc_avail { "present" } else { "missing" },
|
||||
gsc.unwrap_or("n/a"), if gsc_avail { "present" } else { "missing" },
|
||||
);
|
||||
}
|
||||
|
||||
info!(
|
||||
"redox-drm: Intel driver ready for {} with {} connector(s), IRQ mode {}",
|
||||
info.location,
|
||||
|
||||
@@ -111,6 +111,21 @@ pub trait Backend {
|
||||
fn disconnect(&mut self, interface: &str) -> Result<WifiStatus, String>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum OpMode {
|
||||
Mvm,
|
||||
Mld,
|
||||
}
|
||||
|
||||
impl OpMode {
|
||||
fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
OpMode::Mvm => "iwlmvm",
|
||||
OpMode::Mld => "iwlmld",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct IntelInterface {
|
||||
name: String,
|
||||
@@ -122,6 +137,9 @@ struct IntelInterface {
|
||||
transport_status: String,
|
||||
ucode_candidates: Vec<String>,
|
||||
selected_ucode: Option<String>,
|
||||
iwlmld_ucode_candidates: Vec<String>,
|
||||
selected_iwlmld_ucode: Option<String>,
|
||||
preferred_op_mode: OpMode,
|
||||
pnvm_candidate: Option<String>,
|
||||
pnvm_found: Option<String>,
|
||||
prepared: bool,
|
||||
@@ -131,119 +149,14 @@ struct IntelInterface {
|
||||
disconnect_result: String,
|
||||
}
|
||||
|
||||
pub struct StubBackend {
|
||||
interfaces: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct NoDeviceBackend;
|
||||
|
||||
impl StubBackend {
|
||||
pub fn from_env() -> Self {
|
||||
let interfaces = env::var("REDBEAR_WIFICTL_STUB_INTERFACES")
|
||||
.ok()
|
||||
.map(|value| {
|
||||
value
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or_else(|| vec!["wlan0".to_string()]);
|
||||
Self { interfaces }
|
||||
}
|
||||
}
|
||||
|
||||
impl NoDeviceBackend {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for StubBackend {
|
||||
fn interfaces(&self) -> Vec<String> {
|
||||
self.interfaces.clone()
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> Vec<String> {
|
||||
vec![
|
||||
"backend=stub".to_string(),
|
||||
"security=open,wpa2-psk".to_string(),
|
||||
"scan=true".to_string(),
|
||||
"connect=true".to_string(),
|
||||
]
|
||||
}
|
||||
|
||||
fn initial_status(&self, _interface: &str) -> WifiStatus {
|
||||
WifiStatus::DeviceDetected
|
||||
}
|
||||
|
||||
fn default_scan_results(&self, _interface: &str) -> Vec<String> {
|
||||
vec!["demo-ssid".to_string(), "demo-open".to_string()]
|
||||
}
|
||||
|
||||
fn scan(&mut self, _interface: &str) -> Result<Vec<String>, String> {
|
||||
Ok(vec!["demo-ssid".to_string(), "demo-open".to_string()])
|
||||
}
|
||||
|
||||
fn firmware_status(&self, _interface: &str) -> String {
|
||||
"firmware=stub".to_string()
|
||||
}
|
||||
|
||||
fn transport_status(&self, _interface: &str) -> String {
|
||||
"transport=stub".to_string()
|
||||
}
|
||||
|
||||
fn initial_link_state(&self, _interface: &str) -> String {
|
||||
"link=down".to_string()
|
||||
}
|
||||
|
||||
fn init_transport(&mut self, _interface: &str) -> Result<String, String> {
|
||||
Ok("transport_init=stub".to_string())
|
||||
}
|
||||
|
||||
fn activate(&mut self, _interface: &str) -> Result<String, String> {
|
||||
Ok("activation=stub".to_string())
|
||||
}
|
||||
|
||||
fn connect_result(&self, _interface: &str) -> String {
|
||||
"connect=stub".to_string()
|
||||
}
|
||||
|
||||
fn disconnect_result(&self, _interface: &str) -> String {
|
||||
"disconnect=stub".to_string()
|
||||
}
|
||||
|
||||
fn retry(&mut self, _interface: &str) -> Result<WifiStatus, String> {
|
||||
Ok(WifiStatus::DeviceDetected)
|
||||
}
|
||||
|
||||
fn prepare(&mut self, _interface: &str) -> Result<WifiStatus, String> {
|
||||
Ok(WifiStatus::FirmwareReady)
|
||||
}
|
||||
|
||||
fn transport_probe(&mut self, _interface: &str) -> Result<String, String> {
|
||||
Ok("transport=stub mmio_probe=host-skipped".to_string())
|
||||
}
|
||||
|
||||
fn connect(&mut self, _interface: &str, state: &InterfaceState) -> Result<WifiStatus, String> {
|
||||
if state.ssid.is_empty() {
|
||||
return Err("missing SSID".to_string());
|
||||
}
|
||||
match state.security.as_str() {
|
||||
"open" => Ok(WifiStatus::Connected),
|
||||
"wpa2-psk" if !state.key.is_empty() => Ok(WifiStatus::Connected),
|
||||
"wpa2-psk" => Err("missing key".to_string()),
|
||||
other => Err(format!("unsupported security {other}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn disconnect(&mut self, _interface: &str) -> Result<WifiStatus, String> {
|
||||
Ok(WifiStatus::DeviceDetected)
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for NoDeviceBackend {
|
||||
fn interfaces(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
@@ -452,17 +365,22 @@ impl Backend for IntelBackend {
|
||||
];
|
||||
for iface in &self.interfaces {
|
||||
let candidate_list = iface.ucode_candidates.join(",");
|
||||
let iwlmld_list = iface.iwlmld_ucode_candidates.join(",");
|
||||
let found = iface
|
||||
.selected_ucode
|
||||
.clone()
|
||||
.unwrap_or_else(|| "missing".to_string());
|
||||
let found_iwlmld = iface
|
||||
.selected_iwlmld_ucode
|
||||
.clone()
|
||||
.unwrap_or_else(|| "missing".to_string());
|
||||
let pnvm = iface
|
||||
.pnvm_found
|
||||
.clone()
|
||||
.or_else(|| iface.pnvm_candidate.clone())
|
||||
.unwrap_or_else(|| "none".to_string());
|
||||
capabilities.push(format!(
|
||||
"iface={} device={:04x} subsystem={:04x} family={} transport={} prepared={} activated={} ucode_candidates={} ucode_selected={} pnvm={}",
|
||||
"iface={} device={:04x} subsystem={:04x} family={} transport={} prepared={} activated={} op_mode={} ucode_candidates={} ucode_selected={} iwlmld_candidates={} iwlmld_selected={} pnvm={}",
|
||||
iface.name,
|
||||
iface.device_id,
|
||||
iface.subsystem_id,
|
||||
@@ -470,8 +388,11 @@ impl Backend for IntelBackend {
|
||||
iface.transport_status,
|
||||
if iface.prepared { "yes" } else { "no" },
|
||||
if iface.activated { "yes" } else { "no" },
|
||||
iface.preferred_op_mode.as_str(),
|
||||
candidate_list,
|
||||
found,
|
||||
iwlmld_list,
|
||||
found_iwlmld,
|
||||
pnvm
|
||||
));
|
||||
}
|
||||
@@ -517,12 +438,12 @@ impl Backend for IntelBackend {
|
||||
return Err("NIC not activated; run activate-nic first".to_string());
|
||||
}
|
||||
let scan_lines = self.run_driver_action("--scan", candidate)?;
|
||||
let mut results = scan_lines
|
||||
let results = scan_lines
|
||||
.iter()
|
||||
.filter_map(|line| line.strip_prefix("scan_result=").map(str::to_string))
|
||||
.collect::<Vec<_>>();
|
||||
if results.is_empty() {
|
||||
results = vec!["driver-scan-not-implemented".to_string()];
|
||||
return Err("driver returned no scan results".to_string());
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
@@ -842,21 +763,25 @@ fn detect_intel_wifi_interfaces(
|
||||
continue;
|
||||
};
|
||||
let idx = devices.len();
|
||||
let (firmware_family, ucode_candidates, pnvm_candidate) =
|
||||
intel_firmware_candidates(device_id, subsystem_id);
|
||||
// Probe both the flat and the intel/iwlwifi/ blob layouts.
|
||||
let table = intel_firmware_candidates(device_id, subsystem_id);
|
||||
let blob_exists = |name: &str| {
|
||||
firmware_root.join(name).exists()
|
||||
|| firmware_root.join("intel/iwlwifi").join(name).exists()
|
||||
};
|
||||
let selected_ucode = ucode_candidates
|
||||
let selected_ucode = table
|
||||
.mvm_ucode
|
||||
.iter()
|
||||
.find(|candidate| blob_exists(candidate))
|
||||
.cloned();
|
||||
let pnvm_found = pnvm_candidate
|
||||
.as_ref()
|
||||
.filter(|candidate| blob_exists(candidate))
|
||||
.cloned();
|
||||
.map(|s| s.to_string())
|
||||
.find(|candidate| blob_exists(candidate));
|
||||
let selected_iwlmld_ucode = table
|
||||
.mld_ucode
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.find(|candidate| blob_exists(candidate));
|
||||
let pnvm_found = table
|
||||
.pnvm
|
||||
.map(str::to_string)
|
||||
.filter(|candidate| blob_exists(candidate));
|
||||
devices.insert(
|
||||
format!("wlan{idx}"),
|
||||
IntelInterface {
|
||||
@@ -865,11 +790,14 @@ fn detect_intel_wifi_interfaces(
|
||||
config_path: entry.path().join("config"),
|
||||
device_id,
|
||||
subsystem_id,
|
||||
firmware_family,
|
||||
firmware_family: table.family,
|
||||
transport_status: transport_status_from_config(&location, &config),
|
||||
ucode_candidates,
|
||||
ucode_candidates: table.mvm_ucode.iter().map(|s| s.to_string()).collect(),
|
||||
selected_ucode,
|
||||
pnvm_candidate,
|
||||
iwlmld_ucode_candidates: table.mld_ucode.iter().map(|s| s.to_string()).collect(),
|
||||
selected_iwlmld_ucode,
|
||||
preferred_op_mode: table.preferred,
|
||||
pnvm_candidate: table.pnvm.map(str::to_string),
|
||||
pnvm_found,
|
||||
prepared: false,
|
||||
transport_initialized: false,
|
||||
@@ -884,23 +812,30 @@ fn detect_intel_wifi_interfaces(
|
||||
devices.into_values().collect()
|
||||
}
|
||||
|
||||
fn intel_firmware_candidates(
|
||||
device_id: u16,
|
||||
subsystem_id: u16,
|
||||
) -> (&'static str, Vec<String>, Option<String>) {
|
||||
let (stems, pnvm): (Vec<&'static str>, Option<&'static str>) = match (device_id, subsystem_id) {
|
||||
(0x7740, 0x4090) => (
|
||||
vec![
|
||||
struct FirmwareTableEntry {
|
||||
family: &'static str,
|
||||
mvm_ucode: Vec<&'static str>,
|
||||
mld_ucode: Vec<&'static str>,
|
||||
pnvm: Option<&'static str>,
|
||||
preferred: OpMode,
|
||||
}
|
||||
|
||||
fn intel_firmware_candidates(device_id: u16, subsystem_id: u16) -> FirmwareTableEntry {
|
||||
match (device_id, subsystem_id) {
|
||||
(0x7740, 0x4090) => FirmwareTableEntry {
|
||||
family: "intel-bz-arrow-lake",
|
||||
mvm_ucode: vec![
|
||||
"iwlwifi-bz-b0-gf-a0-100.ucode",
|
||||
"iwlwifi-bz-b0-gf-a0-94.ucode",
|
||||
"iwlwifi-bz-b0-gf-a0-92.ucode",
|
||||
],
|
||||
Some("iwlwifi-bz-b0-gf-a0.pnvm"),
|
||||
),
|
||||
(0x7740, _) => (
|
||||
vec![
|
||||
// Keep in sync with redbear-iwlwifi main.rs — only iwlmvm-era
|
||||
// firmware until the MLD op-mode lands (c-series is iwlmld).
|
||||
mld_ucode: vec![],
|
||||
pnvm: Some("iwlwifi-bz-b0-gf-a0.pnvm"),
|
||||
preferred: OpMode::Mvm,
|
||||
},
|
||||
(0x7740, _) => FirmwareTableEntry {
|
||||
family: "intel-bz-arrow-lake",
|
||||
mvm_ucode: vec![
|
||||
"iwlwifi-bz-b0-fm-c0-101.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-100.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-98.ucode",
|
||||
@@ -908,50 +843,51 @@ fn intel_firmware_candidates(
|
||||
"iwlwifi-bz-b0-fm-c0-94.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-92.ucode",
|
||||
],
|
||||
Some("iwlwifi-bz-b0-fm-c0.pnvm"),
|
||||
),
|
||||
(0x2725, _) => (
|
||||
vec![
|
||||
"iwlwifi-ty-a0-gf-a0-59.ucode",
|
||||
"iwlwifi-ty-a0-gf-a0-84.ucode",
|
||||
mld_ucode: vec![
|
||||
"iwlwifi-bz-b0-fm-c0-c106.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-c103.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-c102.ucode",
|
||||
"iwlwifi-bz-b0-fm-c0-c101.ucode",
|
||||
],
|
||||
Some("iwlwifi-ty-a0-gf-a0.pnvm"),
|
||||
),
|
||||
(0x7af0, 0x4090) => (
|
||||
vec![
|
||||
"iwlwifi-so-a0-gf-a0-64.ucode",
|
||||
"iwlwifi-so-a0-gf-a0-66.ucode",
|
||||
],
|
||||
Some("iwlwifi-so-a0-gf-a0.pnvm"),
|
||||
),
|
||||
(0x7af0, 0x4070) => (
|
||||
vec!["iwlwifi-so-a0-hr-b0-64.ucode"],
|
||||
Some("iwlwifi-so-a0-hr-b0.pnvm"),
|
||||
),
|
||||
(0x7af0, 0x0aaa) | (0x7af0, 0x0030) => (
|
||||
vec![
|
||||
"iwlwifi-so-a0-jf-b0-64.ucode",
|
||||
"iwlwifi-9000-pu-b0-jf-b0-46.ucode",
|
||||
],
|
||||
Some("iwlwifi-so-a0-jf-b0.pnvm"),
|
||||
),
|
||||
_ => (vec!["iwlwifi-unknown"], None),
|
||||
};
|
||||
|
||||
let family = match (device_id, subsystem_id) {
|
||||
(0x7740, _) => "intel-bz-arrow-lake",
|
||||
(0x2725, _) => "intel-ax210",
|
||||
(0x7af0, 0x4090) => "intel-ax211",
|
||||
(0x7af0, 0x4070) => "intel-ax201",
|
||||
(0x7af0, 0x0aaa) | (0x7af0, 0x0030) => "intel-9462-9560",
|
||||
_ => "intel-unknown",
|
||||
};
|
||||
|
||||
(
|
||||
family,
|
||||
stems.into_iter().map(str::to_string).collect(),
|
||||
pnvm.map(str::to_string),
|
||||
)
|
||||
pnvm: Some("iwlwifi-bz-b0-fm-c0.pnvm"),
|
||||
preferred: OpMode::Mld,
|
||||
},
|
||||
(0x2725, _) => FirmwareTableEntry {
|
||||
family: "intel-ax210",
|
||||
mvm_ucode: vec!["iwlwifi-ty-a0-gf-a0-59.ucode", "iwlwifi-ty-a0-gf-a0-84.ucode"],
|
||||
mld_ucode: vec![],
|
||||
pnvm: Some("iwlwifi-ty-a0-gf-a0.pnvm"),
|
||||
preferred: OpMode::Mvm,
|
||||
},
|
||||
(0x7af0, 0x4090) => FirmwareTableEntry {
|
||||
family: "intel-ax211",
|
||||
mvm_ucode: vec!["iwlwifi-so-a0-gf-a0-64.ucode", "iwlwifi-so-a0-gf-a0-66.ucode"],
|
||||
mld_ucode: vec![],
|
||||
pnvm: Some("iwlwifi-so-a0-gf-a0.pnvm"),
|
||||
preferred: OpMode::Mvm,
|
||||
},
|
||||
(0x7af0, 0x4070) => FirmwareTableEntry {
|
||||
family: "intel-ax201",
|
||||
mvm_ucode: vec!["iwlwifi-so-a0-hr-b0-64.ucode"],
|
||||
mld_ucode: vec![],
|
||||
pnvm: Some("iwlwifi-so-a0-hr-b0.pnvm"),
|
||||
preferred: OpMode::Mvm,
|
||||
},
|
||||
(0x7af0, 0x0aaa) | (0x7af0, 0x0030) => FirmwareTableEntry {
|
||||
family: "intel-9462-9560",
|
||||
mvm_ucode: vec!["iwlwifi-so-a0-jf-b0-64.ucode", "iwlwifi-9000-pu-b0-jf-b0-46.ucode"],
|
||||
mld_ucode: vec![],
|
||||
pnvm: Some("iwlwifi-so-a0-jf-b0.pnvm"),
|
||||
preferred: OpMode::Mvm,
|
||||
},
|
||||
_ => FirmwareTableEntry {
|
||||
family: "intel-unknown",
|
||||
mvm_ucode: vec!["iwlwifi-unknown"],
|
||||
mld_ucode: vec![],
|
||||
pnvm: None,
|
||||
preferred: OpMode::Mvm,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn transport_status_from_config(location: &ParsedPciLocation, config: &[u8]) -> String {
|
||||
@@ -1251,21 +1187,6 @@ mod tests {
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_backend_connects_with_wpa2() {
|
||||
let mut backend = StubBackend::from_env();
|
||||
let state = InterfaceState {
|
||||
ssid: "demo".to_string(),
|
||||
security: "wpa2-psk".to_string(),
|
||||
key: "secret".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
backend.connect("wlan0", &state).unwrap(),
|
||||
WifiStatus::Connected
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_device_backend_exposes_no_interfaces() {
|
||||
let backend = NoDeviceBackend::new();
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::os::fd::RawFd;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
use backend::{Backend, IntelBackend, NoDeviceBackend, StubBackend};
|
||||
use backend::{Backend, IntelBackend, NoDeviceBackend};
|
||||
#[cfg(target_os = "redox")]
|
||||
use dbus_nm::register_nm_interface;
|
||||
use log::LevelFilter;
|
||||
@@ -69,7 +69,6 @@ fn notify_scheme_ready(notify_fd: Option<RawFd>, socket: &Socket, scheme: &mut W
|
||||
enum BackendMode {
|
||||
Intel,
|
||||
NoDevice,
|
||||
Stub,
|
||||
}
|
||||
|
||||
fn iwlwifi_command_path() -> std::path::PathBuf {
|
||||
@@ -86,12 +85,11 @@ fn select_backend_mode(
|
||||
) -> 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,
|
||||
_ => BackendMode::NoDevice,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +111,6 @@ fn build_backend() -> Box<dyn Backend> {
|
||||
match mode {
|
||||
BackendMode::Intel => Box::new(IntelBackend::from_env()),
|
||||
BackendMode::NoDevice => Box::new(NoDeviceBackend::new()),
|
||||
BackendMode::Stub => Box::new(StubBackend::from_env()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +134,6 @@ mod tests {
|
||||
select_backend_mode(Some("intel"), false, false, false),
|
||||
BackendMode::Intel
|
||||
);
|
||||
assert_eq!(
|
||||
select_backend_mode(Some("stub"), true, true, true),
|
||||
BackendMode::Stub
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -151,7 +144,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
select_backend_mode(None, false, false, true),
|
||||
BackendMode::Stub
|
||||
BackendMode::NoDevice
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,14 +157,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_runtime_stays_stub_without_explicit_override() {
|
||||
fn host_runtime_reports_no_device() {
|
||||
assert_eq!(
|
||||
select_backend_mode(None, true, true, false),
|
||||
BackendMode::Stub
|
||||
BackendMode::NoDevice
|
||||
);
|
||||
assert_eq!(
|
||||
select_backend_mode(None, false, false, false),
|
||||
BackendMode::Stub
|
||||
BackendMode::NoDevice
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -553,7 +553,7 @@ impl SchemeSync for WifiCtlScheme {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::backend::{IntelBackend, StubBackend, TEST_ENV_LOCK};
|
||||
use crate::backend::{Backend, IntelBackend, WifiStatus, TEST_ENV_LOCK};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
@@ -569,9 +569,73 @@ mod tests {
|
||||
path
|
||||
}
|
||||
|
||||
struct TestBackend;
|
||||
|
||||
impl Backend for TestBackend {
|
||||
fn interfaces(&self) -> Vec<String> {
|
||||
vec!["wlan0".to_string()]
|
||||
}
|
||||
fn capabilities(&self) -> Vec<String> {
|
||||
vec!["backend=test".to_string()]
|
||||
}
|
||||
fn initial_status(&self, _interface: &str) -> WifiStatus {
|
||||
WifiStatus::DeviceDetected
|
||||
}
|
||||
fn initial_link_state(&self, _interface: &str) -> String {
|
||||
"link=down".to_string()
|
||||
}
|
||||
fn default_scan_results(&self, _interface: &str) -> Vec<String> {
|
||||
vec!["test-ssid".to_string()]
|
||||
}
|
||||
fn scan(&mut self, _interface: &str) -> Result<Vec<String>, String> {
|
||||
Ok(vec!["test-ssid".to_string(), "test-open".to_string()])
|
||||
}
|
||||
fn firmware_status(&self, _interface: &str) -> String {
|
||||
"firmware=test".to_string()
|
||||
}
|
||||
fn transport_status(&self, _interface: &str) -> String {
|
||||
"transport=test".to_string()
|
||||
}
|
||||
fn prepare(&mut self, _interface: &str) -> Result<WifiStatus, String> {
|
||||
Ok(WifiStatus::FirmwareReady)
|
||||
}
|
||||
fn transport_probe(&mut self, _interface: &str) -> Result<String, String> {
|
||||
Ok("transport=test mmio_probe=host-skipped".to_string())
|
||||
}
|
||||
fn init_transport(&mut self, _interface: &str) -> Result<String, String> {
|
||||
Ok("transport_init=test".to_string())
|
||||
}
|
||||
fn activate(&mut self, _interface: &str) -> Result<String, String> {
|
||||
Ok("activation=test".to_string())
|
||||
}
|
||||
fn connect_result(&self, _interface: &str) -> String {
|
||||
"connect=test".to_string()
|
||||
}
|
||||
fn disconnect_result(&self, _interface: &str) -> String {
|
||||
"disconnect=test".to_string()
|
||||
}
|
||||
fn retry(&mut self, _interface: &str) -> Result<WifiStatus, String> {
|
||||
Ok(WifiStatus::DeviceDetected)
|
||||
}
|
||||
fn connect(&mut self, _interface: &str, state: &InterfaceState) -> Result<WifiStatus, String> {
|
||||
if state.ssid.is_empty() {
|
||||
return Err("missing SSID".to_string());
|
||||
}
|
||||
match state.security.as_str() {
|
||||
"open" => Ok(WifiStatus::Connected),
|
||||
"wpa2-psk" if !state.key.is_empty() => Ok(WifiStatus::Connected),
|
||||
"wpa2-psk" => Err("missing key".to_string()),
|
||||
other => Err(format!("unsupported security {other}")),
|
||||
}
|
||||
}
|
||||
fn disconnect(&mut self, _interface: &str) -> Result<WifiStatus, String> {
|
||||
Ok(WifiStatus::DeviceDetected)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_updates_after_connect_and_disconnect() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(StubBackend::from_env()));
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(TestBackend));
|
||||
let iface = "wlan0".to_string();
|
||||
|
||||
{
|
||||
@@ -603,7 +667,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn apply_connect_outcome_preserves_pending_link_state() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(StubBackend::from_env()));
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(TestBackend));
|
||||
let iface = "wlan0".to_string();
|
||||
|
||||
scheme
|
||||
@@ -624,8 +688,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_prepare_marks_firmware_ready() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(StubBackend::from_env()));
|
||||
fn test_backend_prepare_marks_firmware_ready() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(TestBackend));
|
||||
let iface = "wlan0".to_string();
|
||||
|
||||
let status = scheme.backend.prepare(&iface).unwrap();
|
||||
@@ -637,11 +701,11 @@ mod tests {
|
||||
assert_eq!(scheme.state(&iface).unwrap().status, "firmware-ready");
|
||||
assert_eq!(
|
||||
scheme.state(&iface).unwrap().firmware_status,
|
||||
"firmware=stub"
|
||||
"firmware=test"
|
||||
);
|
||||
assert_eq!(
|
||||
scheme.state(&iface).unwrap().transport_status,
|
||||
"transport=stub"
|
||||
"transport=test"
|
||||
);
|
||||
assert_eq!(
|
||||
scheme.state(&iface).unwrap().transport_init_status,
|
||||
@@ -650,8 +714,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_scan_updates_scan_results() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(StubBackend::from_env()));
|
||||
fn test_backend_scan_updates_scan_results() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(TestBackend));
|
||||
let iface = "wlan0".to_string();
|
||||
|
||||
let results = scheme.backend.scan(&iface).unwrap();
|
||||
@@ -662,7 +726,7 @@ mod tests {
|
||||
assert_eq!(scheme.state(&iface).unwrap().status, "scanning");
|
||||
assert_eq!(
|
||||
scheme.state(&iface).unwrap().scan_results,
|
||||
vec!["demo-ssid".to_string(), "demo-open".to_string()]
|
||||
vec!["test-ssid".to_string(), "test-open".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -717,8 +781,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_transport_probe_updates_transport_status() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(StubBackend::from_env()));
|
||||
fn test_backend_transport_probe_updates_transport_status() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(TestBackend));
|
||||
let iface = "wlan0".to_string();
|
||||
let transport_status = scheme.backend.transport_probe(&iface).unwrap();
|
||||
scheme.state_mut(&iface).unwrap().transport_status = transport_status;
|
||||
@@ -730,14 +794,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_init_transport_records_state() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(StubBackend::from_env()));
|
||||
fn test_backend_init_transport_records_state() {
|
||||
let mut scheme = WifiCtlScheme::new(Box::new(TestBackend));
|
||||
let iface = "wlan0".to_string();
|
||||
let status = scheme.backend.init_transport(&iface).unwrap();
|
||||
scheme.state_mut(&iface).unwrap().transport_init_status = status;
|
||||
assert_eq!(
|
||||
scheme.state(&iface).unwrap().transport_init_status,
|
||||
"transport_init=stub"
|
||||
"transport_init=test"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user