Files
Nomarchy/pkgs/nomarchy-install/hardware-db.sh
Bernardo Magri cdfe92a089 feat(hardware): hide dual-sensor webcam IR node (fixes dark image)
A built-in RGB+IR webcam (common on recent ThinkPads, e.g. the T14s AMD
Gen 4) exposes its IR face-unlock sensor as a second, identically-named
"Integrated Camera"; apps that pick it show a dark, 8-bit-greyscale
image. nomarchy.hardware.camera.hideIrSensor disables that node on the
V4L2 PipeWire path (WirePlumber monitor.v4l2.rules) so only the colour
camera is offered, matched by card name (overridable irMatch).

- libcamera is left untouched: an external camera you plug in is never
  affected, and surgical internal-only libcamera scoping isn't possible
  (its distinguishing props bind after the monitor rule runs).
- Only the PipeWire node is hidden; the kernel /dev/video* stays open,
  so Howdy face-unlock still reads the IR sensor directly.
- The installer's hardware-db.sh auto-detects a paired RGB+IR webcam
  from /sys/class/video4linux/*/name and bakes the toggle into
  system.nix; commented example added to the downstream template.

Validated live on a T14s: collapses the four enumerated "Integrated
Camera" entries to one colour source; the exact shipped regex disables
the IR node (confirmed in the WirePlumber log) and leaves libcamera
intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 00:39:49 +01:00

267 lines
12 KiB
Bash

# Nomarchy hardware database — sourced by nomarchy-install.
#
# Flat table mapping detected DMI fields to nixos-hardware module names
# (the strings nomarchy.lib.mkFlake's hardwareProfile accepts). Ported
# from the previous iteration (installer/hardware-db.sh at 3bdfc35).
#
# Format (pipe-separated):
# sys_vendor_regex | product_regex | nixos_hardware_module
#
# - Matching is bash `[[ =~ ]]` regex, case-insensitive.
# - First match wins — put more specific entries (e.g. exact model years)
# above broader fallbacks.
#
# To add a new device: grab `/sys/class/dmi/id/product_name` on the
# target machine and add a line. Module names must exist in
# nixos-hardware.nixosModules (mkFlake fails with suggestions if not).
HARDWARE_DB=(
# Framework ---------------------------------------------------------------
# Module names follow nixos-hardware's actual attrs — for Framework 13
# the per-generation modules dropped the "13-" prefix.
"Framework|Laptop 16.*AMD|framework-16-7040-amd"
"Framework|Laptop 16.*Ryzen AI 300|framework-16-amd-ai-300-series"
"Framework|Laptop 13.*Ryzen AI 300|framework-amd-ai-300-series"
"Framework|Laptop 13.*Ryzen 7040|framework-13-7040-amd"
"Framework|Laptop 13.*Core Ultra|framework-intel-core-ultra-series1"
"Framework|Laptop 13.*13th Gen Intel|framework-13th-gen-intel"
"Framework|Laptop 13.*12th Gen Intel|framework-12th-gen-intel"
"Framework|Laptop 13.*11th Gen Intel|framework-11th-gen-intel"
"Framework|Laptop \(13.*|framework"
# Dell XPS / Precision / Latitude ----------------------------------------
"Dell|XPS 15 9500|dell-xps-15-9500"
"Dell|XPS 15 9510|dell-xps-15-9510"
"Dell|XPS 15 9520|dell-xps-15-9520"
"Dell|XPS 13 9310|dell-xps-13-9310"
"Dell|XPS 13 9370|dell-xps-13-9370"
"Dell|XPS 13 9380|dell-xps-13-9380"
"Dell|XPS 13 7390|dell-xps-13-7390"
"Dell|Precision 5530|dell-precision-5530"
"Dell|Latitude 7490|dell-latitude-7490"
"Dell|Latitude 7430|dell-latitude-7430"
"Dell|Latitude 7420|dell-latitude-7420"
# Lenovo ThinkPad --------------------------------------------------------
# X1 Carbon: the per-gen modules are named "x1-Nth-gen", not
# "x1-carbon-genN" — the X1 series IS the Carbon line.
"LENOVO|ThinkPad X1 Carbon Gen 11|lenovo-thinkpad-x1-11th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 10|lenovo-thinkpad-x1-10th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 9|lenovo-thinkpad-x1-9th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 7|lenovo-thinkpad-x1-7th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 6|lenovo-thinkpad-x1-6th-gen"
"LENOVO|ThinkPad X1 Extreme|lenovo-thinkpad-x1-extreme"
"LENOVO|ThinkPad X1 Nano|lenovo-thinkpad-x1-nano-gen1"
"LENOVO|ThinkPad T14 Gen 3|lenovo-thinkpad-t14-amd-gen3"
"LENOVO|ThinkPad T14 Gen 2|lenovo-thinkpad-t14-amd-gen2"
"LENOVO|ThinkPad T14 Gen 1|lenovo-thinkpad-t14-amd-gen1"
"LENOVO|ThinkPad T480|lenovo-thinkpad-t480"
"LENOVO|ThinkPad L13|lenovo-thinkpad-l13"
"LENOVO|ThinkPad P14s.*Gen 5|lenovo-thinkpad-p14s-amd-gen5"
"LENOVO|ThinkPad P14s.*Gen 4|lenovo-thinkpad-p14s-amd-gen4"
"LENOVO|ThinkPad P14s.*Gen 3|lenovo-thinkpad-p14s-amd-gen3"
# Microsoft Surface ------------------------------------------------------
# nixos-hardware ships per-chip modules, not per-revision.
"Microsoft|Surface Pro 10|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 9|microsoft-surface-pro-9"
"Microsoft|Surface Pro 8|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 7|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 6|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 3|microsoft-surface-pro-3"
"Microsoft|Surface Laptop.*Ryzen|microsoft-surface-laptop-amd"
"Microsoft|Surface Go|microsoft-surface-go"
# ASUS ROG Ally / Zephyrus / Strix ---------------------------------------
"ASUS.*|RC71L|asus-ally-rc71l"
"ASUS.*|RC72LA|asus-ally-rc71l"
"ASUS.*|ROG Ally|asus-ally-rc71l"
"ASUS.*|ROG Zephyrus G14.*GA402X|asus-zephyrus-ga402x"
"ASUS.*|ROG Zephyrus G14.*GA402|asus-zephyrus-ga402"
"ASUS.*|ROG Zephyrus G14.*GA401|asus-zephyrus-ga401"
"ASUS.*|ROG Zephyrus G15|asus-zephyrus-ga503"
"ASUS.*|ROG Zephyrus.*GU603|asus-zephyrus-gu603h"
"ASUS.*|ROG Strix G513|asus-rog-strix-g513im"
"ASUS.*|ROG Strix G533|asus-rog-strix-g533zw"
# Apple (T2 Intel; M-series is aarch64 — out of scope) -------------------
"Apple.*|MacBookPro15|apple-t2"
"Apple.*|MacBookPro16|apple-t2"
"Apple.*|MacBookAir8|apple-t2"
"Apple.*|MacBookAir9|apple-t2"
"Apple.*|iMac19|apple-t2"
"Apple.*|iMacPro1|apple-t2"
# System76 ---------------------------------------------------------------
"System76|Oryx Pro.*|system76"
"System76|Lemur Pro.*|system76"
"System76|Darter Pro.*|system76"
"System76|Galago Pro.*|system76"
"System76|Pangolin.*|system76"
# Known-unsupported (so contributors know they're not missing):
# - Valve Steam Deck: Jovian-NixOS territory, not nixos-hardware.
# - Snapdragon X laptops / Raspberry Pi: aarch64; installer is x86_64.
)
# ----------------------------------------------------------------------------
# nomarchy_detect_hw — inspects DMI + lspci + /sys, emits one line per hit:
# MODULE <nixos-hardware module name>
# DETAIL <human-readable explanation>
# Returns 0 if at least one module was detected.
# ----------------------------------------------------------------------------
nomarchy_detect_hw() {
local sys_vendor product_name cpu_vendor
local nvidia=0 amdgpu=0 intelgpu=0
sys_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || echo "")
product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "")
cpu_vendor=$(lscpu 2>/dev/null | awk -F: '/Vendor ID/{gsub(/ /,"",$2); print $2; exit}')
local detected=0
# CPU
case "$cpu_vendor" in
AuthenticAMD) echo "MODULE common-cpu-amd"; echo "DETAIL cpu: AMD"; detected=1 ;;
GenuineIntel) echo "MODULE common-cpu-intel"; echo "DETAIL cpu: Intel"; detected=1 ;;
esac
# GPU (lspci may list several; report all)
if command -v lspci >/dev/null 2>&1; then
local gpu_line
while IFS= read -r gpu_line; do
case "$gpu_line" in
*"[10de:"*|*"NVIDIA"*) nvidia=1 ;;
*"[1002:"*|*"AMD/ATI"*|*"Advanced Micro Devices"*) amdgpu=1 ;;
*"[8086:"*|*"Intel Corporation"*) intelgpu=1 ;;
esac
done < <(lspci -nn 2>/dev/null | grep -iE 'vga|3d|display')
(( nvidia )) && { echo "MODULE common-gpu-nvidia"; echo "DETAIL gpu: NVIDIA"; detected=1; }
(( amdgpu )) && { echo "MODULE common-gpu-amd"; echo "DETAIL gpu: AMD"; detected=1; }
(( intelgpu )) && { echo "MODULE common-gpu-intel"; echo "DETAIL gpu: Intel"; detected=1; }
fi
# ── nomarchy.hardware.* enablement — the gap ABOVE the nixos-hardware
# commons (GuC/HuC, amd-pstate, the AMD VA-API env, GPU-compute runtimes,
# fprintd, the NPU driver). Emitted as NOMARCHY lines the installer turns
# into nomarchy.hardware.* in system.nix; safe bits active, heavy opt-ins
# commented.
if [[ "$cpu_vendor" == "GenuineIntel" || $intelgpu -eq 1 ]]; then
echo "NOMARCHY hardware.intel.enable=true"
echo "DETAIL nomarchy.hardware.intel: GuC/HuC on; GPU-compute runtime opt-in"
# i915.enable_guc applies to the i915 driver only. The newer `xe` driver
# (Lunar Lake / Battlemage / Panther Lake, recent Xe GPUs) enables GuC by
# default and ignores the param — so turn the toggle off when xe is bound.
if lspci -k 2>/dev/null | grep -qiE 'driver in use: xe\b'; then
echo "NOMARCHY hardware.intel.guc=false"
echo "DETAIL Intel GPU on the xe driver → GuC default-on (i915 param skipped)"
fi
fi
if [[ "$cpu_vendor" == "AuthenticAMD" || $amdgpu -eq 1 ]]; then
echo "NOMARCHY hardware.amd.enable=true"
echo "DETAIL nomarchy.hardware.amd: amd-pstate + VA-API on; ROCm opt-in"
fi
# Fingerprint reader — libfprint's common USB vendor IDs (Goodix,
# Synaptics/Validity, Elan, EgisTec, Upek, AuthenTec, FocalTech, NB).
local have_fp=0
if command -v lsusb >/dev/null 2>&1; then
local vid
for vid in 27c6 06cb 138a 04f3 1c7a 147e 08ff 2808 1fae; do
lsusb 2>/dev/null | grep -qiE "ID ${vid}:" && { have_fp=1; break; }
done
else
local f
for f in /sys/bus/usb/devices/*/idVendor; do
[[ -e "$f" ]] || continue
case "$(cat "$f" 2>/dev/null)" in
27c6|06cb|138a|04f3|1c7a|147e|08ff|2808|1fae) have_fp=1; break ;;
esac
done
fi
if [[ $have_fp -eq 1 ]]; then
echo "NOMARCHY hardware.fingerprint.enable=true"
echo "DETAIL fingerprint reader detected → fprintd (PAM login/sudo opt-in)"
fi
# Dual-sensor webcam (RGB + IR face-unlock). Such modules expose the IR
# sensor as a SECOND, identically-named "Integrated Camera"; an app that
# picks it shows a dark, 8-bit-greyscale image. When an IR-companion node
# sits alongside a normal camera node, enable hiding the IR one from
# PipeWire (the colour camera stays; the kernel /dev/video* is untouched, so
# Howdy can still read the IR sensor directly). Read from /sys — no
# v4l2-ctl needed in the installer env.
local cam_name have_ir_cam=0 have_color_cam=0 vf
local ir_re='Integrated I|IR Camera|Infrared'
for vf in /sys/class/video4linux/video*/name; do
[[ -e "$vf" ]] || continue
cam_name=$(cat "$vf" 2>/dev/null)
shopt -s nocasematch
if [[ "$cam_name" =~ $ir_re ]]; then
have_ir_cam=1
elif [[ "$cam_name" =~ (Camera|Webcam) ]]; then
have_color_cam=1
fi
shopt -u nocasematch
done
if [[ $have_ir_cam -eq 1 && $have_color_cam -eq 1 ]]; then
echo "NOMARCHY hardware.camera.hideIrSensor=true"
echo "DETAIL dual-sensor webcam (RGB+IR) → hide the IR node so apps get the colour camera"
fi
# NPU (detect-only → a commented, experimental opt-in). Match the PCI
# "Processing accelerators" class [1200] (or accelerator keywords) and
# attribute by vendor — future-proof vs a per-device-ID list, so new gens
# (e.g. Panther Lake) are caught without a code change. Known IDs kept for
# reference: Intel VPU 8086:{7d1d,643e,ad1d,b03e}, AMD XDNA 1022:1502.
if command -v lspci >/dev/null 2>&1; then
local npu_line
npu_line=$(lspci -nn 2>/dev/null \
| grep -iE '\[1200\]|processing accelerat|neural|\[8086:(7d1d|643e|ad1d|b03e)\]|\[1022:1502\]' \
| head -1)
case "$npu_line" in
*"[8086:"*|*Intel*)
echo "NOMARCHY-NPU intel"; echo "DETAIL Intel NPU detected (opt-in, experimental)" ;;
*"[1022:"*|*"Advanced Micro Devices"*|*AMD*)
echo "NOMARCHY-NPU amd"; echo "DETAIL AMD XDNA NPU detected (opt-in, experimental)" ;;
esac
fi
# Chassis (glob test, not compgen — nixpkgs' non-interactive bash
# is built without the completion builtins)
local bats=(/sys/class/power_supply/BAT*)
if [[ -e "${bats[0]}" ]]; then
echo "MODULE common-pc-laptop"
echo "DETAIL chassis: laptop (battery present)"
else
echo "MODULE common-pc"
echo "DETAIL chassis: desktop"
fi
detected=1
# SSD
local rotational
rotational=$(cat /sys/block/nvme0n1/queue/rotational 2>/dev/null \
|| cat /sys/block/sda/queue/rotational 2>/dev/null || echo "")
if [[ "$rotational" == "0" ]]; then
echo "MODULE common-pc-ssd"
echo "DETAIL storage: SSD"
fi
# Model-specific match from the DB
local entry v_re p_re mod
for entry in "${HARDWARE_DB[@]}"; do
IFS='|' read -r v_re p_re mod <<< "$entry"
shopt -s nocasematch
if [[ "$sys_vendor" =~ $v_re ]] && [[ "$product_name" =~ $p_re ]]; then
shopt -u nocasematch
echo "MODULE $mod"
echo "DETAIL model: $sys_vendor $product_name$mod"
break
fi
shopt -u nocasematch
done
return $(( 1 - detected ))
}