LenovoLegionLinux

AGENTS.md

Guidance for AI coding agents (and humans) working on LenovoLegionLinux (LLL).

What this project is

LLL is a Linux driver + userspace toolkit for Lenovo Legion laptops (an open alternative to Lenovo Vantage / Legion Zone on Windows). It talks to the embedded controller (EC) and ACPI/WMI firmware to expose fan curves, power modes, sensors, battery conservation, and more through standard Linux interfaces (sysfs, debugfs, hwmon).

This project writes to hardware (EC memory, ACPI methods). Changes to the kernel module, legiond, or SmartFan can affect real fan/thermal behavior. Prefer conservative defaults and never enable risky behavior unconditionally.

Repository layout

Path What it is Language
kernel_module/ legion-laptop.c kernel module (sysfs/hwmon/debugfs/platform-profile) C (kernel)
python/legion_linux/ Python package: legion.py (library), legion_cli.py, legion_gui.py Python 3
extra/service/legiond/ legiond + legiond-ctl daemon (auto fan-profile switching) C (gnu2x)
extra/service/ systemd/OpenRC units, legiond.ini, fan curve profiles/*.yaml shell/ini/yaml
extra/smartfan/ Standalone shell fan daemon for Legion 7 Gen 10+ (needs only acpi_call) bash
deploy/ Packaging: RPM specs, Dockerfiles, per-distro dependency scripts shell/spec
tests/ Test/lint scripts — these are exactly what CI runs bash
subprojects/ AUR DKMS packaging PKGBUILD
doc/ Assets, FEATURES_AND_TESTING.md, REVERSE_ENGINEER.md, TESTPLAN.md markdown

Build, test, lint — per component

CI (.github/workflows/build.yml, ubuntu-24.04) installs dependencies via deploy/dependencies/install_dependencies_ubuntu_24_04.sh (build) and install_development_dependencies_ubuntu_24_04.sh (lint), then runs the scripts below. Run the same scripts locally before committing.

Kernel module (kernel_module/)

cd kernel_module
make                      # build (needs linux-headers for the running kernel)
sudo make reloadmodule    # unload + reload in-place for testing
sudo make forcereloadmodule   # same, but bypass the DMI model allowlist
sudo make install         # permanent install
sudo make dkms            # install via DKMS (auto-rebuild on kernel updates)

legiond daemon (extra/service/legiond/)

cd extra/service/legiond
make          # builds legiond and legiond-ctl; gcc -std=gnu2x -Wall -Wextra
make clean    # requires libinih (links -linih)

Python package (python/legion_linux/)

# Lint gate — MUST pass (CI runs exactly this):
./tests/test_python.sh
# = pylint --rcfile python/legion_linux/pylintrc python/legion_linux/legion_linux

# CLI smoke test (works without the kernel module loaded):
./tests/test_python_cli.sh

# Format (config in pyproject.toml, line-length 120 to match pylintrc):
black python/legion_linux

SmartFan (extra/smartfan/)

Pure bash + acpi_call. install.sh installs the daemon, TUI switcher (workmode), and turbo toggles. Test changes with bash -n and shellcheck if available.

Contribution conventions

Useful verification commands on real hardware

sudo dmesg | grep -i legion              # module probe status
sensors                                  # legion_hwmon temps/fan RPM
sudo cat /sys/kernel/debug/legion/fancurve   # EC fan curve debug dump
cat /sys/firmware/acpi/platform_profile      # current power mode

If the module refuses to load with “not in allowlist”, that is the DMI allowlist working as intended — test with sudo make forcereloadmodule and report the model/BIOS instead of silently widening the allowlist.