xhcid: converge xHCI quirks to canonical redox-driver-sys path

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
This commit is contained in:
Red Bear OS
2026-07-18 21:52:05 +09:00
parent ad4bedd46d
commit 9fc1947d53
4 changed files with 189 additions and 320 deletions
Generated
+83 -9
View File
@@ -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",
]
+1
View File
@@ -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
+44 -26
View File
@@ -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<File>, InterruptMethod) {
fn get_int_method(
pcid_handle: &mut PciFunctionHandle,
quirks: xhci::quirks::XhciQuirks,
) -> (Option<File>, 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<File>, Interru
//TODO: MSI on non-x86_64?
#[cfg(not(target_arch = "x86_64"))]
fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option<File>, InterruptMethod) {
fn get_int_method(
pcid_handle: &mut PciFunctionHandle,
quirks: xhci::quirks::XhciQuirks,
) -> (Option<File>, 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<const N: usize>(
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");
+61 -285
View File
@@ -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 251517).
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,
)
}