From 00e1c9ea16be2927c01f5fad5f955e5b3f01db3e Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Sun, 7 Jun 2026 21:41:07 +0300 Subject: [PATCH] quirks: Platform DMI dispatch infrastructure + 31-entry data file (R13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase R13 (2026-06-07) — Laptop/Embedded DMI quirks. The data side lands now; consumer wiring in inputd, thermald, and redbear-upower is a follow-up. Changes: 1. PlatformDmiQuirkFlags (mod.rs:286) with 7 bits: TOUCHSCREEN, HOTKEY, ACCELEROMETER, ALS, TABLET_MODE, BATTERY, PROXIMITY. 2. PlatformSubsystem enum (mod.rs:316) — dispatch label for TOML and consumers. from_name() for parsing, as_str() for logging, flag_bit() for converting to the bitflags. 3. PlatformDmiQuirkRule (dmi.rs:566) — DMI match + subsystem. Each entry fires one subsystem. 4. load_platform_dmi_quirks() (dmi.rs:583) — reads live SMBIOS, returns Vec of all rules that fire. Falls back to empty vector if DMI data is unavailable. 5. read_toml_platform_dmi_entries + parse_platform_dmi_toml (toml_loader.rs) — new [[platform_dmi_quirk]] TOML table with sub-table + string. Unknown subsystem names log a warning and skip. 6. 1 new unit test: phase_r13_platform_subsystem_from_name_round_trip exercises all 7 subsystems. 124/124 tests pass. 7. quirks.d/80-platform-x86.toml (201 lines) — 31 DMI entries covering: touchscreen (3): Chuwi Hi8 / Hi8 Pro / Hi10 Plus tablet_mode (8): Acer, Asus, Lenovo convertibles hotkey (12): Dynabook, GPD, AYA NEO, AYN, OneXPlayer, Valve Steam Deck family accelerometer (5): GPD WIN series, AYA NEO 2, Valve battery (2): Samsung Galaxy Book, Chuwi Hi10 Plus Targeted at Red Bear's 2026 hardware scope. The full Linux 7.1 platform/x86 DMI surface is ~1153 entries; this is a focused subset that maps to actual Red Bear targets. cargo test: 124/124 (was 123, +1 for the new test). cargo check: clean. Consumer wiring: load_platform_dmi_quirks() is callable today from inputd, thermald, redbear-upower. Each consumer can filter on rule.subsystem to dispatch the appropriate behavior. This is a follow-up commit. --- .../redox-driver-sys/source/src/quirks/dmi.rs | 53 ++++- .../redox-driver-sys/source/src/quirks/mod.rs | 76 +++++++ .../source/src/quirks/toml_loader.rs | 71 ++++++- .../source/quirks.d/80-platform-x86.toml | 201 ++++++++++++++++++ 4 files changed, 397 insertions(+), 4 deletions(-) create mode 100644 local/recipes/system/redbear-quirks/source/quirks.d/80-platform-x86.toml diff --git a/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs b/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs index bfc6da8bcc..6e5f0e1597 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs @@ -1,5 +1,5 @@ use super::{ - toml_loader, AcpiQuirkFlags, DrmPanelOrientation, PciQuirkFlags, + toml_loader, AcpiQuirkFlags, DrmPanelOrientation, PciQuirkFlags, PlatformSubsystem, XhciControllerQuirkFlags, PCI_QUIRK_ANY_ID, }; use crate::pci::PciDeviceInfo; @@ -543,6 +543,36 @@ pub fn load_drm_panel_orientation() -> DrmPanelOrientation { .unwrap_or(DrmPanelOrientation::Normal) } +/// One DMI-conditioned platform / subsystem dispatch rule. +/// +/// Phase R13 (2026-06-07). Sourced from Linux 7.1 +/// `drivers/platform/x86/*` DMI tables (touchscreen_dmi.c, +/// wireless-hotkey.c, etc.). +#[derive(Clone, Debug)] +pub struct PlatformDmiQuirkRule { + pub dmi_match: DmiMatchRule, + pub subsystem: PlatformSubsystem, +} + +/// Look up the platform DMI rules that fire for the host system, +/// grouped by subsystem. Returns an empty vector if DMI data is +/// unavailable or no rule matches. +pub fn load_platform_dmi_quirks() -> Vec { + let dmi_info = match read_dmi_info() { + Ok(info) => info, + Err(()) => return Vec::new(), + }; + let mut out = Vec::new(); + for rule in toml_loader::read_toml_platform_dmi_entries() + .unwrap_or_default() + { + if rule.dmi_match.matches(&dmi_info) { + out.push(rule); + } + } + out +} + /// Walk a slice of `DmiAcpiQuirkRule` and OR-accumulate the flags /// of every rule whose DMI match succeeds. Mirrors /// `apply_dmi_xhci_quirk_rules` (R7-B) and `apply_dmi_pci_quirks`. @@ -888,4 +918,25 @@ mod tests { ); assert!(DrmPanelOrientation::from_name("Bogus").is_none()); } + + /// Phase R13 — `PlatformSubsystem::from_name` round-trips all + /// seven subsystem identifiers. + #[test] + fn phase_r13_platform_subsystem_from_name_round_trip() { + for name in [ + "touchscreen", + "hotkey", + "accelerometer", + "als", + "tablet_mode", + "battery", + "proximity", + ] { + assert_eq!( + PlatformSubsystem::from_name(name).unwrap().as_str(), + name + ); + } + assert!(PlatformSubsystem::from_name("not_a_subsystem").is_none()); + } } diff --git a/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs b/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs index 025dab1470..75b5701ce2 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs @@ -291,6 +291,82 @@ impl DrmPanelOrientation { } } +bitflags::bitflags! { + /// Per-subsystem platform DMI quirk flags. Each bit names a + /// consumer subsystem that the DMI rule fires for; consumers + /// (inputd, acpid, thermald, etc.) read the bit and react + /// accordingly. + /// + /// Phase R13 (2026-06-07) — initial set covers the + /// sub-subsystems Red Bear actively consumes from + /// `drivers/platform/x86/`. New bits can be added as + /// consumer wiring lands. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct PlatformDmiQuirkFlags: u64 { + const TOUCHSCREEN = 1 << 0; // drivers/platform/x86/touchscreen_dmi.c + const HOTKEY = 1 << 1; // drivers/platform/x86/wireless-hotkey.c + const ACCELEROMETER = 1 << 2; // drivers/platform/x86/dual_accel_detect.h + const ALS = 1 << 3; // drivers/platform/x86/x86-android-tablets/ — ALS + const TABLET_MODE = 1 << 4; // convertible / 360° hinge detection + const BATTERY = 1 << 5; // battery reporting quirks (separate from ACPI) + const PROXIMITY = 1 << 6; // proximity sensor quirks + } +} + +/// Sub-system dispatch label used in `[[platform_dmi_quirk]]` TOML +/// entries and by consumers to filter the flag set. Stable +/// string identifiers (lowercase snake_case) so the TOML side +/// is robust against enum reorderings. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PlatformSubsystem { + Touchscreen, + Hotkey, + Accelerometer, + Als, + TabletMode, + Battery, + Proximity, +} + +impl PlatformSubsystem { + pub fn from_name(name: &str) -> Option { + match name { + "touchscreen" => Some(Self::Touchscreen), + "hotkey" => Some(Self::Hotkey), + "accelerometer" => Some(Self::Accelerometer), + "als" => Some(Self::Als), + "tablet_mode" => Some(Self::TabletMode), + "battery" => Some(Self::Battery), + "proximity" => Some(Self::Proximity), + _ => None, + } + } + + pub fn as_str(&self) -> &'static str { + match self { + Self::Touchscreen => "touchscreen", + Self::Hotkey => "hotkey", + Self::Accelerometer => "accelerometer", + Self::Als => "als", + Self::TabletMode => "tablet_mode", + Self::Battery => "battery", + Self::Proximity => "proximity", + } + } + + pub fn flag_bit(&self) -> PlatformDmiQuirkFlags { + match self { + Self::Touchscreen => PlatformDmiQuirkFlags::TOUCHSCREEN, + Self::Hotkey => PlatformDmiQuirkFlags::HOTKEY, + Self::Accelerometer => PlatformDmiQuirkFlags::ACCELEROMETER, + Self::Als => PlatformDmiQuirkFlags::ALS, + Self::TabletMode => PlatformDmiQuirkFlags::TABLET_MODE, + Self::Battery => PlatformDmiQuirkFlags::BATTERY, + Self::Proximity => PlatformDmiQuirkFlags::PROXIMITY, + } + } +} + /// Wildcard value for PCI ID matching. pub const PCI_QUIRK_ANY_ID: u16 = 0xFFFF; diff --git a/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs b/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs index 53a4c93ac0..ac6140bb93 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs @@ -1,8 +1,9 @@ use super::{ - dmi::{self, DmiAcpiQuirkRule, DmiDrmPanelQuirkRule, DmiInfo, DmiMatchRule, DmiPciQuirkRule, DmiXhciQuirkRule}, + dmi::{self, DmiAcpiQuirkRule, DmiDrmPanelQuirkRule, DmiInfo, DmiMatchRule, DmiPciQuirkRule, DmiXhciQuirkRule, PlatformDmiQuirkRule}, AcpiQuirkFlags, DrmPanelOrientation, HidQuirkEntry, HidQuirkFlags, MaskWidth, - PciQuirkEntry, PciQuirkFlags, PciQuirkLookup, PciQuirkPhase, QuirkAction, UsbQuirkEntry, - UsbQuirkFlags, XhciControllerQuirk, XhciControllerQuirkFlags, PCI_QUIRK_ANY_ID, + PciQuirkEntry, PciQuirkFlags, PciQuirkLookup, PciQuirkPhase, PlatformSubsystem, + QuirkAction, UsbQuirkEntry, UsbQuirkFlags, XhciControllerQuirk, + XhciControllerQuirkFlags, PCI_QUIRK_ANY_ID, }; use crate::pci::PciDeviceInfo; use std::borrow::Cow; @@ -331,6 +332,70 @@ pub(crate) fn load_drm_panel_orientation( Ok(DrmPanelOrientation::Normal) } +pub(crate) fn read_toml_platform_dmi_entries() -> std::io::Result> { + let mut entries = Vec::new(); + for path in sorted_toml_files(QUIRKS_DIR)? { + let path_str = path.display().to_string(); + let content = match std::fs::read_to_string(&path) { + Ok(c) => c, + Err(e) => { + log::warn!("quirks: failed to read {path_str}: {e}"); + continue; + } + }; + let doc = match content.parse::() { + Ok(d) => d, + Err(e) => { + log::warn!("quirks: failed to parse {path_str}: {e}"); + continue; + } + }; + parse_platform_dmi_toml(&doc, &mut entries, &path_str); + } + Ok(entries) +} + +fn parse_platform_dmi_toml( + doc: &toml::Value, + out: &mut Vec, + path: &str, +) { + let Some(arr) = doc.get("platform_dmi_quirk").and_then(|v| v.as_array()) else { + return; + }; + for item in arr { + let Some(table) = item.as_table() else { + log::warn!("quirks: {path}: platform_dmi_quirk entry is not a table, skipping"); + continue; + }; + let Some(match_table) = table.get("match").and_then(|v| v.as_table()) else { + log::warn!( + "quirks: {path}: platform_dmi_quirk entry is missing match table, skipping" + ); + continue; + }; + let Some(dmi_match) = parse_dmi_match_rule(match_table, path) else { + continue; + }; + let Some(subsystem_str) = table.get("subsystem").and_then(|v| v.as_str()) else { + log::warn!( + "quirks: {path}: platform_dmi_quirk entry is missing subsystem string, skipping" + ); + continue; + }; + let Some(subsystem) = PlatformSubsystem::from_name(subsystem_str) else { + log::warn!( + "quirks: {path}: unknown platform_dmi_quirk subsystem {subsystem_str:?}, skipping" + ); + continue; + }; + out.push(PlatformDmiQuirkRule { + dmi_match, + subsystem, + }); + } +} + fn bounded_u16(val: &toml::Value, field: &str, path: &str) -> Option { match val.as_integer() { Some(v) => u16::try_from(v).ok().or_else(|| { diff --git a/local/recipes/system/redbear-quirks/source/quirks.d/80-platform-x86.toml b/local/recipes/system/redbear-quirks/source/quirks.d/80-platform-x86.toml new file mode 100644 index 0000000000..4e51f76c24 --- /dev/null +++ b/local/recipes/system/redbear-quirks/source/quirks.d/80-platform-x86.toml @@ -0,0 +1,201 @@ +# Platform DMI dispatch rules — DMI-based, subsystem-tagged. +# Mined from Linux 7.1 `drivers/platform/x86/*` DMI tables. +# Each `[[platform_dmi_quirk]]` entry maps a DMI fingerprint to +# a single `subsystem` (touchscreen / hotkey / accelerometer / +# als / tablet_mode / battery / proximity). Consumers +# (inputd, acpid, thermald, redbear-upower, etc.) read the +# firing rules and react accordingly. +# +# Phase R13 (2026-06-07) initial commit. The data file is +# sparse by design — the audit estimated ~1153 Linux 7.1 +# entries, but Red Bear's hardware scope is much narrower. +# Focus on hardware that ships in 2026 and that Red Bear +# actually targets: Framework, GPD, AYANEO, AYN, Dell, +# Lenovo, Asus, Valve, Chuwi, Acer. +# +# Consumers currently in the tree that could read this: +# - inputd: touchscreen, hotkey, accelerometer, tablet_mode +# - acpid: button, lid (related, not a direct match) +# - thermald: als (ambient light → thermal profile) +# - redbear-upower: battery + +# Framework Laptop 13 / 16 — well-behaved; no quirks needed. +# Listed here as documentation of the absence of quirks. + +# Chuwi Hi8 (CWI506 / ilife S806) — touchscreen needs I2C-HID fallback +[[platform_dmi_quirk]] +subsystem = "touchscreen" +match.sys_vendor = "ilife" +match.product_name = "S806" + +# Chuwi Hi8 Pro (CWI513 / Hampoo X1D3_C806N) — same family +[[platform_dmi_quirk]] +subsystem = "touchscreen" +match.sys_vendor = "Hampoo" +match.product_name = "X1D3_C806N" + +# Chuwi Hi10 Plus (CWI527) — touchscreen quirks +[[platform_dmi_quirk]] +subsystem = "touchscreen" +match.board_vendor = "Hampoo" +match.product_name = "Hi10 Pro tablet" + +# Acer Switch V 10 (SW5-017) — convertible / tablet_mode +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "Acer" +match.product_name = "SW5-017" + +# Acer One 10 (S1003) — convertible / tablet_mode +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "Acer" +match.product_name = "One S1003" + +# Asus T100HAN — convertible / tablet_mode +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "ASUSTeK COMPUTER INC." +match.product_name = "T100HAN" + +# Asus T101HA / T103HAF — convertible / tablet_mode +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "ASUSTeK COMPUTER INC." +match.product_name = "T101HA" + +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "ASUSTeK COMPUTER INC." +match.product_name = "T103HAF" + +# Lenovo Ideapad Miix 320 (80SG) — convertible +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "LENOVO" +match.product_name = "80SG" + +# Lenovo Ideapad D330 (80XF) — convertible +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "LENOVO" +match.product_name = "80XF" + +# Lenovo Yoga Book (YB1-X91) — convertible + Wacom touchscreen +[[platform_dmi_quirk]] +subsystem = "tablet_mode" +match.sys_vendor = "Intel Corporation" +match.product_name = "CHERRYVIEW D1 PLATFORM" + +# Dynabook K50 — wireless hotkey quirks +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "Dynabook Inc." +match.product_name = "dynabook K50/FR" + +# GPD MicroPC — wireless hotkey (airplane-mode) +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "GPD" +match.product_name = "MicroPC" + +# GPD Pocket 2/3 — wireless hotkey +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "GPD" +match.product_name = "Pocket 2" + +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "GPD" +match.product_name = "Pocket 3" + +# AYA NEO 2 / AIR / SLIDE — wireless hotkey (airplane-mode) +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "AYANEO" +match.product_name = "AYANEO 2" + +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "AYANEO" +match.product_name = "AIR" + +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "AYANEO" +match.product_name = "SLIDE" + +# AYN Loki Max / Zero — wireless hotkey +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "ayn" +match.product_name = "Loki Max" + +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "ayn" +match.product_name = "Loki Zero" + +# OneXPlayer — wireless hotkey +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "ONE-NETBOOK TECHNOLOGY CO., LTD." +match.product_name = "ONE XPLAYER" + +# Valve Steam Deck (Jupiter) — wireless hotkey +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "Valve" +match.product_name = "Jupiter" + +# Valve Steam Deck OLED (Galileo) +[[platform_dmi_quirk]] +subsystem = "hotkey" +match.sys_vendor = "Valve" +match.product_name = "Galileo" + +# GPD WIN2/3/4 — accelerometer (auto-rotation in handheld mode) +[[platform_dmi_quirk]] +subsystem = "accelerometer" +match.sys_vendor = "GPD" +match.product_name = "WIN2" + +[[platform_dmi_quirk]] +subsystem = "accelerometer" +match.sys_vendor = "GPD" +match.product_name = "G1618-03" + +[[platform_dmi_quirk]] +subsystem = "accelerometer" +match.sys_vendor = "GPD" +match.product_name = "G1618-04" + +# AYA NEO 2 — accelerometer +[[platform_dmi_quirk]] +subsystem = "accelerometer" +match.sys_vendor = "AYANEO" +match.product_name = "AYANEO 2" + +# Valve Steam Deck — accelerometer +[[platform_dmi_quirk]] +subsystem = "accelerometer" +match.sys_vendor = "Valve" +match.product_name = "Jupiter" + +[[platform_dmi_quirk]] +subsystem = "accelerometer" +match.sys_vendor = "Valve" +match.product_name = "Galileo" + +# Samsung Galaxy Book 10.6 — battery reporting quirks +[[platform_dmi_quirk]] +subsystem = "battery" +match.sys_vendor = "SAMSUNG ELECTRONICS CO., LTD." +match.product_name = "Galaxy Book 10.6" + +# Chuwi Hi10 Plus — battery quirk (BIX broken) +[[platform_dmi_quirk]] +subsystem = "battery" +match.board_vendor = "Hampoo" +match.product_name = "Hi10 Pro tablet"