From 9fc1947d539fa6b770a6e377dca43b187e67cdd6 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Sat, 18 Jul 2026 21:52:05 +0900 Subject: [PATCH] xhcid: converge xHCI quirks to canonical redox-driver-sys path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace xhcid's self-contained 294-line xhci/quirks.rs (7-flag bitflags type + ~30-entry quirk table) with a thin re-export shim that delegates to redox-driver-sys::quirks, which now carries all 51 Linux 7.1 quirk flags and the full ~85-entry canonical controller table. Changes: - Cargo.toml: add redox-driver-sys path dependency - xhci/quirks.rs: replace table with pub use XhciControllerQuirkFlags as XhciQuirks + delegating lookup_quirks(vendor, device, revision, hci_version) - main.rs: read real HCIVERSION from MMIO offset 0x04 via cap.hci_ver.read() instead of hardcoded 0x100 — matches Linux 7.1 xhci_gen_setup() at xhci.c:5455. Two table entries depend on the value: AMD_0x96_HOST (xhci-pci.c:308) and >= 0x120 spec rule (xhci-pci.c:511). - main.rs: resolve quirks BEFORE get_int_method() so BROKEN_MSI skips MSI/MSI-X probing entirely (matching Linux xhci_try_enable_msi at xhci-pci.c:143-209). Previously the quirk only overrode the method label while irq_file still held the MSI handle — a mismatch causing silent interrupt-delivery failures on BROKEN_MSI controllers. - get_int_method (both cfg variants): accept quirks parameter --- Cargo.lock | 92 ++++++- drivers/usb/xhcid/Cargo.toml | 1 + drivers/usb/xhcid/src/main.rs | 70 ++++-- drivers/usb/xhcid/src/xhci/quirks.rs | 346 +++++---------------------- 4 files changed, 189 insertions(+), 320 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f9809710a..d658e13d33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,7 +111,7 @@ version = "0.0.1" dependencies = [ "acpi", "serde", - "toml", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -800,7 +800,7 @@ dependencies = [ "redox_event", "redox_syscall 0.9.0+rb0.3.1", "scheme-utils", - "toml", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -1209,7 +1209,7 @@ dependencies = [ "redox_syscall 0.9.0+rb0.3.1", "serde", "serde_json", - "toml", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -1677,7 +1677,7 @@ dependencies = [ "pico-args", "redox_syscall 0.9.0+rb0.3.1", "serde", - "toml", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -1926,6 +1926,20 @@ version = "0.1.0" name = "redbear-ufw" version = "0.1.0" +[[package]] +name = "redox-driver-sys" +version = "0.3.1" +dependencies = [ + "bincode", + "bitflags 2.13.0", + "libredox", + "log", + "redox_syscall 0.9.0+rb0.3.1", + "serde", + "thiserror 2.0.18", + "toml 0.8.23", +] + [[package]] name = "redox-initfs" version = "0.2.0" @@ -2251,6 +2265,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -2461,6 +2484,18 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit", +] + [[package]] name = "toml" version = "1.1.2+spec-1.1.0" @@ -2469,11 +2504,20 @@ checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", - "serde_spanned", - "toml_datetime", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", ] [[package]] @@ -2485,15 +2529,35 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + [[package]] name = "toml_parser" version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "toml_writer" version = "1.1.1+spec-1.1.0" @@ -2955,6 +3019,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr 2.8.3", +] + [[package]] name = "winnow" version = "1.0.3" @@ -2976,6 +3049,7 @@ dependencies = [ "log", "pcid", "plain", + "redox-driver-sys", "redox-scheme", "redox_event", "redox_syscall 0.9.0+rb0.3.1", @@ -2985,7 +3059,7 @@ dependencies = [ "serde_json", "smallvec 1.15.2", "thiserror 2.0.18", - "toml", + "toml 1.1.2+spec-1.1.0", "usb-core", ] diff --git a/drivers/usb/xhcid/Cargo.toml b/drivers/usb/xhcid/Cargo.toml index f989eec1f3..c3cf0ae275 100644 --- a/drivers/usb/xhcid/Cargo.toml +++ b/drivers/usb/xhcid/Cargo.toml @@ -36,6 +36,7 @@ pcid = { path = "../../pcid" } libredox.workspace = true regex = "1.10.6" usb-core = { path = "../../../../../recipes/drivers/usb-core/source" } +redox-driver-sys = { path = "../../../../../recipes/drivers/redox-driver-sys/source" } [lints] workspace = true diff --git a/drivers/usb/xhcid/src/main.rs b/drivers/usb/xhcid/src/main.rs index 4243e6c600..28772547c5 100644 --- a/drivers/usb/xhcid/src/main.rs +++ b/drivers/usb/xhcid/src/main.rs @@ -29,6 +29,7 @@ extern crate bitflags; use std::fs::File; use std::sync::Arc; +use common::io::Io; use pcid_interface::irq_helpers::read_bsp_apic_id; #[cfg(target_arch = "x86_64")] use pcid_interface::irq_helpers::{ @@ -52,14 +53,25 @@ mod xhci; use usb_core::scheme_path; #[cfg(target_arch = "x86_64")] -fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option, InterruptMethod) { +fn get_int_method( + pcid_handle: &mut PciFunctionHandle, + quirks: xhci::quirks::XhciQuirks, +) -> (Option, InterruptMethod) { let pci_config = pcid_handle.config(); let all_pci_features = pcid_handle.fetch_all_features(); log::debug!("XHCI PCI FEATURES: {:?}", all_pci_features); - let has_msi = all_pci_features.iter().any(|feature| feature.is_msi()); - let has_msix = all_pci_features.iter().any(|feature| feature.is_msix()); + let broken_msi = quirks.contains(xhci::quirks::XhciQuirks::BROKEN_MSI); + if broken_msi { + log::warn!( + "xhcid: BROKEN_MSI quirk active — skipping MSI/MSI-X entirely \ + (Linux xhci-pci.c xhci_try_enable_msi equivalent)" + ); + } + + let has_msi = !broken_msi && all_pci_features.iter().any(|feature| feature.is_msi()); + let has_msix = !broken_msi && all_pci_features.iter().any(|feature| feature.is_msix()); if has_msix { let msix_info = match pcid_handle.feature_info(PciFeature::MsiX) { @@ -108,7 +120,13 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option, Interru //TODO: MSI on non-x86_64? #[cfg(not(target_arch = "x86_64"))] -fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option, InterruptMethod) { +fn get_int_method( + pcid_handle: &mut PciFunctionHandle, + quirks: xhci::quirks::XhciQuirks, +) -> (Option, InterruptMethod) { + if quirks.contains(xhci::quirks::XhciQuirks::BROKEN_MSI) { + log::debug!("xhcid: BROKEN_MSI quirk set (moot on non-x86_64 — MSI not used)"); + } let pci_config = pcid_handle.config(); if let Some(irq) = pci_config.func.legacy_interrupt_line { @@ -141,37 +159,37 @@ fn daemon_with_context_size( let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize; - let (irq_file, mut interrupt_method) = get_int_method(&mut pcid_handle); + // Read HCIVERSION from the mapped capability registers (xHCI spec + // § 5.3 table 5-9, MMIO offset 0x04). Mirrors Linux 7.1 + // xhci_gen_setup() at xhci.c:5455 which reads hci_version from MMIO + // BEFORE the quirk table — two table entries depend on it + // (AMD_0x96_HOST at xhci-pci.c:308, the >= 0x120 spec rule at + // xhci-pci.c:511). + let hci_version = unsafe { + (*(address as *const xhci::CapabilityRegs)) + .hci_ver + .read() + }; log::info!("XHCI {}", pci_config.func.display()); - // Apply per-vendor/per-device quirks (Linux 7.1 xhci-pci.c port). - // Without quirks, xhcid will silently misbehave on real hardware. - let (vendor, device, hci_version) = match pci_config.device_id { + let (vendor, device, revision) = match pci_config.device_id { Some(did) => (did.vendor_id, did.device_id, did.revision), None => (0, 0, 0), }; - let quirks = xhci::quirks::lookup_quirks(vendor, device, hci_version); + let quirks = xhci::quirks::lookup_quirks(vendor, device, revision, hci_version); log::info!( - "XHCI quirks: vendor={:04X} device={:04X} hci_ver={:02X} -> {:#x}", - vendor, device, hci_version, quirks.bits() + "XHCI quirks: vendor={:04X} device={:04X} rev={:02X} hci_ver={:04X} -> {:#x}", + vendor, device, revision, hci_version, quirks.bits() ); - // Linux 7.1 xhci-pci.c: if BROKEN_MSI quirk is set, fall back from - // MSI/MSI-X to legacy INTx or Polling to avoid interrupt storms - // and spurious reboots on buggy controller implementations. - let interrupt_method = if quirks.contains(xhci::quirks::XhciQuirks::BROKEN_MSI) { - log::warn!("xhcid: BROKEN_MSI quirk active — falling back from MSI to legacy INTx/polling"); - if let Some(irq) = pci_config.func.legacy_interrupt_line { - log::info!("xhcid: using legacy IRQ {}", irq); - InterruptMethod::Intx - } else { - log::warn!("xhcid: no legacy IRQ available — using polling mode"); - InterruptMethod::Polling - } - } else { - interrupt_method - }; + // Quirks must be resolved before interrupt-method selection so that + // BROKEN_MSI skips MSI/MSI-X probing entirely (matching Linux + // xhci_try_enable_msi() at xhci-pci.c:143-209). This guarantees the + // interrupt handle and the delivery method agree — previously the + // handle could still be an MSI file while the method was overridden + // to Intx/Polling, causing silent interrupt-delivery failures. + let (irq_file, interrupt_method) = get_int_method(&mut pcid_handle, quirks); let scheme_name = scheme_path(&name); let socket = Socket::create().expect("xhcid: failed to create usb scheme"); diff --git a/drivers/usb/xhcid/src/xhci/quirks.rs b/drivers/usb/xhcid/src/xhci/quirks.rs index 183099ef41..601f5097f4 100644 --- a/drivers/usb/xhcid/src/xhci/quirks.rs +++ b/drivers/usb/xhcid/src/xhci/quirks.rs @@ -1,294 +1,70 @@ -//! xHCI per-controller quirks. +//! xHCI per-controller quirks — converged to the canonical path. //! -//! Cross-referenced with `local/reference/linux-7.1/drivers/usb/host/xhci.h` -//! (51 quirk flags) and `xhci-pci.c` (per-vendor/per-device lookup). +//! Previously this file carried a self-contained 51-flag bitflags type and +//! a ~30-entry quirk table. As of the USB P2-A quirk-convergence work, the +//! canonical type, table, and lookup live in +//! `redox-driver-sys/source/src/quirks/` (`XhciControllerQuirkFlags`, +//! `xhci_table::XHCI_QUIRK_TABLE`, `lookup_xhci_controller_quirks_full`). +//! This file is now a thin re-export so existing call sites +//! (`xhci::quirks::XhciQuirks::BROKEN_MSI` etc.) keep working unchanged. //! -//! Each vendor has controller-specific errata requiring workaround code. -//! Without quirks, xhcid will silently misbehave on real hardware. -//! This module ports Linux's quirk table verbatim. +//! Bugs fixed by the convergence (vs the prior self-contained table): +//! - Intel 0x9c31 duplicate removed; WildcatPoint corrected to 0x9cb1. +//! - Panther Point device ID corrected to 0x1e31. +//! - VIA VL805 (0x3483) flags corrected to LPM_SUPPORT + TRB_OVERFETCH. +//! - Phytium (0xdc27) flags corrected to RESET_ON_RESUME. +//! - ASMedia 0x1142 flags corrected to NO_64BIT_SUPPORT + ASMEDIA_MODIFY_FLOWCONTROL. +//! - Intel 0x9a13 (Tiger Lake non-PCH) moved off RESET_TO_DEFAULT list. +//! +//! Authoritative reference: `local/reference/linux-7.1/drivers/usb/host/xhci-pci.c` +//! function `xhci_pci_quirks()` (lines 251–517). -use bitflags::bitflags; - -bitflags! { - /// xHCI controller quirks. See `linux-7.1/drivers/usb/host/xhci.h:1587-1649`. - #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] - pub struct XhciQuirks: u64 { - const LINK_TRB_QUIRK = 1u64 << 0; - /// Deprecated — kept for binary compat with the Linux header. - const RESET_EP_QUIRK = 1u64 << 1; - const NEC_HOST = 1u64 << 2; - const AMD_PLL_FIX = 1u64 << 3; - const SPURIOUS_SUCCESS = 1u64 << 4; - const EP_LIMIT_QUIRK = 1u64 << 5; - const BROKEN_MSI = 1u64 << 6; - const RESET_ON_RESUME = 1u64 << 7; - const SW_BW_CHECKING = 1u64 << 8; - const AMD_0x96_HOST = 1u64 << 9; - /// Deprecated. - const TRUST_TX_LENGTH = 1u64 << 10; - const LPM_SUPPORT = 1u64 << 11; - const INTEL_HOST = 1u64 << 12; - const SPURIOUS_REBOOT = 1u64 << 13; - const COMP_MODE_QUIRK = 1u64 << 14; - const AVOID_BEI = 1u64 << 15; - /// Deprecated. - const PLAT = 1u64 << 16; - const SLOW_SUSPEND = 1u64 << 17; - const SPURIOUS_WAKEUP = 1u64 << 18; - const BROKEN_STREAMS = 1u64 << 19; - const PME_STUCK_QUIRK = 1u64 << 20; - const MTK_HOST = 1u64 << 21; - const SSIC_PORT_UNUSED = 1u64 << 22; - const NO_64BIT_SUPPORT = 1u64 << 23; - const MISSING_CAS = 1u64 << 24; - const BROKEN_PORT_PED = 1u64 << 25; - const LIMIT_ENDPOINT_INTERVAL_7 = 1u64 << 26; - const U2_DISABLE_WAKE = 1u64 << 27; - const ASMEDIA_MODIFY_FLOWCONTROL = 1u64 << 28; - const HW_LPM_DISABLE = 1u64 << 29; - const SUSPEND_DELAY = 1u64 << 30; - const INTEL_USB_ROLE_SW = 1u64 << 31; - const ZERO_64B_REGS = 1u64 << 32; - const DEFAULT_PM_RUNTIME_ALLOW = 1u64 << 33; - const RESET_PLL_ON_DISCONNECT = 1u64 << 34; - const SNPS_BROKEN_SUSPEND = 1u64 << 35; - const SKIP_PHY_INIT = 1u64 << 37; - const DISABLE_SPARSE = 1u64 << 38; - const SG_TRB_CACHE_SIZE_QUIRK = 1u64 << 39; - const NO_SOFT_RETRY = 1u64 << 40; - const BROKEN_D3COLD_S2I = 1u64 << 41; - const EP_CTX_BROKEN_DCS = 1u64 << 42; - const SUSPEND_RESUME_CLKS = 1u64 << 43; - const RESET_TO_DEFAULT = 1u64 << 44; - const TRB_OVERFETCH = 1u64 << 45; - const ZHAOXIN_HOST = 1u64 << 46; - const WRITE_64_HI_LO = 1u64 << 47; - const CDNS_SCTX_QUIRK = 1u64 << 48; - const ETRON_HOST = 1u64 << 49; - const LIMIT_ENDPOINT_INTERVAL_9 = 1u64 << 50; - } -} +pub use redox_driver_sys::quirks::XhciControllerQuirkFlags as XhciQuirks; +/// PCI vendor IDs used by xhcid for log/lookup convenience. Values mirror +/// the constants in +/// `redox-driver-sys/source/src/quirks/xhci_table.rs`. pub mod vendor { - pub const FRESCO_LOGIC: u16 = 0x1b73; - pub const NEC: u16 = 0x1033; - pub const AMD: u16 = 0x1022; - pub const ATI: u16 = 0x1002; - pub const INTEL: u16 = 0x8086; - pub const ASMEDIA: u16 = 0x1b21; - pub const ETRON: u16 = 0x1b6f; - pub const RENESAS: u16 = 0x1912; - pub const VIA: u16 = 0x1106; - pub const CDNS: u16 = 0x17cd; - pub const PHYTIUM: u16 = 0x1db7; - pub const ZHAOXIN: u16 = 0x1d17; + pub const FRESCO_LOGIC: u16 = 0x1b73; + pub const NEC: u16 = 0x1033; + pub const AMD: u16 = 0x1022; + pub const ATI: u16 = 0x1002; + pub const INTEL: u16 = 0x8086; + pub const ASMEDIA: u16 = 0x1b21; + pub const ETRON: u16 = 0x1b6f; + pub const RENESAS: u16 = 0x1912; + pub const VIA: u16 = 0x1106; + pub const CDNS: u16 = 0x17cd; + pub const PHYTIUM: u16 = 0x1db7; + pub const ZHAOXIN: u16 = 0x1d17; pub const REDOX_OS_VENDOR: u16 = 0x1af4; } -pub struct DeviceQuirkEntry { - pub vendor: u16, - pub device: u16, // 0 = any device - pub hci_version: u8, // 0 = any version - pub quirks: XhciQuirks, -} - -const QUIRK_TABLE: &[DeviceQuirkEntry] = &[ - // Fresco Logic: all revisions have broken MSI. - DeviceQuirkEntry { - vendor: vendor::FRESCO_LOGIC, device: 0x1000, hci_version: 0, - quirks: XhciQuirks::from_bits(XhciQuirks::BROKEN_MSI.bits() | XhciQuirks::BROKEN_STREAMS.bits()).unwrap(), - }, - // ASMedia ASM1042/ASM1042A: modify flow control, broken MSI on rev 0x01. - DeviceQuirkEntry { - vendor: vendor::ASMEDIA, device: 0x1042, hci_version: 0, - quirks: XhciQuirks::ASMEDIA_MODIFY_FLOWCONTROL, - }, - // ASMedia ASM1142: broken MSI. - DeviceQuirkEntry { - vendor: vendor::ASMEDIA, device: 0x1142, hci_version: 0, - quirks: XhciQuirks::BROKEN_MSI, - }, - // ASMedia ASM2142/ASM3142: U2 disable wake, broken MSI. - DeviceQuirkEntry { - vendor: vendor::ASMEDIA, device: 0x2142, hci_version: 0, - quirks: XhciQuirks::from_bits(XhciQuirks::BROKEN_MSI.bits() | XhciQuirks::U2_DISABLE_WAKE.bits()).unwrap(), - }, - // ASMedia ASM3242: broken MSI. - DeviceQuirkEntry { - vendor: vendor::ASMEDIA, device: 0x3242, hci_version: 0, - quirks: XhciQuirks::BROKEN_MSI, - }, - // VIA VL805. - DeviceQuirkEntry { - vendor: vendor::VIA, device: 0x3483, hci_version: 0, - quirks: XhciQuirks::RESET_ON_RESUME, - }, - // NEC: oldest quirk target. - DeviceQuirkEntry { - vendor: vendor::NEC, device: 0, hci_version: 0, - quirks: XhciQuirks::NEC_HOST, - }, - // AMD 0x96 family. - DeviceQuirkEntry { - vendor: vendor::AMD, device: 0, hci_version: 0x96, - quirks: XhciQuirks::AMD_0x96_HOST, - }, - // AMD Promontory: U2 wake is broken. - DeviceQuirkEntry { - vendor: vendor::AMD, device: 0x43bc, hci_version: 0, - quirks: XhciQuirks::U2_DISABLE_WAKE, - }, - // AMD Renoir. - DeviceQuirkEntry { - vendor: vendor::AMD, device: 0x1639, hci_version: 0, - quirks: XhciQuirks::BROKEN_D3COLD_S2I, - }, - // AMD VanGogh. - DeviceQuirkEntry { - vendor: vendor::AMD, device: 0x161d, hci_version: 0, - quirks: XhciQuirks::BROKEN_D3COLD_S2I, - }, - // Intel baseline. - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0, hci_version: 0, - quirks: XhciQuirks::from_bits(XhciQuirks::LPM_SUPPORT.bits() | XhciQuirks::INTEL_HOST.bits() | XhciQuirks::AVOID_BEI.bits()).unwrap(), - }, - // Intel Panther Point. - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0x9c31, hci_version: 0, - quirks: XhciQuirks::from_bits(XhciQuirks::EP_LIMIT_QUIRK.bits() | XhciQuirks::SW_BW_CHECKING.bits() | XhciQuirks::SPURIOUS_REBOOT.bits()).unwrap(), - }, - // Intel LynxPoint / WildcatPoint. - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0x9c31, hci_version: 0, - quirks: XhciQuirks::from_bits(XhciQuirks::SPURIOUS_REBOOT.bits() | XhciQuirks::SPURIOUS_WAKEUP.bits()).unwrap(), - }, - // Intel SunrisePoint. - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0x9d2f, hci_version: 0, - quirks: XhciQuirks::PME_STUCK_QUIRK, - }, - // Intel Cherryview. - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0xa2af, hci_version: 0, - quirks: XhciQuirks::from_bits(XhciQuirks::PME_STUCK_QUIRK.bits() | XhciQuirks::SSIC_PORT_UNUSED.bits()).unwrap(), - }, - // Intel TigerLake / AlderLake. - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0x9a13, hci_version: 0, - quirks: XhciQuirks::RESET_TO_DEFAULT, - }, - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0x51ed, hci_version: 0, - quirks: XhciQuirks::RESET_TO_DEFAULT, - }, - // Intel Alpine / Titan Ridge / IceLake / TigerLake / MapleRidge. - DeviceQuirkEntry { - vendor: vendor::INTEL, device: 0x15b5, hci_version: 0, - quirks: XhciQuirks::DEFAULT_PM_RUNTIME_ALLOW, - }, - // Etron EJ168/EJ188. - DeviceQuirkEntry { - vendor: vendor::ETRON, device: 0x7023, hci_version: 0, - quirks: XhciQuirks::from_bits( - XhciQuirks::ETRON_HOST.bits() - | XhciQuirks::RESET_ON_RESUME.bits() - | XhciQuirks::BROKEN_STREAMS.bits() - | XhciQuirks::NO_SOFT_RETRY.bits(), - ) - .unwrap(), - }, - // Renesas uPD720202 (gen 1). - DeviceQuirkEntry { - vendor: vendor::RENESAS, device: 0x0014, hci_version: 0, - quirks: XhciQuirks::ZERO_64B_REGS, - }, - // Renesas uPD720202 K2 (gen 2). - DeviceQuirkEntry { - vendor: vendor::RENESAS, device: 0x0015, hci_version: 0, - quirks: XhciQuirks::from_bits(XhciQuirks::RESET_ON_RESUME.bits() | XhciQuirks::ZERO_64B_REGS.bits()).unwrap(), - }, - // VIA. - DeviceQuirkEntry { - vendor: vendor::VIA, device: 0, hci_version: 0, - quirks: XhciQuirks::RESET_ON_RESUME, - }, - // Phytium. - DeviceQuirkEntry { - vendor: vendor::PHYTIUM, device: 0, hci_version: 0, - quirks: XhciQuirks::LIMIT_ENDPOINT_INTERVAL_9, - }, - // Zhaoxin. - DeviceQuirkEntry { - vendor: vendor::ZHAOXIN, device: 0, hci_version: 0, - quirks: XhciQuirks::ZHAOXIN_HOST, - }, - // Redox OS (QEMU emulated, VID 0x1af4): baseline AVOID_BEI. - DeviceQuirkEntry { - vendor: vendor::REDOX_OS_VENDOR, device: 0, hci_version: 0, - quirks: XhciQuirks::AVOID_BEI, - }, -]; - -/// Look up quirks for a given controller. -pub fn lookup_quirks(vendor: u16, device: u16, hci_version: u8) -> XhciQuirks { - let mut result = XhciQuirks::default(); - for entry in QUIRK_TABLE { - if entry.vendor != vendor { - continue; - } - let device_matches = entry.device == 0 || entry.device == device; - let version_matches = entry.hci_version == 0 || entry.hci_version == hci_version; - if device_matches && version_matches { - result |= entry.quirks; - } - } - result -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn intel_has_lpm_and_avoid_bei() { - let q = lookup_quirks(vendor::INTEL, 0x9c31, 0); - assert!(q.contains(XhciQuirks::LPM_SUPPORT)); - assert!(q.contains(XhciQuirks::INTEL_HOST)); - assert!(q.contains(XhciQuirks::AVOID_BEI)); - } - - #[test] - fn etron_has_broken_streams() { - let q = lookup_quirks(vendor::ETRON, 0x7023, 0); - assert!(q.contains(XhciQuirks::ETRON_HOST)); - assert!(q.contains(XhciQuirks::BROKEN_STREAMS)); - assert!(q.contains(XhciQuirks::NO_SOFT_RETRY)); - } - - #[test] - fn renesas_first_gen_has_zero_64b() { - let q = lookup_quirks(vendor::RENESAS, 0x0014, 0); - assert!(q.contains(XhciQuirks::ZERO_64B_REGS)); - assert!(!q.contains(XhciQuirks::RESET_ON_RESUME)); - } - - #[test] - fn renesas_k2_has_both() { - let q = lookup_quirks(vendor::RENESAS, 0x0015, 0); - assert!(q.contains(XhciQuirks::ZERO_64B_REGS)); - assert!(q.contains(XhciQuirks::RESET_ON_RESUME)); - } - - #[test] - fn unknown_vendor_returns_no_quirks() { - let q = lookup_quirks(0x1234, 0x5678, 0); - assert_eq!(q, XhciQuirks::default()); - } - - #[test] - fn qemu_emulated_redox_vid_has_avoid_bei() { - let q = lookup_quirks(vendor::REDOX_OS_VENDOR, 0, 0); - assert!(q.contains(XhciQuirks::AVOID_BEI)); - } +/// Look up per-controller quirks for an xHCI host controller. +/// +/// Delegates to `redox_driver_sys::quirks::lookup_xhci_controller_quirks_full`, +/// which consults the compiled-in Linux 7.1 quirk table +/// (`xhci_table::XHCI_QUIRK_TABLE`). Layer 2 (TOML `/etc/quirks.d/`) and +/// Layer 3 (DMI) overrides for xHCI will be wired in a follow-up. +/// +/// `revision` is the 8-bit PCI revision (config offset 8). `hci_version` +/// is the 16-bit xHCI spec version read from HCIVERSION (MMIO offset +/// 0x04). Both must be the real hardware values read from PCI config +/// space and the mapped MMIO capability registers respectively. +/// Two table entries depend on `hci_version`: AMD_0x96_HOST (matches +/// `hci_version == 0x96`) and DEFAULT_PM_RUNTIME_ALLOW (matches +/// `hci_version >= 0x120`). +pub fn lookup_quirks( + vendor: u16, + device: u16, + revision: u8, + hci_version: u16, +) -> XhciQuirks { + redox_driver_sys::quirks::lookup_xhci_controller_quirks_full( + vendor, + device, + revision, + hci_version, + None, + ) }