feat(hardware): auto-detect audio, bluetooth, camera with declarative override
This commit is contained in:
46
README.md
46
README.md
@@ -287,7 +287,7 @@ The bootstrap auto-switches to dracut when `method: tpm2` is set. Override via `
|
|||||||
| `desktop.*` | dict | see below | Desktop environment settings (see [4.2.5](#425-systemfeaturesdesktop)) |
|
| `desktop.*` | dict | see below | Desktop environment settings (see [4.2.5](#425-systemfeaturesdesktop)) |
|
||||||
| `firmware.*` | dict | see below | Vendor firmware blobs and CPU microcode (see [4.2.6](#426-systemfeaturesfirmware)) |
|
| `firmware.*` | dict | see below | Vendor firmware blobs and CPU microcode (see [4.2.6](#426-systemfeaturesfirmware)) |
|
||||||
| `gpu.*` | dict | see below | Mesa/Vulkan and per-vendor GPU userspace (see [4.2.7](#427-systemfeaturesgpu)) |
|
| `gpu.*` | dict | see below | Mesa/Vulkan and per-vendor GPU userspace (see [4.2.7](#427-systemfeaturesgpu)) |
|
||||||
| `peripherals.*` | dict | see below | Fingerprint readers, webcams, DisplayLink (see [4.2.8](#428-systemfeaturesperipherals)) |
|
| `peripherals.*` | dict | see below | Fingerprint, camera, audio, bluetooth, DisplayLink (see [4.2.8](#428-systemfeaturesperipherals)) |
|
||||||
| `hardware.*` | dict | see below | Hardware-detection profile override (see [4.2.9](#429-systemfeatureshardware)) |
|
| `hardware.*` | dict | see below | Hardware-detection profile override (see [4.2.9](#429-systemfeatureshardware)) |
|
||||||
|
|
||||||
**Initramfs generator auto-detection:** RedHat -> dracut, Arch -> mkinitcpio, Debian/Ubuntu -> initramfs-tools.
|
**Initramfs generator auto-detection:** RedHat -> dracut, Arch -> mkinitcpio, Debian/Ubuntu -> initramfs-tools.
|
||||||
@@ -359,13 +359,20 @@ automatically when needed. Debian uses `nvidia-driver` from the `non-free` compo
|
|||||||
managed `sources.list`). Ubuntu uses `restricted`. Arch ships both `nvidia-open-dkms` and `nvidia-dkms` in
|
managed `sources.list`). Ubuntu uses `restricted`. Arch ships both `nvidia-open-dkms` and `nvidia-dkms` in
|
||||||
the `extra` repository - no third-party setup required.
|
the `extra` repository - no third-party setup required.
|
||||||
|
|
||||||
|
> **Known limitation - Nvidia on Enterprise Linux (AlmaLinux/Rocky/RHEL):** the EL `akmod-nvidia*`
|
||||||
|
> packages live in RPMFusion non-free, and the bootstrap only enables RPMFusion automatically on
|
||||||
|
> **Fedora**, not on EL. So Nvidia on a bare EL desktop is best-effort: enable RPMFusion (or supply the
|
||||||
|
> driver repo) out of band, or it falls back to `nouveau`. EL desktops are not a primary target.
|
||||||
|
|
||||||
#### 4.2.8 `system.features.peripherals`
|
#### 4.2.8 `system.features.peripherals`
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
| ------------- | --------------- | ------- | ---------------------------------------------------------- |
|
| ------------- | --------------- | ------- | ---------------------------------------------------------- |
|
||||||
| `enabled` | bool \| `auto` | `auto` | Master switch. `auto` follows `desktop.enabled` |
|
| `enabled` | bool \| `auto` | `auto` | Master switch. `auto` follows `desktop.enabled` |
|
||||||
| `fingerprint` | bool \| `auto` | `auto` | `fprintd`/`libfprint`. `auto` = install when reader detected |
|
| `fingerprint` | bool \| `auto` | `auto` | `fprintd`/`libfprint`. `auto` = install when reader detected |
|
||||||
| `webcam` | bool \| `auto` | `auto` | `v4l-utils` and userspace tooling. `auto` follows `enabled` |
|
| `camera` | bool \| `auto` | `auto` | `v4l-utils` for UVC webcams. `auto` = install when a UVC/IPU6 camera is detected (IPU6 out-of-tree stack is logged, not auto-installed) |
|
||||||
|
| `audio` | bool \| `auto` | `auto` | SOF firmware + ALSA UCM. `auto` = install when an audio device is detected |
|
||||||
|
| `bluetooth` | bool \| `auto` | `auto` | `bluez`. `auto` = install when a Bluetooth controller is detected |
|
||||||
| `displaylink` | bool | `false` | DisplayLink dock support (explicit opt-in; see notes) |
|
| `displaylink` | bool | `false` | DisplayLink dock support (explicit opt-in; see notes) |
|
||||||
|
|
||||||
Fingerprint detection scans `lsusb` for known reader vendor IDs (Synaptics, Validity, Goodix, Elan, Egis,
|
Fingerprint detection scans `lsusb` for known reader vendor IDs (Synaptics, Validity, Goodix, Elan, Egis,
|
||||||
@@ -381,7 +388,8 @@ must still be installed manually from DisplayLink's site after first boot. Arch
|
|||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
| --------- | ---- | ------- | -------------------------------------------------------------------- |
|
| --------- | ---- | ------- | -------------------------------------------------------------------- |
|
||||||
| `profile` | dict | `{}` | Hardware-detection override; empty means autodetect from live host |
|
| `profile` | dict | `{}` | Full override: non-empty SKIPS detection (golden image); empty = autodetect |
|
||||||
|
| group fields | mixed | -- | `cpu`/`gpus`/`wireless`/`audio`/`camera`/`fingerprint`/`bluetooth`/`packages`/`disable`/`kernel_params` MERGE over autodetect (see below) |
|
||||||
|
|
||||||
When empty, hardware is detected at the start of the bootstrap. When set, detection is skipped and the
|
When empty, hardware is detected at the start of the bootstrap. When set, detection is skipped and the
|
||||||
supplied profile drives package selection - this is the **golden-image** flow: bake an image with a fixed
|
supplied profile drives package selection - this is the **golden-image** flow: bake an image with a fixed
|
||||||
@@ -402,6 +410,38 @@ system:
|
|||||||
fingerprint: false # set true to force fprintd install
|
fingerprint: false # set true to force fprintd install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The same keys (minus `profile`) can also be set **directly under `hardware`** as a
|
||||||
|
declarative **hardware group** that MERGES over auto-detection (auto-detect = base; the
|
||||||
|
group supplements/overrides it). Unlike `profile`, which skips detection entirely, the
|
||||||
|
group keeps detection running and layers on top - use it to pin everything a known device
|
||||||
|
needs so nothing is ever under-set.
|
||||||
|
|
||||||
|
| Key | Type | Merge semantics |
|
||||||
|
| ------------------------- | ---- | -------------------------------------------------------- |
|
||||||
|
| `cpu` | str | pin the CPU vendor (overrides detection when non-empty) |
|
||||||
|
| `gpus`/`wireless`/`audio` | list | union with the detected vendor codes |
|
||||||
|
| `camera` | dict | `{uvc, ipu6}` booleans OR'd with detection |
|
||||||
|
| `fingerprint`/`bluetooth` | bool | OR'd with detection (force-on) |
|
||||||
|
| `packages` | dict | per-`os_family` extra packages, added to the install set (deduped; empty entries dropped) |
|
||||||
|
| `disable` | list | feature/vendor names force-off, applied last |
|
||||||
|
| `kernel_params` | list | extra kernel cmdline params, appended to the bootloader |
|
||||||
|
|
||||||
|
Example - a laptop with an Intel IPU6 camera (out-of-tree stack) and a Cirrus amp, pinned
|
||||||
|
in a group's `group_vars`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
system:
|
||||||
|
features:
|
||||||
|
hardware:
|
||||||
|
bluetooth: true # force-on if detection misses the combo card
|
||||||
|
camera:
|
||||||
|
ipu6: true # force the IPU6 path
|
||||||
|
packages: # out-of-tree/AUR bits detection must not auto-install
|
||||||
|
Archlinux: [intel-ipu6-dkms, v4l2-relayd, linux-firmware-cirrus]
|
||||||
|
disable: [displaylink] # never pull DisplayLink on this device
|
||||||
|
kernel_params: ["i915.enable_psr=0"]
|
||||||
|
```
|
||||||
|
|
||||||
### 4.3 `hypervisor` Dictionary
|
### 4.3 `hypervisor` Dictionary
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
---
|
---
|
||||||
# Installs vendor-matched microcode/firmware/GPU/peripheral packages into /mnt
|
|
||||||
# based on hardware_profile_active set by environment/_detect_hardware.yml.
|
|
||||||
- name: Load hardware package definitions
|
- name: Load hardware package definitions
|
||||||
ansible.builtin.include_vars:
|
ansible.builtin.include_vars:
|
||||||
file: hardware.yml
|
file: hardware.yml
|
||||||
@@ -22,7 +20,7 @@
|
|||||||
vars:
|
vars:
|
||||||
_family: "{{ bootstrap_hardware_packages[os_family] }}"
|
_family: "{{ bootstrap_hardware_packages[os_family] }}"
|
||||||
_user_driver: "{{ system_cfg.features.gpu.nvidia_driver | default('auto') }}"
|
_user_driver: "{{ system_cfg.features.gpu.nvidia_driver | default('auto') }}"
|
||||||
_has_nvidia: "{{ 'nvidia' in (hardware_profile_active.gpus | default([])) }}"
|
_has_nvidia: "{{ 'nvidia' in (hardware_profile_active.gpus | default([]) | difference(_hardware_profile_disable | default([]))) }}"
|
||||||
_supports_open: "{{ hardware_profile_active.nvidia_supports_open | default(true) | bool }}"
|
_supports_open: "{{ hardware_profile_active.nvidia_supports_open | default(true) | bool }}"
|
||||||
_open_pkgs: "{{ _family.gpu_nvidia.open | default([]) }}"
|
_open_pkgs: "{{ _family.gpu_nvidia.open | default([]) }}"
|
||||||
_prop_pkgs: "{{ _family.gpu_nvidia.proprietary | default([]) }}"
|
_prop_pkgs: "{{ _family.gpu_nvidia.proprietary | default([]) }}"
|
||||||
@@ -58,101 +56,7 @@
|
|||||||
changed_when: _rpmfusion_result.rc == 0
|
changed_when: _rpmfusion_result.rc == 0
|
||||||
|
|
||||||
- name: Resolve hardware package set
|
- name: Resolve hardware package set
|
||||||
vars:
|
ansible.builtin.include_tasks: _resolve_hardware_packages.yml
|
||||||
_family: "{{ bootstrap_hardware_packages[os_family] }}"
|
|
||||||
_cpu: "{{ hardware_profile_active.cpu | default('') | string }}"
|
|
||||||
_gpus: "{{ hardware_profile_active.gpus | default([]) | list }}"
|
|
||||||
_wifi: "{{ hardware_profile_active.wireless | default([]) | list }}"
|
|
||||||
_fp_detected: "{{ hardware_profile_active.fingerprint | default(false) | bool }}"
|
|
||||||
_firmware_on: "{{ system_cfg.features.firmware.enabled | bool }}"
|
|
||||||
_microcode_on: "{{ _firmware_on and (system_cfg.features.firmware.microcode | bool) }}"
|
|
||||||
_gpu_on: "{{ system_cfg.features.gpu.enabled | bool }}"
|
|
||||||
_peripherals_on: "{{ system_cfg.features.peripherals.enabled | bool }}"
|
|
||||||
_webcam_pref: "{{ system_cfg.features.peripherals.webcam | default('auto') }}"
|
|
||||||
_fp_pref: "{{ system_cfg.features.peripherals.fingerprint | default('auto') }}"
|
|
||||||
_dl_on: "{{ system_cfg.features.peripherals.displaylink | bool }}"
|
|
||||||
_webcam_on: >-
|
|
||||||
{{
|
|
||||||
_peripherals_on
|
|
||||||
and (_webcam_pref == 'true' or (_webcam_pref == 'auto' and _peripherals_on))
|
|
||||||
}}
|
|
||||||
_fp_on: >-
|
|
||||||
{{
|
|
||||||
_peripherals_on
|
|
||||||
and (_fp_pref == 'true' or (_fp_pref == 'auto' and _fp_detected))
|
|
||||||
}}
|
|
||||||
# Union of GPU/wireless/CPU vendors; CPU vendor is included so Intel-CPU
|
|
||||||
# systems pull i915/iwlwifi firmware via the same vendor split.
|
|
||||||
_cpu_vendor_list: "{{ ([_cpu] if (_cpu | length > 0) else []) | list }}"
|
|
||||||
_firmware_vendors: >-
|
|
||||||
{{
|
|
||||||
(_firmware_on | ternary(
|
|
||||||
(_gpus + _wifi + _cpu_vendor_list)
|
|
||||||
| reject('equalto', '') | unique | list,
|
|
||||||
[]
|
|
||||||
))
|
|
||||||
}}
|
|
||||||
_microcode_pkgs: >-
|
|
||||||
{{
|
|
||||||
((_microcode_on and _cpu | length > 0) | ternary(
|
|
||||||
_family.cpu_microcode[_cpu] | default([]),
|
|
||||||
[]
|
|
||||||
)) | list
|
|
||||||
}}
|
|
||||||
_firmware_pkgs: >-
|
|
||||||
{{
|
|
||||||
(_firmware_on | ternary(
|
|
||||||
(_family.firmware_base | default([]) | list)
|
|
||||||
+ (_firmware_vendors
|
|
||||||
| map('extract', _family.firmware | default({}))
|
|
||||||
| select('truthy')
|
|
||||||
| list
|
|
||||||
| sum(start=[])),
|
|
||||||
[]
|
|
||||||
)) | list
|
|
||||||
}}
|
|
||||||
_gpu_base_pkgs: "{{ (_gpu_on | ternary(_family.gpu_base | default([]), [])) | list }}"
|
|
||||||
_gpu_vendor_pkgs: >-
|
|
||||||
{{
|
|
||||||
(_gpu_on | ternary(
|
|
||||||
(_gpus | reject('equalto', 'nvidia') | list)
|
|
||||||
| map('extract', _family.gpu | default({}))
|
|
||||||
| select('truthy')
|
|
||||||
| list
|
|
||||||
| sum(start=[]),
|
|
||||||
[]
|
|
||||||
)) | list
|
|
||||||
}}
|
|
||||||
_gpu_nvidia_pkgs: >-
|
|
||||||
{{
|
|
||||||
((_gpu_on and ('nvidia' in _gpus)) | ternary(
|
|
||||||
_family.gpu_nvidia[_nvidia_driver_resolved] | default([]),
|
|
||||||
[]
|
|
||||||
)) | list
|
|
||||||
}}
|
|
||||||
_peripherals_base_pkgs: >-
|
|
||||||
{{
|
|
||||||
(_webcam_on | ternary(_family.peripherals_base | default([]), [])) | list
|
|
||||||
}}
|
|
||||||
_peripherals_fingerprint_pkgs: >-
|
|
||||||
{{
|
|
||||||
(_fp_on | ternary(_family.peripherals_fingerprint | default([]), [])) | list
|
|
||||||
}}
|
|
||||||
_peripherals_displaylink_pkgs: >-
|
|
||||||
{{
|
|
||||||
(_dl_on | ternary(_family.peripherals_displaylink | default([]), [])) | list
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_hardware_packages: >-
|
|
||||||
{{
|
|
||||||
(_microcode_pkgs + _firmware_pkgs
|
|
||||||
+ _gpu_base_pkgs + _gpu_vendor_pkgs + _gpu_nvidia_pkgs
|
|
||||||
+ _peripherals_base_pkgs + _peripherals_fingerprint_pkgs
|
|
||||||
+ _peripherals_displaylink_pkgs)
|
|
||||||
| reject('equalto', '')
|
|
||||||
| unique
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Report hardware package selection
|
- name: Report hardware package selection
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
@@ -165,6 +69,15 @@
|
|||||||
fingerprint={{ hardware_profile_active.fingerprint | default(false) }}
|
fingerprint={{ hardware_profile_active.fingerprint | default(false) }}
|
||||||
-> {{ _hardware_packages | length }} package(s)
|
-> {{ _hardware_packages | length }} package(s)
|
||||||
|
|
||||||
|
- name: Note Intel IPU6 camera (out-of-tree stack)
|
||||||
|
when: hardware_profile_active.camera.ipu6 | default(false) | bool
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
Intel IPU6 MIPI camera detected. Its driver stack (intel-ipu6 firmware,
|
||||||
|
DKMS module, v4l2-relayd, libcamera) is out-of-tree/AUR and is NOT auto-
|
||||||
|
installed. Pin the packages in a hardware group via
|
||||||
|
system.features.hardware.packages[{{ os_family }}].
|
||||||
|
|
||||||
- name: Install hardware packages
|
- name: Install hardware packages
|
||||||
when: _hardware_packages | length > 0
|
when: _hardware_packages | length > 0
|
||||||
vars:
|
vars:
|
||||||
|
|||||||
125
roles/bootstrap/tasks/_resolve_hardware_packages.yml
Normal file
125
roles/bootstrap/tasks/_resolve_hardware_packages.yml
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
---
|
||||||
|
# Split out of _hardware.yml so fixtures can seed the inputs and assert the
|
||||||
|
# resolved _hardware_packages list with no chroot/install.
|
||||||
|
- name: Resolve hardware package set
|
||||||
|
vars:
|
||||||
|
_family: "{{ bootstrap_hardware_packages[os_family] }}"
|
||||||
|
_disable: "{{ _hardware_profile_disable | default([]) | list }}"
|
||||||
|
_profile_packages: "{{ (_hardware_profile_packages | default({}))[os_family] | default([]) | list }}"
|
||||||
|
_cpu: "{{ hardware_profile_active.cpu | default('') | string }}"
|
||||||
|
_gpus: "{{ hardware_profile_active.gpus | default([]) | difference(_disable) | list }}"
|
||||||
|
_wifi: "{{ hardware_profile_active.wireless | default([]) | difference(_disable) | list }}"
|
||||||
|
_fp_detected: "{{ hardware_profile_active.fingerprint | default(false) | bool }}"
|
||||||
|
_audio: "{{ hardware_profile_active.audio | default([]) | difference(_disable) | list }}"
|
||||||
|
_bt_detected: "{{ hardware_profile_active.bluetooth | default(false) | bool }}"
|
||||||
|
_firmware_on: "{{ system_cfg.features.firmware.enabled | bool }}"
|
||||||
|
_microcode_on: "{{ _firmware_on and (system_cfg.features.firmware.microcode | bool) }}"
|
||||||
|
_gpu_on: "{{ system_cfg.features.gpu.enabled | bool }}"
|
||||||
|
_peripherals_on: "{{ system_cfg.features.peripherals.enabled | bool }}"
|
||||||
|
_camera_pref: "{{ system_cfg.features.peripherals.camera | default('auto') }}"
|
||||||
|
_camera_uvc: "{{ hardware_profile_active.camera.uvc | default(false) | bool }}"
|
||||||
|
_camera_ipu6: "{{ hardware_profile_active.camera.ipu6 | default(false) | bool }}"
|
||||||
|
_fp_pref: "{{ system_cfg.features.peripherals.fingerprint | default('auto') }}"
|
||||||
|
_audio_pref: "{{ system_cfg.features.peripherals.audio | default('auto') }}"
|
||||||
|
_bt_pref: "{{ system_cfg.features.peripherals.bluetooth | default('auto') }}"
|
||||||
|
_dl_on: "{{ (system_cfg.features.peripherals.displaylink | bool) and ('displaylink' not in _disable) }}"
|
||||||
|
_camera_on: >-
|
||||||
|
{{
|
||||||
|
_peripherals_on
|
||||||
|
and ('camera' not in _disable)
|
||||||
|
and (_camera_pref == 'true' or (_camera_pref == 'auto' and (_camera_uvc or _camera_ipu6)))
|
||||||
|
}}
|
||||||
|
_fp_on: >-
|
||||||
|
{{
|
||||||
|
_peripherals_on
|
||||||
|
and ('fingerprint' not in _disable)
|
||||||
|
and (_fp_pref == 'true' or (_fp_pref == 'auto' and _fp_detected))
|
||||||
|
}}
|
||||||
|
_audio_on: >-
|
||||||
|
{{
|
||||||
|
_peripherals_on
|
||||||
|
and ('audio' not in _disable)
|
||||||
|
and (_audio_pref == 'true' or (_audio_pref == 'auto' and (_audio | length > 0)))
|
||||||
|
}}
|
||||||
|
_bt_on: >-
|
||||||
|
{{
|
||||||
|
_peripherals_on
|
||||||
|
and ('bluetooth' not in _disable)
|
||||||
|
and (_bt_pref == 'true' or (_bt_pref == 'auto' and _bt_detected))
|
||||||
|
}}
|
||||||
|
# Union of GPU/wireless/CPU vendors; CPU vendor is included so Intel-CPU
|
||||||
|
# systems pull i915/iwlwifi firmware via the same vendor split.
|
||||||
|
_cpu_vendor_list: "{{ ([_cpu] if (_cpu | length > 0 and _cpu not in _disable) else []) | list }}"
|
||||||
|
_firmware_vendors: >-
|
||||||
|
{{
|
||||||
|
(_firmware_on | ternary(
|
||||||
|
(_gpus + _wifi + _cpu_vendor_list)
|
||||||
|
| reject('equalto', '') | unique | list,
|
||||||
|
[]
|
||||||
|
))
|
||||||
|
}}
|
||||||
|
_microcode_pkgs: >-
|
||||||
|
{{
|
||||||
|
((_microcode_on and _cpu | length > 0 and _cpu not in _disable) | ternary(
|
||||||
|
_family.cpu_microcode[_cpu] | default([]),
|
||||||
|
[]
|
||||||
|
)) | list
|
||||||
|
}}
|
||||||
|
_firmware_pkgs: >-
|
||||||
|
{{
|
||||||
|
(_firmware_on | ternary(
|
||||||
|
(_family.firmware_base | default([]) | list)
|
||||||
|
+ (_firmware_vendors
|
||||||
|
| map('extract', _family.firmware | default({}))
|
||||||
|
| select('truthy')
|
||||||
|
| list
|
||||||
|
| sum(start=[])),
|
||||||
|
[]
|
||||||
|
)) | list
|
||||||
|
}}
|
||||||
|
_gpu_base_pkgs: "{{ (_gpu_on | ternary(_family.gpu_base | default([]), [])) | list }}"
|
||||||
|
_gpu_vendor_pkgs: >-
|
||||||
|
{{
|
||||||
|
(_gpu_on | ternary(
|
||||||
|
(_gpus | reject('equalto', 'nvidia') | list)
|
||||||
|
| map('extract', _family.gpu | default({}))
|
||||||
|
| select('truthy')
|
||||||
|
| list
|
||||||
|
| sum(start=[]),
|
||||||
|
[]
|
||||||
|
)) | list
|
||||||
|
}}
|
||||||
|
_gpu_nvidia_pkgs: >-
|
||||||
|
{{
|
||||||
|
((_gpu_on and ('nvidia' in _gpus)) | ternary(
|
||||||
|
_family.gpu_nvidia[_nvidia_driver_resolved] | default([]),
|
||||||
|
[]
|
||||||
|
)) | list
|
||||||
|
}}
|
||||||
|
_camera_base_pkgs: >-
|
||||||
|
{{
|
||||||
|
(_camera_on | ternary(_family.camera_base | default([]), [])) | list
|
||||||
|
}}
|
||||||
|
_peripherals_fingerprint_pkgs: >-
|
||||||
|
{{
|
||||||
|
(_fp_on | ternary(_family.peripherals_fingerprint | default([]), [])) | list
|
||||||
|
}}
|
||||||
|
_peripherals_displaylink_pkgs: >-
|
||||||
|
{{
|
||||||
|
(_dl_on | ternary(_family.peripherals_displaylink | default([]), [])) | list
|
||||||
|
}}
|
||||||
|
_audio_base_pkgs: "{{ (_audio_on | ternary(_family.audio_base | default([]), [])) | list }}"
|
||||||
|
_bluetooth_base_pkgs: "{{ (_bt_on | ternary(_family.bluetooth_base | default([]), [])) | list }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_hardware_packages: >-
|
||||||
|
{{
|
||||||
|
(_microcode_pkgs + _firmware_pkgs
|
||||||
|
+ _gpu_base_pkgs + _gpu_vendor_pkgs + _gpu_nvidia_pkgs
|
||||||
|
+ _audio_base_pkgs + _bluetooth_base_pkgs
|
||||||
|
+ _camera_base_pkgs + _peripherals_fingerprint_pkgs
|
||||||
|
+ _peripherals_displaylink_pkgs
|
||||||
|
+ _profile_packages)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| unique
|
||||||
|
| list
|
||||||
|
}}
|
||||||
@@ -1,18 +1,7 @@
|
|||||||
---
|
---
|
||||||
# Hardware-aware package definitions keyed by os_family. Schema:
|
# Hardware-aware package definitions keyed by os_family, consumed by
|
||||||
# cpu_microcode[intel|amd] CPU vendor microcode
|
# _resolve_hardware_packages.yml. Only packages matching detected hardware are
|
||||||
# firmware_base unconditional firmware packages
|
# installed; families without vendor splits collapse to one firmware meta package.
|
||||||
# firmware[<vendor>] vendor-split firmware (intel|amd|nvidia|
|
|
||||||
# atheros|broadcom|mediatek|marvell|realtek|
|
|
||||||
# qcom|cirrus|other)
|
|
||||||
# gpu_base mesa + vulkan loader
|
|
||||||
# gpu[intel|amd] per-GPU userspace
|
|
||||||
# gpu_nvidia[open|proprietary|nouveau] nvidia driver flavor
|
|
||||||
# peripherals_base webcam/scanner stack
|
|
||||||
# peripherals_fingerprint fprintd + libfprint
|
|
||||||
# peripherals_displaylink evdi kernel module + DisplayLink helpers
|
|
||||||
# Only packages matching detected hardware are installed; families without
|
|
||||||
# vendor splits collapse to a single firmware meta package.
|
|
||||||
bootstrap_hardware_packages:
|
bootstrap_hardware_packages:
|
||||||
Archlinux:
|
Archlinux:
|
||||||
cpu_microcode:
|
cpu_microcode:
|
||||||
@@ -40,9 +29,11 @@ bootstrap_hardware_packages:
|
|||||||
proprietary: [nvidia-dkms, nvidia-utils]
|
proprietary: [nvidia-dkms, nvidia-utils]
|
||||||
# Wayland-only: kernel nouveau module + mesa/gbm drive the display; no Xorg DDX.
|
# Wayland-only: kernel nouveau module + mesa/gbm drive the display; no Xorg DDX.
|
||||||
nouveau: [vulkan-nouveau]
|
nouveau: [vulkan-nouveau]
|
||||||
peripherals_base: [v4l-utils]
|
camera_base: [v4l-utils]
|
||||||
peripherals_fingerprint: [fprintd, libfprint]
|
peripherals_fingerprint: [fprintd, libfprint]
|
||||||
peripherals_displaylink: [] # AUR only; user must wire in AUR helper
|
peripherals_displaylink: [] # AUR only; user must wire in AUR helper
|
||||||
|
audio_base: [sof-firmware, alsa-ucm-conf]
|
||||||
|
bluetooth_base: [bluez, bluez-utils]
|
||||||
|
|
||||||
Debian:
|
Debian:
|
||||||
cpu_microcode:
|
cpu_microcode:
|
||||||
@@ -72,9 +63,11 @@ bootstrap_hardware_packages:
|
|||||||
proprietary: [nvidia-driver, nvidia-vulkan-icd]
|
proprietary: [nvidia-driver, nvidia-vulkan-icd]
|
||||||
# Wayland-only: kernel module + mesa (gpu_base) cover it; no Xorg DDX, no extra pkg.
|
# Wayland-only: kernel module + mesa (gpu_base) cover it; no Xorg DDX, no extra pkg.
|
||||||
nouveau: []
|
nouveau: []
|
||||||
peripherals_base: [v4l-utils]
|
camera_base: [v4l-utils]
|
||||||
peripherals_fingerprint: [fprintd, libpam-fprintd]
|
peripherals_fingerprint: [fprintd, libpam-fprintd]
|
||||||
peripherals_displaylink: [evdi-dkms] # userspace driver still needs vendor .run
|
peripherals_displaylink: [evdi-dkms] # userspace driver still needs vendor .run
|
||||||
|
audio_base: [firmware-sof-signed, alsa-ucm-conf]
|
||||||
|
bluetooth_base: [bluez]
|
||||||
|
|
||||||
RedHat:
|
RedHat:
|
||||||
cpu_microcode:
|
cpu_microcode:
|
||||||
@@ -103,6 +96,8 @@ bootstrap_hardware_packages:
|
|||||||
proprietary: [akmod-nvidia, xorg-x11-drv-nvidia, xorg-x11-drv-nvidia-cuda]
|
proprietary: [akmod-nvidia, xorg-x11-drv-nvidia, xorg-x11-drv-nvidia-cuda]
|
||||||
# Wayland-only: kernel module + mesa (gpu_base) cover it; no Xorg DDX, no extra pkg.
|
# Wayland-only: kernel module + mesa (gpu_base) cover it; no Xorg DDX, no extra pkg.
|
||||||
nouveau: []
|
nouveau: []
|
||||||
peripherals_base: [v4l-utils]
|
camera_base: [v4l-utils]
|
||||||
peripherals_fingerprint: [fprintd, fprintd-pam]
|
peripherals_fingerprint: [fprintd, fprintd-pam]
|
||||||
peripherals_displaylink: [evdi] # COPR-supplied; repo enablement deferred
|
peripherals_displaylink: [evdi] # COPR-supplied; repo enablement deferred
|
||||||
|
audio_base: [alsa-sof-firmware, alsa-ucm]
|
||||||
|
bluetooth_base: [bluez]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
line: "{{ item.line }}"
|
line: "{{ item.line }}"
|
||||||
loop:
|
loop:
|
||||||
- regexp: ^GRUB_CMDLINE_LINUX_DEFAULT=
|
- regexp: ^GRUB_CMDLINE_LINUX_DEFAULT=
|
||||||
line: GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3"
|
line: 'GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3{{ (" " ~ (_hardware_profile_kernel_params | join(" "))) if (_hardware_profile_kernel_params | default([]) | length > 0) else "" }}"'
|
||||||
- regexp: ^GRUB_TIMEOUT=
|
- regexp: ^GRUB_TIMEOUT=
|
||||||
line: GRUB_TIMEOUT=1
|
line: GRUB_TIMEOUT=1
|
||||||
loop_control:
|
loop_control:
|
||||||
@@ -43,19 +43,21 @@
|
|||||||
}}
|
}}
|
||||||
grub_root_flags: >-
|
grub_root_flags: >-
|
||||||
{{ ['rootflags=subvol=@'] if system_cfg.filesystem == 'btrfs' else [] }}
|
{{ ['rootflags=subvol=@'] if system_cfg.filesystem == 'btrfs' else [] }}
|
||||||
|
# String-concat (not list-concat like grub_kernel_cmdline_base below): ansible-lint's
|
||||||
|
# jinja render trips on list+list when grub_lvm_args leads the expression here.
|
||||||
grub_cmdline_linux_base: >-
|
grub_cmdline_linux_base: >-
|
||||||
{{
|
{{
|
||||||
(['crashkernel=auto'] + grub_lvm_args)
|
((grub_lvm_args | join(' ')) ~ ' ' ~ (_hardware_profile_kernel_params | default([]) | join(' '))) | trim
|
||||||
| join(' ')
|
|
||||||
}}
|
}}
|
||||||
grub_kernel_cmdline_base: >-
|
grub_kernel_cmdline_base: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
(['root=UUID=' + grub_root_uuid]
|
(['root=UUID=' + grub_root_uuid]
|
||||||
if grub_root_uuid | length > 0 else [])
|
if grub_root_uuid | length > 0 else [])
|
||||||
+ ['ro', 'crashkernel=auto']
|
+ ['ro']
|
||||||
+ grub_lvm_args
|
+ grub_lvm_args
|
||||||
+ grub_root_flags
|
+ grub_root_flags
|
||||||
|
+ (_hardware_profile_kernel_params | default([]))
|
||||||
)
|
)
|
||||||
| join(' ')
|
| join(' ')
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
---
|
---
|
||||||
# Connection and timing
|
|
||||||
environment_wait_timeout: 180
|
environment_wait_timeout: 180
|
||||||
environment_wait_delay: 5
|
environment_wait_delay: 5
|
||||||
|
|
||||||
# Pacman installer settings
|
|
||||||
environment_parallel_downloads: 20
|
environment_parallel_downloads: 20
|
||||||
environment_pacman_lock_timeout: 120
|
environment_pacman_lock_timeout: 120
|
||||||
environment_pacman_retries: 4
|
environment_pacman_retries: 4
|
||||||
environment_pacman_retry_delay: 15
|
environment_pacman_retry_delay: 15
|
||||||
|
|
||||||
# Libraries the installer tools pull whose soname may have bumped past the ISO.
|
# Installer-tool libraries whose soname may have bumped past the ISO. Each one's
|
||||||
# Each library's installed reverse-dependencies are upgraded together with the
|
# installed reverse-deps are co-upgraded so the install stays a consistent
|
||||||
# tools so a current install onto an older ISO stays a consistent transaction
|
# transaction. Extend if a future transition breaks the install.
|
||||||
# instead of a partial upgrade. Extend if a future transition breaks the install.
|
|
||||||
environment_partial_upgrade_libs:
|
environment_partial_upgrade_libs:
|
||||||
- nettle
|
- nettle
|
||||||
- leancrypto
|
- leancrypto
|
||||||
|
|
||||||
# PCI vendor IDs -> vendor codes used by hardware detection.
|
# PCI vendor ID -> vendor code. Only vendors that drive distinct
|
||||||
# Only vendors that drive distinct firmware/driver packages are mapped.
|
# firmware/driver packages are mapped.
|
||||||
environment_pci_vendor_map:
|
environment_pci_vendor_map:
|
||||||
"8086": intel
|
"8086": intel
|
||||||
"1002": amd
|
"1002": amd
|
||||||
@@ -37,8 +34,8 @@ environment_pci_vendor_map:
|
|||||||
"1cf3": cirrus
|
"1cf3": cirrus
|
||||||
"13d7": cirrus
|
"13d7": cirrus
|
||||||
|
|
||||||
# USB vendor IDs of fingerprint readers supported by libfprint / fprintd.
|
# USB vendor IDs of fingerprint readers supported by libfprint / fprintd,
|
||||||
# Lowercase, four-digit hex; matched against `lsusb` output.
|
# matched against `lsusb` output.
|
||||||
environment_fingerprint_vendor_ids:
|
environment_fingerprint_vendor_ids:
|
||||||
- "06cb" # Synaptics (modern ThinkPad/Dell)
|
- "06cb" # Synaptics (modern ThinkPad/Dell)
|
||||||
- "138a" # Validity Sensors (older ThinkPad)
|
- "138a" # Validity Sensors (older ThinkPad)
|
||||||
@@ -49,3 +46,15 @@ environment_fingerprint_vendor_ids:
|
|||||||
- "08ff" # AuthenTec (legacy)
|
- "08ff" # AuthenTec (legacy)
|
||||||
- "147e" # Upek (legacy)
|
- "147e" # Upek (legacy)
|
||||||
- "1491" # Futronic
|
- "1491" # Futronic
|
||||||
|
|
||||||
|
# USB vendor IDs of common Bluetooth controllers. A fallback: detection also
|
||||||
|
# matches the literal "Bluetooth" string in `lsusb` for adapters that omit it.
|
||||||
|
environment_bluetooth_vendor_ids:
|
||||||
|
- "8087" # Intel (AX2xx combo cards)
|
||||||
|
- "0a12" # Cambridge Silicon Radio (CSR)
|
||||||
|
- "0bda" # Realtek
|
||||||
|
- "0cf3" # Qualcomm Atheros
|
||||||
|
- "13d3" # IMC / AzureWave
|
||||||
|
- "0489" # Foxconn / Lite-On
|
||||||
|
- "04ca" # Lite-On
|
||||||
|
- "0b05" # ASUS
|
||||||
|
|||||||
@@ -1,22 +1,6 @@
|
|||||||
---
|
---
|
||||||
# Hardware detection on the live installer host.
|
# A user-supplied override profile skips detection (golden-image flow: bake an
|
||||||
#
|
# image with a fixed profile).
|
||||||
# Resolves system_cfg.features.hardware.profile when not explicitly set, so
|
|
||||||
# downstream bootstrap can install vendor-matched microcode/firmware/GPU/
|
|
||||||
# peripheral packages. When the user supplies an override profile, detection
|
|
||||||
# is skipped (golden-image flow: bake an image with a fixed profile).
|
|
||||||
#
|
|
||||||
# Output fact: hardware_profile_active = {
|
|
||||||
# cpu: 'intel'|'amd'|'',
|
|
||||||
# gpus: list of 'intel'|'amd'|'nvidia',
|
|
||||||
# nvidia_supports_open: bool, # true when all detected Nvidia GPUs are
|
|
||||||
# # Turing or newer (device id >= 0x1e00)
|
|
||||||
# wireless: list of vendor codes ('intel'|'realtek'|'atheros'|...),
|
|
||||||
# fingerprint: bool, # USB fingerprint reader detected
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# Skipped entirely when neither firmware/gpu/peripherals features are enabled.
|
|
||||||
|
|
||||||
- name: Resolve hardware detection requirement
|
- name: Resolve hardware detection requirement
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
_hardware_detection_needed: >-
|
_hardware_detection_needed: >-
|
||||||
@@ -37,7 +21,12 @@
|
|||||||
gpus: "{{ _hardware_profile_override.gpus | default([]) | map('lower') | list }}"
|
gpus: "{{ _hardware_profile_override.gpus | default([]) | map('lower') | list }}"
|
||||||
nvidia_supports_open: "{{ _hardware_profile_override.nvidia_supports_open | default(true) | bool }}"
|
nvidia_supports_open: "{{ _hardware_profile_override.nvidia_supports_open | default(true) | bool }}"
|
||||||
wireless: "{{ _hardware_profile_override.wireless | default([]) | map('lower') | list }}"
|
wireless: "{{ _hardware_profile_override.wireless | default([]) | map('lower') | list }}"
|
||||||
|
audio: "{{ _hardware_profile_override.audio | default([]) | map('lower') | list }}"
|
||||||
fingerprint: "{{ _hardware_profile_override.fingerprint | default(false) | bool }}"
|
fingerprint: "{{ _hardware_profile_override.fingerprint | default(false) | bool }}"
|
||||||
|
bluetooth: "{{ _hardware_profile_override.bluetooth | default(false) | bool }}"
|
||||||
|
camera:
|
||||||
|
uvc: "{{ _hardware_profile_override.camera.uvc | default(false) | bool }}"
|
||||||
|
ipu6: "{{ _hardware_profile_override.camera.ipu6 | default(false) | bool }}"
|
||||||
|
|
||||||
- name: Detect hardware from live host
|
- name: Detect hardware from live host
|
||||||
when:
|
when:
|
||||||
@@ -61,71 +50,7 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Resolve detected hardware profile
|
- name: Resolve detected hardware profile
|
||||||
vars:
|
ansible.builtin.include_tasks: _resolve_hardware_profile.yml
|
||||||
_vendor_keys: "{{ environment_pci_vendor_map.keys() | list }}"
|
|
||||||
_cpu_vendor_raw: >-
|
|
||||||
{{
|
|
||||||
_hardware_lscpu.stdout
|
|
||||||
| regex_search('(?im)^Vendor ID:\\s*(\\S+)', '\\1')
|
|
||||||
| default([''], true)
|
|
||||||
| first
|
|
||||||
}}
|
|
||||||
_cpu_vendor: >-
|
|
||||||
{{
|
|
||||||
'intel' if _cpu_vendor_raw == 'GenuineIntel'
|
|
||||||
else ('amd' if _cpu_vendor_raw == 'AuthenticAMD' else '')
|
|
||||||
}}
|
|
||||||
# PCI classes: 0300 = VGA, 0302 = 3D, 0280 = wireless network controller.
|
|
||||||
_gpu_lines: "{{ _hardware_lspci.stdout_lines | select('search', '\\[(0300|0302)\\]:') | list }}"
|
|
||||||
_gpu_pairs: >-
|
|
||||||
{{
|
|
||||||
_gpu_lines
|
|
||||||
| map('regex_search', '\\[([0-9a-f]{4}):([0-9a-f]{4})\\]', '\\1', '\\2')
|
|
||||||
| select('truthy')
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
_gpu_vendor_ids: "{{ _gpu_pairs | map('first') | select('in', _vendor_keys) | list }}"
|
|
||||||
_gpu_vendors: "{{ _gpu_vendor_ids | map('extract', environment_pci_vendor_map) | unique | list }}"
|
|
||||||
_nvidia_device_ids: >-
|
|
||||||
{{
|
|
||||||
_gpu_pairs
|
|
||||||
| selectattr('0', 'equalto', '10de')
|
|
||||||
| map(attribute=1)
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
_nvidia_min_id: >-
|
|
||||||
{{
|
|
||||||
(_nvidia_device_ids | map('int', base=16) | list | min)
|
|
||||||
if _nvidia_device_ids | length > 0 else 0
|
|
||||||
}}
|
|
||||||
# 0x1e00 = 7680 = first Turing device id; Turing+ supports nvidia-open.
|
|
||||||
_nvidia_supports_open: "{{ _nvidia_device_ids | length > 0 and (_nvidia_min_id | int) >= 7680 }}"
|
|
||||||
_wifi_lines: "{{ _hardware_lspci.stdout_lines | select('search', '\\[0280\\]:') | list }}"
|
|
||||||
_wifi_vendor_ids: >-
|
|
||||||
{{
|
|
||||||
_wifi_lines
|
|
||||||
| map('regex_search', '\\[([0-9a-f]{4}):[0-9a-f]{4}\\]', '\\1')
|
|
||||||
| select('truthy')
|
|
||||||
| map('first')
|
|
||||||
| select('in', _vendor_keys)
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
_wifi_vendors: "{{ _wifi_vendor_ids | map('extract', environment_pci_vendor_map) | unique | list }}"
|
|
||||||
_fingerprint_present: >-
|
|
||||||
{{
|
|
||||||
(_hardware_lsusb.stdout | default(''))
|
|
||||||
| regex_search(
|
|
||||||
'(?i)ID (' ~ (environment_fingerprint_vendor_ids | join('|')) ~ '):'
|
|
||||||
)
|
|
||||||
is not none
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
hardware_profile_active:
|
|
||||||
cpu: "{{ _cpu_vendor }}"
|
|
||||||
gpus: "{{ _gpu_vendors }}"
|
|
||||||
nvidia_supports_open: "{{ _nvidia_supports_open | bool }}"
|
|
||||||
wireless: "{{ _wifi_vendors }}"
|
|
||||||
fingerprint: "{{ _fingerprint_present | bool }}"
|
|
||||||
|
|
||||||
- name: Initialize empty hardware profile when detection skipped
|
- name: Initialize empty hardware profile when detection skipped
|
||||||
when: not (_hardware_detection_needed | bool)
|
when: not (_hardware_detection_needed | bool)
|
||||||
@@ -135,7 +60,14 @@
|
|||||||
gpus: []
|
gpus: []
|
||||||
nvidia_supports_open: true
|
nvidia_supports_open: true
|
||||||
wireless: []
|
wireless: []
|
||||||
|
audio: []
|
||||||
fingerprint: false
|
fingerprint: false
|
||||||
|
bluetooth: false
|
||||||
|
camera: { uvc: false, ipu6: false }
|
||||||
|
|
||||||
|
- name: Merge declarative hardware group over detection
|
||||||
|
when: _hardware_detection_needed | bool
|
||||||
|
ansible.builtin.include_tasks: _merge_hardware_profile.yml
|
||||||
|
|
||||||
- name: Report active hardware profile
|
- name: Report active hardware profile
|
||||||
when: _hardware_detection_needed | bool
|
when: _hardware_detection_needed | bool
|
||||||
@@ -146,4 +78,7 @@
|
|||||||
gpus={{ hardware_profile_active.gpus | default([]) | join(',') | default('-', true) }}
|
gpus={{ hardware_profile_active.gpus | default([]) | join(',') | default('-', true) }}
|
||||||
{{ '(open-supported)' if hardware_profile_active.nvidia_supports_open | bool else '(legacy)' }},
|
{{ '(open-supported)' if hardware_profile_active.nvidia_supports_open | bool else '(legacy)' }},
|
||||||
wireless={{ hardware_profile_active.wireless | default([]) | join(',') | default('-', true) }},
|
wireless={{ hardware_profile_active.wireless | default([]) | join(',') | default('-', true) }},
|
||||||
fingerprint={{ hardware_profile_active.fingerprint | default(false) }}
|
audio={{ hardware_profile_active.audio | default([]) | join(',') | default('-', true) }},
|
||||||
|
fingerprint={{ hardware_profile_active.fingerprint | default(false) }},
|
||||||
|
bluetooth={{ hardware_profile_active.bluetooth | default(false) }},
|
||||||
|
camera={{ 'uvc' if hardware_profile_active.camera.uvc | default(false) else '' }}{{ '+ipu6' if hardware_profile_active.camera.ipu6 | default(false) else '' }}
|
||||||
|
|||||||
22
roles/environment/tasks/_merge_hardware_profile.yml
Normal file
22
roles/environment/tasks/_merge_hardware_profile.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
# Supplements whatever profile is active (detected or full-override) rather than
|
||||||
|
# replacing it: vendor lists union, booleans OR, cpu overrides when set.
|
||||||
|
- name: Merge declarative hardware group over detection
|
||||||
|
vars:
|
||||||
|
_hw: "{{ system_cfg.features.hardware }}"
|
||||||
|
_det: "{{ hardware_profile_active }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
hardware_profile_active:
|
||||||
|
cpu: "{{ (_hw.cpu | default('') | string | lower) if (_hw.cpu | default('') | length > 0) else _det.cpu }}"
|
||||||
|
gpus: "{{ ((_det.gpus | default([])) + (_hw.gpus | default([]) | map('lower') | list)) | unique | list }}"
|
||||||
|
nvidia_supports_open: "{{ _det.nvidia_supports_open | default(true) | bool }}"
|
||||||
|
wireless: "{{ ((_det.wireless | default([])) + (_hw.wireless | default([]) | map('lower') | list)) | unique | list }}"
|
||||||
|
audio: "{{ ((_det.audio | default([])) + (_hw.audio | default([]) | map('lower') | list)) | unique | list }}"
|
||||||
|
fingerprint: "{{ (_det.fingerprint | default(false) | bool) or (_hw.fingerprint | default(false) | bool) }}"
|
||||||
|
bluetooth: "{{ (_det.bluetooth | default(false) | bool) or (_hw.bluetooth | default(false) | bool) }}"
|
||||||
|
camera:
|
||||||
|
uvc: "{{ (_det.camera.uvc | default(false) | bool) or (_hw.camera.uvc | default(false) | bool) }}"
|
||||||
|
ipu6: "{{ (_det.camera.ipu6 | default(false) | bool) or (_hw.camera.ipu6 | default(false) | bool) }}"
|
||||||
|
_hardware_profile_packages: "{{ _hw.packages | default({}) }}"
|
||||||
|
_hardware_profile_disable: "{{ _hw.disable | default([]) | list }}"
|
||||||
|
_hardware_profile_kernel_params: "{{ _hw.kernel_params | default([]) | list }}"
|
||||||
57
roles/environment/tasks/_resolve_hardware_profile.yml
Normal file
57
roles/environment/tasks/_resolve_hardware_profile.yml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
# Split out of _detect_hardware.yml so fixtures can seed the lscpu/lspci/lsusb
|
||||||
|
# registers and assert the result with no real hardware. Keep regex exprs
|
||||||
|
# double-quoted single-line: ansible-core 2.21 set_fact mangles backslash escapes
|
||||||
|
# inside folded (>-) scalars.
|
||||||
|
- name: Resolve detected hardware profile
|
||||||
|
vars:
|
||||||
|
_vendor_keys: "{{ environment_pci_vendor_map.keys() | list }}"
|
||||||
|
_cpu_vendor_raw: "{{ _hardware_lscpu.stdout | regex_findall('(?im)^Vendor ID:\\s*(\\S+)') | first | default('') }}"
|
||||||
|
_cpu_vendor: >-
|
||||||
|
{{
|
||||||
|
'intel' if _cpu_vendor_raw == 'GenuineIntel'
|
||||||
|
else ('amd' if _cpu_vendor_raw == 'AuthenticAMD' else '')
|
||||||
|
}}
|
||||||
|
# PCI classes: 0300 = VGA, 0302 = 3D, 0280 = wireless network controller.
|
||||||
|
_gpu_lines: "{{ _hardware_lspci.stdout_lines | select('search', '\\[(0300|0302)\\]:') | list }}"
|
||||||
|
_gpu_pairs: "{{ (_gpu_lines | join('\n')) | regex_findall('\\[([0-9a-f]{4}):([0-9a-f]{4})\\]') | list }}"
|
||||||
|
_gpu_vendor_ids: "{{ _gpu_pairs | map('first') | select('in', _vendor_keys) | list }}"
|
||||||
|
_gpu_vendors: "{{ _gpu_vendor_ids | map('extract', environment_pci_vendor_map) | unique | list }}"
|
||||||
|
_nvidia_device_ids: "{{ _gpu_pairs | selectattr('0', 'equalto', '10de') | map(attribute=1) | list }}"
|
||||||
|
_nvidia_min_id: >-
|
||||||
|
{{
|
||||||
|
(_nvidia_device_ids | map('int', base=16) | list | min)
|
||||||
|
if _nvidia_device_ids | length > 0 else 0
|
||||||
|
}}
|
||||||
|
# 0x1e00 = 7680 = first Turing device id; Turing+ supports nvidia-open.
|
||||||
|
_nvidia_supports_open: "{{ _nvidia_device_ids | length > 0 and (_nvidia_min_id | int) >= 7680 }}"
|
||||||
|
_wifi_lines: "{{ _hardware_lspci.stdout_lines | select('search', '\\[0280\\]:') | list }}"
|
||||||
|
_wifi_vendor_ids: "{{ (_wifi_lines | join('\n')) | regex_findall('\\[([0-9a-f]{4}):[0-9a-f]{4}\\]') | select('in', _vendor_keys) | list }}"
|
||||||
|
_wifi_vendors: "{{ _wifi_vendor_ids | map('extract', environment_pci_vendor_map) | unique | list }}"
|
||||||
|
# PCI class 0403 = audio device (HD-audio controller). Vendor drives SOF/firmware.
|
||||||
|
_audio_lines: "{{ _hardware_lspci.stdout_lines | select('search', '\\[0403\\]:') | list }}"
|
||||||
|
_audio_vendor_ids: "{{ (_audio_lines | join('\n')) | regex_findall('\\[([0-9a-f]{4}):[0-9a-f]{4}\\]') | select('in', _vendor_keys) | list }}"
|
||||||
|
_audio_vendors: "{{ _audio_vendor_ids | map('extract', environment_pci_vendor_map) | unique | list }}"
|
||||||
|
_fingerprint_present: "{{ (_hardware_lsusb.stdout | default('')) | regex_search('(?i)ID (' ~ (environment_fingerprint_vendor_ids | join('|')) ~ '):') is not none }}"
|
||||||
|
_camera_uvc_present: "{{ (_hardware_lsusb.stdout | default('')) is search('(?i)camera|webcam') }}"
|
||||||
|
# Intel IPU6 MIPI camera: PCI class 0480 (multimedia) under Intel 8086, or an ISP description. Out-of-tree userspace.
|
||||||
|
_camera_ipu6_desc: "{{ (_hardware_lspci.stdout | default('')) is search('(?i)image signal processor|IPU6') }}"
|
||||||
|
_camera_ipu6_pci: "{{ (_hardware_lspci.stdout_lines | select('search', '\\[0480\\]:') | select('search', '\\[8086:') | list) | length > 0 }}"
|
||||||
|
# No backslash escapes here, so a folded scalar is safe (unlike the \[..\] regexes above).
|
||||||
|
_bluetooth_present: >-
|
||||||
|
{{
|
||||||
|
((_hardware_lsusb.stdout | default('')) | regex_search('(?i)ID (' ~ (environment_bluetooth_vendor_ids | join('|')) ~ '):') is not none)
|
||||||
|
or ((_hardware_lsusb.stdout | default('')) is search('(?i)bluetooth'))
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
hardware_profile_active:
|
||||||
|
cpu: "{{ _cpu_vendor }}"
|
||||||
|
gpus: "{{ _gpu_vendors }}"
|
||||||
|
nvidia_supports_open: "{{ _nvidia_supports_open | bool }}"
|
||||||
|
wireless: "{{ _wifi_vendors }}"
|
||||||
|
audio: "{{ _audio_vendors }}"
|
||||||
|
fingerprint: "{{ _fingerprint_present | bool }}"
|
||||||
|
bluetooth: "{{ _bluetooth_present | bool }}"
|
||||||
|
camera:
|
||||||
|
uvc: "{{ _camera_uvc_present | bool }}"
|
||||||
|
ipu6: "{{ (_camera_ipu6_desc | bool) or (_camera_ipu6_pci | bool) }}"
|
||||||
@@ -143,11 +143,27 @@ system_defaults:
|
|||||||
nvidia_driver: "auto" # auto | open | proprietary | nouveau
|
nvidia_driver: "auto" # auto | open | proprietary | nouveau
|
||||||
peripherals:
|
peripherals:
|
||||||
enabled: "auto" # auto = follows desktop.enabled
|
enabled: "auto" # auto = follows desktop.enabled
|
||||||
fingerprint: "auto"
|
fingerprint: "auto" # auto|true|false (auto = install when detected)
|
||||||
webcam: "auto"
|
camera: "auto" # v4l-utils when a UVC/IPU6 camera is detected
|
||||||
|
audio: "auto" # SOF firmware + ALSA UCM when an audio device is present
|
||||||
|
bluetooth: "auto" # bluez when a Bluetooth controller is present
|
||||||
displaylink: false
|
displaylink: false
|
||||||
hardware:
|
hardware:
|
||||||
profile: {} # empty = autodetect; set to override (golden image)
|
profile: {} # full override: non-empty SKIPS detection (golden image)
|
||||||
|
# Declarative hardware group: a per-device profile that MERGES over
|
||||||
|
# auto-detect (auto-detect = base; these supplement/override it). Vendor
|
||||||
|
# lists union with detection, booleans OR with detection, packages append,
|
||||||
|
# disable[] force-off (applied last), kernel_params append to the cmdline.
|
||||||
|
cpu: "" # pin a CPU vendor (intel|amd); empty = use detection
|
||||||
|
gpus: [] # extra GPU vendor codes to force
|
||||||
|
wireless: [] # extra wireless vendor codes to force
|
||||||
|
audio: [] # extra audio vendor codes to force
|
||||||
|
camera: {} # {uvc: true, ipu6: true} to force a camera kind
|
||||||
|
fingerprint: false # force-on a fingerprint reader detection missed
|
||||||
|
bluetooth: false # force-on a Bluetooth controller detection missed
|
||||||
|
packages: {} # per-os_family extra packages, e.g. {Archlinux: [intel-ipu6-dkms]}
|
||||||
|
disable: [] # feature/vendor names to force-off (audio|bluetooth|camera|fingerprint|displaylink|<vendor>)
|
||||||
|
kernel_params: [] # extra kernel cmdline params (quirks), e.g. ["i915.enable_psr=0"]
|
||||||
|
|
||||||
# Per-hypervisor required fields - drives data-driven validation.
|
# Per-hypervisor required fields - drives data-driven validation.
|
||||||
# All virtual types additionally require network bridge or interfaces.
|
# All virtual types additionally require network bridge or interfaces.
|
||||||
|
|||||||
@@ -206,23 +206,45 @@
|
|||||||
if (system_raw.features.peripherals.enabled | string | lower) == 'auto'
|
if (system_raw.features.peripherals.enabled | string | lower) == 'auto'
|
||||||
else (system_raw.features.peripherals.enabled | bool)
|
else (system_raw.features.peripherals.enabled | bool)
|
||||||
}}
|
}}
|
||||||
# fingerprint/webcam stay tri-state ('auto'|'true'|'false') because the
|
# fingerprint/camera/audio/bluetooth stay tri-state ('auto'|'true'|'false')
|
||||||
# 'auto' branch is resolved at install time using detection results.
|
# because the 'auto' branch is resolved at install time using detection results.
|
||||||
fingerprint: >-
|
fingerprint: >-
|
||||||
{{
|
{{
|
||||||
'auto'
|
'auto'
|
||||||
if (system_raw.features.peripherals.fingerprint | string | lower) == 'auto'
|
if (system_raw.features.peripherals.fingerprint | string | lower) == 'auto'
|
||||||
else (system_raw.features.peripherals.fingerprint | bool | string | lower)
|
else (system_raw.features.peripherals.fingerprint | bool | string | lower)
|
||||||
}}
|
}}
|
||||||
webcam: >-
|
camera: >-
|
||||||
{{
|
{{
|
||||||
'auto'
|
'auto'
|
||||||
if (system_raw.features.peripherals.webcam | string | lower) == 'auto'
|
if (system_raw.features.peripherals.camera | string | lower) == 'auto'
|
||||||
else (system_raw.features.peripherals.webcam | bool | string | lower)
|
else (system_raw.features.peripherals.camera | bool | string | lower)
|
||||||
|
}}
|
||||||
|
audio: >-
|
||||||
|
{{
|
||||||
|
'auto'
|
||||||
|
if (system_raw.features.peripherals.audio | string | lower) == 'auto'
|
||||||
|
else (system_raw.features.peripherals.audio | bool | string | lower)
|
||||||
|
}}
|
||||||
|
bluetooth: >-
|
||||||
|
{{
|
||||||
|
'auto'
|
||||||
|
if (system_raw.features.peripherals.bluetooth | string | lower) == 'auto'
|
||||||
|
else (system_raw.features.peripherals.bluetooth | bool | string | lower)
|
||||||
}}
|
}}
|
||||||
displaylink: "{{ system_raw.features.peripherals.displaylink | bool }}"
|
displaylink: "{{ system_raw.features.peripherals.displaylink | bool }}"
|
||||||
hardware:
|
hardware:
|
||||||
profile: "{{ system_raw.features.hardware.profile | default({}) }}"
|
profile: "{{ system_raw.features.hardware.profile | default({}) }}"
|
||||||
|
cpu: "{{ system_raw.features.hardware.cpu | default('') | string }}"
|
||||||
|
gpus: "{{ system_raw.features.hardware.gpus | default([]) | list }}"
|
||||||
|
wireless: "{{ system_raw.features.hardware.wireless | default([]) | list }}"
|
||||||
|
audio: "{{ system_raw.features.hardware.audio | default([]) | list }}"
|
||||||
|
camera: "{{ system_raw.features.hardware.camera | default({}) }}"
|
||||||
|
fingerprint: "{{ system_raw.features.hardware.fingerprint | default(false) | bool }}"
|
||||||
|
bluetooth: "{{ system_raw.features.hardware.bluetooth | default(false) | bool }}"
|
||||||
|
packages: "{{ system_raw.features.hardware.packages | default({}) }}"
|
||||||
|
disable: "{{ system_raw.features.hardware.disable | default([]) | list }}"
|
||||||
|
kernel_params: "{{ system_raw.features.hardware.kernel_params | default([]) | list }}"
|
||||||
hostname: "{{ system_name }}"
|
hostname: "{{ system_name }}"
|
||||||
os: "{{ system_os_input if system_os_input | length > 0 else (physical_default_os if system_type == 'physical' else '') }}"
|
os: "{{ system_os_input if system_os_input | length > 0 else (physical_default_os if system_type == 'physical' else '') }}"
|
||||||
os_version: "{{ system_raw.version | default('') | string }}"
|
os_version: "{{ system_raw.version | default('') | string }}"
|
||||||
|
|||||||
@@ -241,15 +241,21 @@
|
|||||||
- system_cfg.features.gpu.nvidia_driver in ["auto", "open", "proprietary", "nouveau"]
|
- system_cfg.features.gpu.nvidia_driver in ["auto", "open", "proprietary", "nouveau"]
|
||||||
- system_cfg.features.peripherals.enabled is defined
|
- system_cfg.features.peripherals.enabled is defined
|
||||||
- system_cfg.features.peripherals.fingerprint in ["auto", "true", "false"]
|
- system_cfg.features.peripherals.fingerprint in ["auto", "true", "false"]
|
||||||
- system_cfg.features.peripherals.webcam in ["auto", "true", "false"]
|
- system_cfg.features.peripherals.camera in ["auto", "true", "false"]
|
||||||
|
- system_cfg.features.peripherals.audio in ["auto", "true", "false"]
|
||||||
|
- system_cfg.features.peripherals.bluetooth in ["auto", "true", "false"]
|
||||||
- system_cfg.features.peripherals.displaylink is defined
|
- system_cfg.features.peripherals.displaylink is defined
|
||||||
- system_cfg.features.hardware.profile is mapping
|
- system_cfg.features.hardware.profile is mapping
|
||||||
|
- system_cfg.features.hardware.packages is mapping
|
||||||
|
- system_cfg.features.hardware.camera is mapping
|
||||||
|
- system_cfg.features.hardware.disable is sequence
|
||||||
|
- system_cfg.features.hardware.kernel_params is sequence
|
||||||
fail_msg: >-
|
fail_msg: >-
|
||||||
Invalid hardware feature flags. firmware.enabled/microcode,
|
Invalid hardware feature flags. firmware.enabled/microcode,
|
||||||
peripherals.enabled and peripherals.displaylink must be bool (or 'auto'
|
peripherals.enabled and peripherals.displaylink must be bool (or 'auto'
|
||||||
sentinel for firmware); gpu.nvidia_driver in
|
sentinel for firmware); gpu.nvidia_driver in
|
||||||
[auto|open|proprietary|nouveau]; peripherals.fingerprint and
|
[auto|open|proprietary|nouveau]; peripherals.fingerprint/camera/audio/
|
||||||
peripherals.webcam in [auto|true|false]; hardware.profile must be a dict.
|
bluetooth in [auto|true|false]; hardware.profile must be a dict.
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Validate desktop environment
|
- name: Validate desktop environment
|
||||||
|
|||||||
24
tests/hardware/_assert_fixture.yml
Normal file
24
tests/hardware/_assert_fixture.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
- name: "Seed lscpu/lspci/lsusb registers ({{ fixture.name }})"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_hardware_lscpu: { stdout: "{{ fixture.lscpu }}" }
|
||||||
|
_hardware_lspci: { stdout: "{{ fixture.lspci | join('\n') }}", stdout_lines: "{{ fixture.lspci }}" }
|
||||||
|
_hardware_lsusb: { stdout: "{{ fixture.lsusb }}" }
|
||||||
|
|
||||||
|
- name: "Resolve hardware profile ({{ fixture.name }})"
|
||||||
|
ansible.builtin.include_tasks: ../../roles/environment/tasks/_resolve_hardware_profile.yml
|
||||||
|
|
||||||
|
- name: "Assert resolved profile ({{ fixture.name }})"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- hardware_profile_active.cpu == fixture.expect.cpu
|
||||||
|
- hardware_profile_active.gpus == fixture.expect.gpus
|
||||||
|
- hardware_profile_active.wireless == fixture.expect.wireless
|
||||||
|
- hardware_profile_active.audio == fixture.expect.audio
|
||||||
|
- (hardware_profile_active.fingerprint | bool) == (fixture.expect.fingerprint | bool)
|
||||||
|
- (hardware_profile_active.bluetooth | bool) == (fixture.expect.bluetooth | bool)
|
||||||
|
- (hardware_profile_active.camera.uvc | bool) == (fixture.expect.camera.uvc | bool)
|
||||||
|
- (hardware_profile_active.camera.ipu6 | bool) == (fixture.expect.camera.ipu6 | bool)
|
||||||
|
- (hardware_profile_active.nvidia_supports_open | bool) == (fixture.expect.nvidia_supports_open | bool)
|
||||||
|
fail_msg: "[{{ fixture.name }}] FAIL got {{ hardware_profile_active }}"
|
||||||
|
success_msg: "[{{ fixture.name }}] OK {{ hardware_profile_active }}"
|
||||||
44
tests/hardware/_assert_merge.yml
Normal file
44
tests/hardware/_assert_merge.yml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
- name: "Seed detection + declarative group (merge {{ mf.name }})"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
hardware_profile_active: "{{ mf.detected }}"
|
||||||
|
os_family: "{{ mf.os_family }}"
|
||||||
|
_nvidia_driver_resolved: "{{ mf.nvidia_driver_resolved | default('nouveau') }}"
|
||||||
|
system_cfg:
|
||||||
|
features:
|
||||||
|
firmware: { enabled: true, microcode: true }
|
||||||
|
gpu: { enabled: true }
|
||||||
|
peripherals:
|
||||||
|
enabled: true
|
||||||
|
camera: "auto"
|
||||||
|
fingerprint: "auto"
|
||||||
|
audio: "auto"
|
||||||
|
bluetooth: "auto"
|
||||||
|
displaylink: false
|
||||||
|
hardware: "{{ mf.hardware }}"
|
||||||
|
|
||||||
|
- name: "Merge group over detection (merge {{ mf.name }})"
|
||||||
|
ansible.builtin.include_tasks: ../../roles/environment/tasks/_merge_hardware_profile.yml
|
||||||
|
|
||||||
|
- name: "Assert merged profile (merge {{ mf.name }})"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (hardware_profile_active.fingerprint | bool) == (mf.expect_profile.fingerprint | bool)
|
||||||
|
- (hardware_profile_active.bluetooth | bool) == (mf.expect_profile.bluetooth | bool)
|
||||||
|
- _hardware_profile_kernel_params == (mf.hardware.kernel_params | default([]))
|
||||||
|
fail_msg: "[merge {{ mf.name }}] profile FAIL {{ hardware_profile_active }}"
|
||||||
|
|
||||||
|
- name: "Load package map (merge {{ mf.name }})"
|
||||||
|
ansible.builtin.include_vars:
|
||||||
|
file: ../../roles/bootstrap/vars/hardware.yml
|
||||||
|
|
||||||
|
- name: "Resolve packages (merge {{ mf.name }})"
|
||||||
|
ansible.builtin.include_tasks: ../../roles/bootstrap/tasks/_resolve_hardware_packages.yml
|
||||||
|
|
||||||
|
- name: "Assert resolved package list (merge {{ mf.name }})"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (mf.expect_contains | default([])) | difference(_hardware_packages) | length == 0
|
||||||
|
- (mf.expect_excludes | default([])) | intersect(_hardware_packages) | length == 0
|
||||||
|
fail_msg: "[merge {{ mf.name }}] FAIL got {{ _hardware_packages }}"
|
||||||
|
success_msg: "[merge {{ mf.name }}] OK {{ _hardware_packages }}"
|
||||||
32
tests/hardware/_assert_packages.yml
Normal file
32
tests/hardware/_assert_packages.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
- name: "Seed resolve inputs (pkg {{ pf.name }})"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
os_family: "{{ pf.os_family }}"
|
||||||
|
hardware_profile_active: "{{ pf.profile }}"
|
||||||
|
_nvidia_driver_resolved: "{{ pf.nvidia_driver_resolved | default('nouveau') }}"
|
||||||
|
system_cfg:
|
||||||
|
features:
|
||||||
|
firmware: { enabled: true, microcode: true }
|
||||||
|
gpu: { enabled: true }
|
||||||
|
peripherals:
|
||||||
|
enabled: true
|
||||||
|
camera: "auto"
|
||||||
|
fingerprint: "auto"
|
||||||
|
audio: "auto"
|
||||||
|
bluetooth: "auto"
|
||||||
|
displaylink: false
|
||||||
|
|
||||||
|
- name: "Load package map (pkg {{ pf.name }})"
|
||||||
|
ansible.builtin.include_vars:
|
||||||
|
file: ../../roles/bootstrap/vars/hardware.yml
|
||||||
|
|
||||||
|
- name: "Resolve packages (pkg {{ pf.name }})"
|
||||||
|
ansible.builtin.include_tasks: ../../roles/bootstrap/tasks/_resolve_hardware_packages.yml
|
||||||
|
|
||||||
|
- name: "Assert resolved package list (pkg {{ pf.name }})"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (pf.expect_contains | default([])) | difference(_hardware_packages) | length == 0
|
||||||
|
- (pf.expect_excludes | default([])) | intersect(_hardware_packages) | length == 0
|
||||||
|
fail_msg: "[pkg {{ pf.name }}] FAIL got {{ _hardware_packages }}"
|
||||||
|
success_msg: "[pkg {{ pf.name }}] OK {{ _hardware_packages }}"
|
||||||
41
tests/hardware/fixtures.yml
Normal file
41
tests/hardware/fixtures.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
# Canned lscpu/lspci/lsusb -> expected profile. lspci lines are `lspci -nn` shaped,
|
||||||
|
# lsusb is plain `lsusb`.
|
||||||
|
hardware_fixtures:
|
||||||
|
- name: intel-laptop-nvidia-turing
|
||||||
|
lscpu: "Architecture: x86_64\nVendor ID: GenuineIntel\nModel name: 12th Gen Core i7"
|
||||||
|
lspci:
|
||||||
|
- "00:02.0 VGA compatible controller [0300]: Intel Corporation Alder Lake-P GT2 [8086:46a6] (rev 0c)"
|
||||||
|
- "01:00.0 3D controller [0302]: NVIDIA Corporation TU117M [GeForce GTX 1650 Mobile] [10de:1f99] (rev a1)"
|
||||||
|
- "00:14.3 Network controller [0280]: Intel Corporation Wi-Fi 6 AX201 [8086:a0f0] (rev 11)"
|
||||||
|
- "00:1f.3 Audio device [0403]: Intel Corporation Alder Lake PCH-P High Definition Audio [8086:51c8] (rev 01)"
|
||||||
|
lsusb: |-
|
||||||
|
Bus 001 Device 003: ID 06cb:00bd Synaptics, Inc. Prometheus MIS Touch Fingerprint Reader
|
||||||
|
Bus 001 Device 004: ID 8087:0026 Intel Corp. AX201 Bluetooth
|
||||||
|
Bus 001 Device 005: ID 5986:118d Acer, Inc. Integrated Camera
|
||||||
|
expect: { cpu: intel, gpus: [intel, nvidia], nvidia_supports_open: true, wireless: [intel], audio: [intel], fingerprint: true, bluetooth: true, camera: { uvc: true, ipu6: false } }
|
||||||
|
|
||||||
|
- name: amd-desktop-realtek
|
||||||
|
lscpu: "Architecture: x86_64\nVendor ID: AuthenticAMD\nModel name: AMD Ryzen 7 5800X"
|
||||||
|
lspci:
|
||||||
|
- "0a:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Navi 22 [1002:73df] (rev c7)"
|
||||||
|
- "05:00.0 Network controller [0280]: Realtek Semiconductor Co., Ltd. RTL8822CE 802.11ac [10ec:c822]"
|
||||||
|
- "0b:00.4 Audio device [0403]: Advanced Micro Devices, Inc. [AMD] Starship/Matisse HD Audio [1022:1487]"
|
||||||
|
lsusb: "Bus 002 Device 002: ID 046d:c52b Logitech, Inc. Unifying Receiver"
|
||||||
|
expect: { cpu: amd, gpus: [amd], nvidia_supports_open: false, wireless: [realtek], audio: [amd], fingerprint: false, bluetooth: false, camera: { uvc: false, ipu6: false } }
|
||||||
|
|
||||||
|
- name: nvidia-legacy-maxwell
|
||||||
|
lscpu: "Vendor ID: GenuineIntel"
|
||||||
|
lspci:
|
||||||
|
- "01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GM107 [GeForce GTX 750 Ti] [10de:1380] (rev a2)"
|
||||||
|
lsusb: ""
|
||||||
|
expect: { cpu: intel, gpus: [nvidia], nvidia_supports_open: false, wireless: [], audio: [], fingerprint: false, bluetooth: false, camera: { uvc: false, ipu6: false } }
|
||||||
|
|
||||||
|
- name: intel-laptop-ipu6-camera
|
||||||
|
lscpu: "Vendor ID: GenuineIntel"
|
||||||
|
lspci:
|
||||||
|
- "00:02.0 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [8086:a7a0] (rev 04)"
|
||||||
|
- "00:05.0 Multimedia controller [0480]: Intel Corporation Raptor Lake IPU6 [8086:a75d] (rev 04)"
|
||||||
|
- "00:14.3 Network controller [0280]: Intel Corporation Wi-Fi 6E AX211 [8086:51f0]"
|
||||||
|
lsusb: "Bus 003 Device 002: ID 8087:0033 Intel Corp. AX211 Bluetooth"
|
||||||
|
expect: { cpu: intel, gpus: [intel], nvidia_supports_open: false, wireless: [intel], audio: [], fingerprint: false, bluetooth: true, camera: { uvc: false, ipu6: true } }
|
||||||
37
tests/hardware/merge_fixtures.yml
Normal file
37
tests/hardware/merge_fixtures.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
# Detected profile + declarative hardware.* -> merged profile + resolved packages.
|
||||||
|
# Exercises union/OR/force-on, disable[] force-off, and packages[os_family] append.
|
||||||
|
merge_fixtures:
|
||||||
|
- name: force-fingerprint-disable-audio-pin-ipu6
|
||||||
|
os_family: Archlinux
|
||||||
|
nvidia_driver_resolved: nouveau
|
||||||
|
detected: { cpu: intel, gpus: [intel], nvidia_supports_open: true, wireless: [intel], audio: [intel], fingerprint: false, bluetooth: false, camera: { uvc: false, ipu6: true } }
|
||||||
|
hardware:
|
||||||
|
fingerprint: true # force-on (detection missed it)
|
||||||
|
packages: { Archlinux: [intel-ipu6-dkms, v4l2-relayd] }
|
||||||
|
disable: [audio] # force-off audio even though detected
|
||||||
|
kernel_params: ["i915.enable_psr=0"]
|
||||||
|
expect_profile: { fingerprint: true, bluetooth: false }
|
||||||
|
expect_contains: [intel-ipu6-dkms, v4l2-relayd, fprintd, libfprint, v4l-utils, intel-ucode]
|
||||||
|
expect_excludes: [sof-firmware, alsa-ucm-conf, bluez]
|
||||||
|
|
||||||
|
- name: union-vendors-force-bluetooth
|
||||||
|
os_family: Debian
|
||||||
|
nvidia_driver_resolved: nouveau
|
||||||
|
detected: { cpu: amd, gpus: [amd], nvidia_supports_open: false, wireless: [], audio: [amd], fingerprint: false, bluetooth: false, camera: { uvc: false, ipu6: false } }
|
||||||
|
hardware:
|
||||||
|
wireless: [realtek] # union: add a wireless vendor detection missed
|
||||||
|
bluetooth: true # force-on bluetooth
|
||||||
|
expect_profile: { fingerprint: false, bluetooth: true }
|
||||||
|
expect_contains: [amd64-microcode, firmware-realtek, firmware-sof-signed, bluez]
|
||||||
|
expect_excludes: [fprintd, v4l-utils]
|
||||||
|
|
||||||
|
- name: disable-vendor-drops-microcode-firmware-gpu
|
||||||
|
os_family: Archlinux
|
||||||
|
nvidia_driver_resolved: open
|
||||||
|
detected: { cpu: intel, gpus: [intel, nvidia], nvidia_supports_open: true, wireless: [intel], audio: [intel], fingerprint: false, bluetooth: false, camera: { uvc: false, ipu6: false } }
|
||||||
|
hardware:
|
||||||
|
disable: [nvidia, intel] # drop the nvidia GPU and every intel-vendor contribution
|
||||||
|
expect_profile: { fingerprint: false, bluetooth: false }
|
||||||
|
expect_contains: [mesa, vulkan-icd-loader]
|
||||||
|
expect_excludes: [nvidia-open-dkms, intel-ucode, vulkan-intel, sof-firmware, linux-firmware-other]
|
||||||
22
tests/hardware/pkg_fixtures.yml
Normal file
22
tests/hardware/pkg_fixtures.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
# profile + os_family -> resolved package list. expect_contains: all must be present;
|
||||||
|
# expect_excludes: none may be present. Features default all-on with auto peripherals.
|
||||||
|
package_fixtures:
|
||||||
|
- name: arch-intel-audio-bt-fp
|
||||||
|
os_family: Archlinux
|
||||||
|
profile: { cpu: intel, gpus: [intel], wireless: [intel], audio: [intel], fingerprint: true, bluetooth: true, camera: { uvc: true, ipu6: false } }
|
||||||
|
expect_contains: [intel-ucode, linux-firmware-other, mesa, vulkan-icd-loader, vulkan-intel, sof-firmware, alsa-ucm-conf, bluez, bluez-utils, fprintd, libfprint, v4l-utils]
|
||||||
|
expect_excludes: [nvidia-open-dkms, evdi]
|
||||||
|
|
||||||
|
- name: debian-amd-audio-no-bt-no-fp
|
||||||
|
os_family: Debian
|
||||||
|
profile: { cpu: amd, gpus: [amd], wireless: [realtek], audio: [amd], fingerprint: false, bluetooth: false, camera: { uvc: false, ipu6: false } }
|
||||||
|
expect_contains: [amd64-microcode, firmware-linux-free, firmware-amd-graphics, firmware-realtek, mesa-vulkan-drivers, firmware-sof-signed, alsa-ucm-conf]
|
||||||
|
expect_excludes: [bluez, fprintd, libpam-fprintd, v4l-utils]
|
||||||
|
|
||||||
|
- name: redhat-intel-nvidia-bt
|
||||||
|
os_family: RedHat
|
||||||
|
profile: { cpu: intel, gpus: [intel, nvidia], wireless: [intel], audio: [intel], fingerprint: false, bluetooth: true, camera: { uvc: false, ipu6: true } }
|
||||||
|
nvidia_driver_resolved: open
|
||||||
|
expect_contains: [microcode_ctl, linux-firmware, mesa-dri-drivers, vulkan-loader, akmod-nvidia-open, alsa-sof-firmware, alsa-ucm, bluez, v4l-utils]
|
||||||
|
expect_excludes: [fprintd, evdi]
|
||||||
16
tests/hardware/test_detection.yml
Normal file
16
tests/hardware/test_detection.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# Run: ansible-playbook tests/hardware/test_detection.yml
|
||||||
|
- name: Hardware detection fixture tests
|
||||||
|
hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
vars_files:
|
||||||
|
- ../../roles/environment/defaults/main.yml
|
||||||
|
- fixtures.yml
|
||||||
|
tasks:
|
||||||
|
- name: Run each detection fixture
|
||||||
|
ansible.builtin.include_tasks: _assert_fixture.yml
|
||||||
|
loop: "{{ hardware_fixtures }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: fixture
|
||||||
|
label: "{{ fixture.name }}"
|
||||||
15
tests/hardware/test_merge.yml
Normal file
15
tests/hardware/test_merge.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
# Run: ansible-playbook tests/hardware/test_merge.yml
|
||||||
|
- name: Hardware merge-layer fixture tests
|
||||||
|
hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
vars_files:
|
||||||
|
- merge_fixtures.yml
|
||||||
|
tasks:
|
||||||
|
- name: Run each merge fixture
|
||||||
|
ansible.builtin.include_tasks: _assert_merge.yml
|
||||||
|
loop: "{{ merge_fixtures }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: mf
|
||||||
|
label: "{{ mf.name }}"
|
||||||
15
tests/hardware/test_packages.yml
Normal file
15
tests/hardware/test_packages.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
# Run: ansible-playbook tests/hardware/test_packages.yml
|
||||||
|
- name: Hardware package-resolution fixture tests
|
||||||
|
hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
vars_files:
|
||||||
|
- pkg_fixtures.yml
|
||||||
|
tasks:
|
||||||
|
- name: Run each package fixture
|
||||||
|
ansible.builtin.include_tasks: _assert_packages.yml
|
||||||
|
loop: "{{ package_fixtures }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: pf
|
||||||
|
label: "{{ pf.name }}"
|
||||||
Reference in New Issue
Block a user