From 904585d4d8f68576f1e6c6592c104ba2f0635eba Mon Sep 17 00:00:00 2001 From: vasilito Date: Wed, 22 Jul 2026 10:40:40 +0900 Subject: [PATCH] lg-gram-16z90tp: Phase 3.5/3.6/6.1 + Wi-Fi stub elimination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../docs/DRM-MODERNIZATION-EXECUTION-PLAN.md | 1 + .../LG-GRAM-16Z90TP-COMPATIBILITY-PLAN.md | 25 +- local/docs/WIFI-IMPLEMENTATION-PLAN.md | 4 +- .../redbear-iwlwifi/source/src/main.rs | 45 ++- .../source/src/drivers/intel/backlight.rs | 153 +++++++++ .../redox-drm/source/src/drivers/intel/dmc.rs | 27 ++ .../redox-drm/source/src/drivers/intel/mod.rs | 18 ++ .../redbear-wifictl/source/src/backend.rs | 305 +++++++----------- .../system/redbear-wifictl/source/src/main.rs | 19 +- .../redbear-wifictl/source/src/scheme.rs | 94 +++++- 10 files changed, 461 insertions(+), 230 deletions(-) create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/backlight.rs diff --git a/local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md b/local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md index 01f77e7409..072ca5a281 100644 --- a/local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md +++ b/local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md @@ -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, diff --git a/local/docs/LG-GRAM-16Z90TP-COMPATIBILITY-PLAN.md b/local/docs/LG-GRAM-16Z90TP-COMPATIBILITY-PLAN.md index fbb6979a84..417634aaca 100644 --- a/local/docs/LG-GRAM-16Z90TP-COMPATIBILITY-PLAN.md +++ b/local/docs/LG-GRAM-16Z90TP-COMPATIBILITY-PLAN.md @@ -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.** diff --git a/local/docs/WIFI-IMPLEMENTATION-PLAN.md b/local/docs/WIFI-IMPLEMENTATION-PLAN.md index 25abf15458..fbf63b32be 100644 --- a/local/docs/WIFI-IMPLEMENTATION-PLAN.md +++ b/local/docs/WIFI-IMPLEMENTATION-PLAN.md @@ -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. | diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs b/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs index ae2a838aa4..91139d829a 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs @@ -133,6 +133,8 @@ struct Candidate { family: &'static str, ucode_candidates: Vec, selected_ucode: Option, + iwlmld_candidates: Vec, + selected_iwlmld: Option, pnvm_candidate: Option, pnvm_found: Option, } @@ -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, DriverEr .iter() .find(|candidate| blob_exists(candidate)) .cloned(); + let iwlmld_candidates: Vec = 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, 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()), }; diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/backlight.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/backlight.rs new file mode 100644 index 0000000000..bfc489e61b --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/backlight.rs @@ -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 { + 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); + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/dmc.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dmc.rs index 1c28753ec7..4bf79703be 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/dmc.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dmc.rs @@ -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. diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs index 064007d7e8..3d48785fc0 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs @@ -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, diff --git a/local/recipes/system/redbear-wifictl/source/src/backend.rs b/local/recipes/system/redbear-wifictl/source/src/backend.rs index 314b69a0e5..e538b07984 100644 --- a/local/recipes/system/redbear-wifictl/source/src/backend.rs +++ b/local/recipes/system/redbear-wifictl/source/src/backend.rs @@ -111,6 +111,21 @@ pub trait Backend { fn disconnect(&mut self, interface: &str) -> Result; } +#[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, selected_ucode: Option, + iwlmld_ucode_candidates: Vec, + selected_iwlmld_ucode: Option, + preferred_op_mode: OpMode, pnvm_candidate: Option, pnvm_found: Option, prepared: bool, @@ -131,119 +149,14 @@ struct IntelInterface { disconnect_result: String, } -pub struct StubBackend { - interfaces: Vec, -} - 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::>() - }) - .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 { - self.interfaces.clone() - } - - fn capabilities(&self) -> Vec { - 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 { - vec!["demo-ssid".to_string(), "demo-open".to_string()] - } - - fn scan(&mut self, _interface: &str) -> Result, 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 { - Ok("transport_init=stub".to_string()) - } - - fn activate(&mut self, _interface: &str) -> Result { - 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 { - Ok(WifiStatus::DeviceDetected) - } - - fn prepare(&mut self, _interface: &str) -> Result { - Ok(WifiStatus::FirmwareReady) - } - - fn transport_probe(&mut self, _interface: &str) -> Result { - Ok("transport=stub mmio_probe=host-skipped".to_string()) - } - - fn connect(&mut self, _interface: &str, state: &InterfaceState) -> Result { - 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 { - Ok(WifiStatus::DeviceDetected) - } -} - impl Backend for NoDeviceBackend { fn interfaces(&self) -> Vec { 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::>(); 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, Option) { - 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(); diff --git a/local/recipes/system/redbear-wifictl/source/src/main.rs b/local/recipes/system/redbear-wifictl/source/src/main.rs index f04b004c14..84157fc7f6 100644 --- a/local/recipes/system/redbear-wifictl/source/src/main.rs +++ b/local/recipes/system/redbear-wifictl/source/src/main.rs @@ -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, 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 { 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 ); } diff --git a/local/recipes/system/redbear-wifictl/source/src/scheme.rs b/local/recipes/system/redbear-wifictl/source/src/scheme.rs index a77dc0271a..ad27b01647 100644 --- a/local/recipes/system/redbear-wifictl/source/src/scheme.rs +++ b/local/recipes/system/redbear-wifictl/source/src/scheme.rs @@ -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 { + vec!["wlan0".to_string()] + } + fn capabilities(&self) -> Vec { + 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 { + vec!["test-ssid".to_string()] + } + fn scan(&mut self, _interface: &str) -> Result, 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 { + Ok(WifiStatus::FirmwareReady) + } + fn transport_probe(&mut self, _interface: &str) -> Result { + Ok("transport=test mmio_probe=host-skipped".to_string()) + } + fn init_transport(&mut self, _interface: &str) -> Result { + Ok("transport_init=test".to_string()) + } + fn activate(&mut self, _interface: &str) -> Result { + 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 { + Ok(WifiStatus::DeviceDetected) + } + fn connect(&mut self, _interface: &str, state: &InterfaceState) -> Result { + 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 { + 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" ); } }