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:
Generated
+83
-9
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user