diff -Nru power-profiles-daemon-0.10.1/.ci/fail_skipped_tests.py power-profiles-daemon-0.21/.ci/fail_skipped_tests.py
--- power-profiles-daemon-0.10.1/.ci/fail_skipped_tests.py 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/.ci/fail_skipped_tests.py 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,39 @@
+#!/usr/bin/python3
+
+from lxml import etree
+import sys
+
+
+def format_title(title):
+ """Put title in a box"""
+ box = {
+ "tl": "╔",
+ "tr": "╗",
+ "bl": "╚",
+ "br": "╝",
+ "h": "═",
+ "v": "║",
+ }
+ hline = box["h"] * (len(title) + 2)
+
+ return "\n".join(
+ [
+ f"{box['tl']}{hline}{box['tr']}",
+ f"{box['v']} {title} {box['v']}",
+ f"{box['bl']}{hline}{box['br']}",
+ ]
+ )
+
+
+tree = etree.parse(sys.argv[1])
+for suite in tree.xpath("/testsuites/testsuite"):
+ skipped = suite.get("skipped")
+ if int(skipped) != 0:
+ print(
+ format_title(
+ "Tests were skipped when they should not have been. All the tests must be run in the CI"
+ ),
+ end="\n\n",
+ flush=True,
+ )
+ sys.exit(1)
diff -Nru power-profiles-daemon-0.10.1/.gitignore power-profiles-daemon-0.21/.gitignore
--- power-profiles-daemon-0.10.1/.gitignore 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/.gitignore 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,3 @@
+power-profiles-daemon
+__pycache__
+.vscode
diff -Nru power-profiles-daemon-0.10.1/.gitlab-ci.yml power-profiles-daemon-0.21/.gitlab-ci.yml
--- power-profiles-daemon-0.10.1/.gitlab-ci.yml 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/.gitlab-ci.yml 2024-04-03 23:55:02.000000000 +0000
@@ -2,7 +2,6 @@
variables:
DEPENDENCIES: gcc
- gtk-doc
pkgconfig(udev)
pkgconfig(systemd)
pkgconfig(gio-2.0)
@@ -12,24 +11,224 @@
systemd
meson
git
- python3-gobject
- python3-dbusmock
- python3-pylint
- umockdev
+ python3-packaging
+ TEST_DEPENDENCIES: e2fsprogs
+ python3-dbusmock
+ python3-gobject
+ python3-pylint
+ umockdev
+ TEST_DEBUG_DEPENDENCIES: glib2
+ glibc
+ libgudev
+ upower
+ polkit-libs
-build_stable:
+workflow:
+ rules:
+ - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+ - if: $CI_PIPELINE_SOURCE == 'push'
+
+.install-deps:
+ variables:
+ TMPDIR: $CI_BUILDS_DIR/tmpdir
before_script:
- - dnf upgrade -y --nogpgcheck fedora-release fedora-repos*
- - dnf update -y && dnf install -y $DEPENDENCIES
+ - echo 8096000 > /proc/sys/fs/inotify/max_user_instances
+ - mkdir -m 700 $TMPDIR -p
+ - if command -v dnf5 &>/dev/null; then ln -sfv $(command -v dnf5) /usr/local/bin/dnf; fi
+ - dnf update -y
+ - dnf install -y $DEPENDENCIES $JOB_DEPS
+ # dnf5 does not include debuginfo-install yet, so let's use dnf4 instead.
+ - if [ -n "$JOB_DEBUG_DEPS" ]; then
+ if ! command -v dnf4 &>/dev/null; then
+ dnf install -y dnf4;
+ dnf4 update -y;
+ fi;
+ dnf4 install -y "dnf-command(debuginfo-install)";
+ dnf4 debuginfo-install -y $JOB_DEBUG_DEPS;
+ fi
+
+pre_commit:
+ variables:
+ DEPENDENCIES: {}
+ JOB_DEPS: pre-commit
+ git
+ extends:
+ - .install-deps
+ script:
+ - pre-commit run --all-files
+
+build_and_test:
+ variables:
+ JOB_DEPS: $TEST_DEPENDENCIES
+ gcovr
+ python3-coverage
+ JOB_DEBUG_DEPS: $TEST_DEBUG_DEPENDENCIES
+ extends:
+ - .install-deps
script:
- - meson -Dgtk_doc=true -Dpylint=true _build
- - ninja -v -C _build
- - ninja -v -C _build install
- - ninja -v -C _build uninstall
- - ninja -v -C _build dist
- - meson test -C _build
+ - meson setup
+ --werror
+ --fatal-meson-warnings
+ --warnlevel 2
+ -Dpylint=enabled
+ -Db_coverage=true
+ _build
+ - meson test -C _build --print-errorlogs
+ - .ci/fail_skipped_tests.py _build/meson-logs/testlog.junit.xml
+ - ninja -C _build coverage
+ - python_coverage_data=_build/meson-logs/python.coverage
+ - coverage3 combine --data-file=$python_coverage_data
+ _build/python-coverage/*
+ - coverage3 xml --data-file=$python_coverage_data
+ -o $python_coverage_data.xml
+ - sed "s,_build/src/,src/,g" -i $python_coverage_data.xml
+ - coverage3 report --data-file=$python_coverage_data
+ - coverage3 html --data-file=$python_coverage_data
+ -d _build/meson-logs/python-coverage-html
+ - cat _build/meson-logs/coverage.txt || true
artifacts:
when: always
paths:
- _build/meson-logs/*.txt
+ - _build/meson-logs/*.xml
- _build/meson-dist/*
+ - _build/meson-logs/coveragereport/*
+ - _build/meson-logs/python-coverage-html/*
+ reports:
+ junit:
+ - _build/meson-logs/testlog.junit.xml
+ coverage_report:
+ coverage_format: cobertura
+ path: _build/meson-logs/*coverage.xml
+ coverage: '/^TOTAL.*\s+(\d+\%)$/'
+
+dist_install:
+ variables:
+ JOB_DEPS: argparse-manpage
+ bash-completion-devel
+ python3-gobject
+ python3-shtab
+ extends:
+ - .install-deps
+ script:
+ - meson setup
+ --werror
+ --fatal-meson-warnings
+ --warnlevel 2
+ -Dmanpage=enabled
+ -Dbashcomp=enabled
+ -Dzshcomp=enabled
+ -Dtests=false
+ _build
+ - meson install -C _build
+ - ninja -C _build uninstall -v
+ - meson dist -C _build
+ artifacts:
+ when: always
+ paths:
+ - _build/meson-logs/*.txt
+ rules:
+ - if: ($CI_PIPELINE_SOURCE == "merge_request_event" ||
+ $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH)
+
+address_sanitizer:
+ variables:
+ JOB_DEPS: $TEST_DEPENDENCIES
+ libasan
+ libubsan
+ JOB_DEBUG_DEPS: $TEST_DEBUG_DEPENDENCIES
+ extends:
+ - .install-deps
+ script:
+ - meson setup
+ --werror
+ --buildtype=debug
+ _build
+ -Db_sanitize=address,undefined
+ - meson test -C _build --print-errorlogs -t 3
+ artifacts:
+ when: on_failure
+ paths:
+ - _build/meson-logs/*.txt
+
+valgrind:
+ variables:
+ JOB_DEPS: $TEST_DEPENDENCIES
+ valgrind
+ JOB_DEBUG_DEPS: $TEST_DEBUG_DEPENDENCIES
+ extends:
+ - .install-deps
+ script:
+ - meson setup
+ --werror
+ --buildtype=debug
+ _build
+ - meson test -C _build --print-errorlogs --setup=valgrind
+ artifacts:
+ when: on_failure
+ paths:
+ - _build/meson-logs/*.txt
+
+scan_build:
+ variables:
+ JOB_DEPS: clang-analyzer
+ which
+ extends:
+ - .install-deps
+ script:
+ - meson setup _build -Dtests=false
+ - env SCANBUILD=$(which scan-build) ninja -C _build scan-build
+ artifacts:
+ when: on_failure
+ paths:
+ - _build/meson-logs
+
+docs:
+ variables:
+ JOB_DEPS: gtk-doc
+ extends:
+ - .install-deps
+ script:
+ - meson setup
+ -Dgtk_doc=true
+ -Dtests=false
+ _build-docs
+ - ninja -C _build-docs power-profiles-daemon-doc
+ artifacts:
+ expose_as: "HTML Documentation"
+ paths:
+ - _build-docs/docs/html
+ - _build-docs/docs/html/index.html
+ expire_in: 1 week
+ rules:
+ - if: ($CI_PIPELINE_SOURCE == "merge_request_event" ||
+ $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH)
+
+pages:
+ image: alpine:latest
+ stage: deploy
+ needs:
+ - job: build_and_test
+ artifacts: true
+ - job: docs
+ artifacts: true
+ script:
+ - mv _build-docs/docs/html public
+ - mkdir public/coverage
+ - mv _build/meson-logs/coveragereport public/coverage/daemon
+ - mv _build/meson-logs/python-coverage-html public/coverage/tool
+ - echo '
+
+
Power Profiles Daemon Coverage
+
+
+
+ ' > public/coverage/index.html
+ artifacts:
+ paths:
+ - public
+ rules:
+ - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
diff -Nru power-profiles-daemon-0.10.1/.markdownlint.json power-profiles-daemon-0.21/.markdownlint.json
--- power-profiles-daemon-0.10.1/.markdownlint.json 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/.markdownlint.json 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,10 @@
+{
+ "default": true,
+ "MD033": false,
+ "MD041": false,
+ "MD036": false,
+ "MD013": {
+ "tables": false,
+ "line_length": 1000
+ }
+}
diff -Nru power-profiles-daemon-0.10.1/.pre-commit-config.yaml power-profiles-daemon-0.21/.pre-commit-config.yaml
--- power-profiles-daemon-0.10.1/.pre-commit-config.yaml 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/.pre-commit-config.yaml 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,39 @@
+default_stages: [commit]
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.4.0
+ hooks:
+ - id: no-commit-to-branch
+ args: [--branch, main]
+ - id: check-added-large-files
+ - id: check-byte-order-marker
+ - id: check-executables-have-shebangs
+ - id: forbid-new-submodules
+ - id: check-yaml
+ - id: check-json
+ - id: pretty-format-json
+ args: ['--no-sort-keys']
+ - id: check-symlinks
+ - id: check-xml
+ - id: end-of-file-fixer
+ types_or: [c, shell, python]
+ - id: trailing-whitespace
+ types_or: [c, shell, python, xml]
+ - id: check-docstring-first
+ - id: check-merge-conflict
+ - id: mixed-line-ending
+ args: [--fix=lf]
+- repo: https://github.com/codespell-project/codespell
+ rev: v2.2.6
+ hooks:
+ - id: codespell
+ args: ['--write-changes']
+- repo: https://github.com/ambv/black
+ rev: 23.12.0
+ hooks:
+ - id: black
+- repo: https://github.com/igorshubovych/markdownlint-cli
+ rev: v0.38.0
+ hooks:
+ - id: markdownlint
+ args: ['--fix']
diff -Nru power-profiles-daemon-0.10.1/NEWS power-profiles-daemon-0.21/NEWS
--- power-profiles-daemon-0.10.1/NEWS 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/NEWS 2024-04-03 23:55:02.000000000 +0000
@@ -1,3 +1,81 @@
+0.21
+----
+
+Since this release power-profiles-daemon is battery-state aware and some drivers
+use a more power efficient state when using the balanced profile on battery.
+In particular both the AMD and Intel P-State drivers will use the
+balance_power EPP profile, while for Intel one we also set the energy
+performance bias to 8 (instead of 6).
+
+This release also contains various fixes for the powerprofilesctl command line
+tool when using the launch or version commands.
+The tool is now better documented as we generate a manual page for it (if
+python3-argparse is installed) and bash completions. We're even generating the
+ZSH completions, but the install path must be provided.
+
+The daemon command line interface has been improved for debugging, so use
+--help-debug for further information.
+
+The systemd service lockdown settings have been restricted even more.
+
+Various code optimizations.
+
+0.20
+----
+
+The project has moved under the freedesktop 'Upower' group. The service is
+now advertised as 'org.freedesktop.UPower.PowerProfiles' in addition to the
+previous 'net.hadess.PowerProfiles' for compatibility reasons.
+
+This release adds support for:
+
+* Multiple power-profiles-daemon drivers to load simultaneously. This notably
+ allows both CPU based control with amd-pstate or intel-pstate as well as
+ ACPI platform profile based control.
+
+* amdgpu panel power savings which uses dedicated hardware in systems with
+ integrated Radeon graphics to decrease panel power consumption when the
+ system is on battery.
+
+This release also enables the test suite by default, so distribution vendors
+should update packaging accordingly.
+
+0.13
+----
+
+This release adds support for the AMD P-State driver that's been added to the
+6.3 Linux kernel. This release also fixes mismatched profiles on some HP
+laptops and some miscellaneous bug fixes.
+
+0.12
+----
+
+This release adds support for the Intel "Energy Performance Bias" feature, which
+can be used on hardware that doesn't have a platform_profile or doesn't support
+HWP. It will also be used to eke out a bit more performance, or power, on systems
+which already supported HWP.
+
+More information is available in the README.
+
+0.11.1
+------
+
+This release stops power-profiles-daemon from modifying the cpufreq driver when
+driver when the user/administrator has chosen to disable the Intel P-State scaling
+governor (eg. forcing a passive operation mode).
+
+More information is available in the README.
+
+0.11
+----
+
+This release fixes problems on Intel machines when the CPUs didn't support turbo at
+all, or the performance scaling governor was built as default in the kernel.
+
+It also adds better end-user documentation, fixes in the command-line tool to not
+cause bug report tools to popup on not-uncommon errors, and a bug fix for running
+on some systems with controllable charge speeds.
+
0.10.1
------
diff -Nru power-profiles-daemon-0.10.1/README.md power-profiles-daemon-0.21/README.md
--- power-profiles-daemon-0.10.1/README.md 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/README.md 2024-04-03 23:55:02.000000000 +0000
@@ -1,18 +1,17 @@
-power-profiles-daemon
-=====================
+# power-profiles-daemon
Makes power profiles handling available over D-Bus.
-Installation
-------------
+## Installation
+
```sh
-$ meson _build -Dprefix=/usr
-$ ninja -v -C _build install
+meson setup _build -Dprefix=/usr
+ninja -C _build install
```
+
It requires libgudev, systemd and polkit-gobject.
-Introduction
-------------
+## Introduction
power-profiles-daemon offers to modify system behaviour based upon user-selected
power profiles. There are 3 different power profiles, a "balanced" default mode,
@@ -30,8 +29,41 @@
such as turning the screen off after inaction more aggressively when in power-saver
mode.
-Debugging
----------
+## How to use
+
+There are interfaces to switch profiles in the latest versions of KDE and GNOME. Those
+desktops also include more thorough integration with its low-power mode. Please check
+the user guides for each of them for details.
+
+power-profiles-daemon also ships with a command-line utility called `powerprofilesctl`
+which can be used for scripting, as it allows getting and setting the active profile,
+listing the available profiles, and launching commands while holding the performance
+or the power-saver profile.
+
+For example, this will be useful to avoid manual switching profiles while compiling
+large projects:
+
+```sh
+powerprofilesctl launch make
+```
+
+If you're a developer, you might also want to use GLib's [`GPowerProfileMonitor`](https://docs.gtk.org/gio/iface.PowerProfileMonitor.html)
+through C, or one of its bindings, so your application can react to the user requesting
+a low-power mode.
+
+## Conflicts
+
+If `power-profiles-daemon` refuses to start, it's likely that you have [a conflicting
+service installed and running](data/power-profiles-daemon.service.in#L3), or your
+distribution ships [a version of tlp that actively breaks power-profiles-daemon](https://bugzilla.redhat.com/show_bug.cgi?id=2028701#c11),
+or you use the [upstream package](https://github.com/linrunner/TLP/commit/6a9388e1af95051a90a33b4014af1158dfa241f6).
+
+```sh
+systemctl unmask power-profiles-daemon.service
+systemctl start power-profiles-daemon.service
+```
+
+## Debugging
You can now check which mode is in use, and which ones are available by running:
@@ -51,19 +83,134 @@
Those commands are also available through the D-Bus interface:
-```
-gdbus introspect --system --dest net.hadess.PowerProfiles --object-path /net/hadess/PowerProfiles
-gdbus call --system --dest net.hadess.PowerProfiles --object-path /net/hadess/PowerProfiles --method org.freedesktop.DBus.Properties.Set 'net.hadess.PowerProfiles' 'ActiveProfile' "<'power-saver'>"
+```sh
+gdbus introspect --system --dest org.freedesktop.UPower.PowerProfiles \
+ --object-path /org/freedesktop/UPower/PowerProfiles
+gdbus call --system --dest org.freedesktop.UPower.PowerProfiles \
+ --object-path /org/freedesktop/UPower/PowerProfiles \
+ --method org.freedesktop.DBus.Properties.Set 'org.freedesktop.UPower.PowerProfiles' \
+ 'ActiveProfile' "<'power-saver'>"
```
If that doesn't work, please file an issue, attach the output of:
```sh
-sudo G_MESSAGES_DEBUG=all /usr/libexec/power-profiles-daemon -r -v
+sudo /usr/libexec/power-profiles-daemon -r -vv
```
-Testing
--------
+## Operations on Intel-based machines
+
+The "driver" for making the hardware act on the user-selected power profile on Intel
+CPU-based machines is based on the [Intel P-State scaling driver](https://www.kernel.org/doc/html/v5.17/admin-guide/pm/intel_pstate.html)
+or the Energy Performance Bias (EPB) feature if available.
+
+It is only used if the CPU supports either hardware-managed P-states (HWP)
+or Energy Performance Bias (EPB).
+
+Example of a system without `platform_profile support` but with `active` P-State
+operation mode:
+
+```sh
+$ cat /sys/firmware/acpi/platform_profile_choices
+cat: /sys/firmware/acpi/platform_profile_choices: No such file or directory
+$ cat /sys/devices/system/cpu/intel_pstate/status
+active
+```
+
+Example of a system with `EPB` support:
+
+```sh
+$ cat /sys/devices/system/cpu/cpu0/power/energy_perf_bias
+0
+```
+
+If the Intel P-State scaling driver is in `passive` mode, either because the system doesn't
+support HWP, or the administrator has disabled it, and `EPB` isn't available, then the
+placeholder driver will be used, and there won't be a performance mode.
+
+Finally, if the Intel P-State scaling driver is used in `active` mode, the P-State
+scaling governor will be changed to `powersave` as it is the only P-State scaling
+governor that allows for the "Energy vs Performance Hints" to be taken into consideration,
+ie. the only P-State scaling governor that allows HWP to work.
+
+For more information, please refer to the [Intel P-State scaling driver documentation](https://www.kernel.org/doc/html/v5.17/admin-guide/pm/intel_pstate.html)
+and the [Intel Performance and Energy Bias Hint](https://www.kernel.org/doc/html/v5.17/admin-guide/pm/intel_epb.html).
+
+## Operations on AMD-based machines
+
+### CPU power savings
+
+The "driver" for making the hardware act on the user-selected power profile on AMD CPU-based
+machines is based on the [AMD P-State scaling driver](https://www.kernel.org/doc/html/v6.3/admin-guide/pm/amd-pstate.html)
+if available.
+
+It is only used if the CPU supports Collaborative Processor Performance
+Control (CPPC), the machine is a laptop or workstation and the
+AMD P-State scaling driver is in `active` mode.
+
+Example of a system without `platform_profile` support but with `active` P-State
+operation mode:
+
+```sh
+$ cat /sys/firmware/acpi/platform_profile_choices
+cat: /sys/firmware/acpi/platform_profile_choices: No such file or directory
+$ cat /sys/devices/system/cpu/amd_pstate/status
+active
+```
+
+If the AMD P-State scaling driver is not loaded or is not in `active` mode, then
+the placeholder driver will be used, and there won't be a performance mode.
+
+Finally, if the AMD P-State scaling driver is used in `active` mode, the P-State
+scaling governor will be changed to `powersave` as it is the only P-State scaling
+governor that allows for the "Energy vs Performance Hints" to be taken into consideration.
+
+For more information, please refer to the [AMD P-State scaling driver documentation](https://www.kernel.org/doc/html/v6.3/admin-guide/pm/amd-pstate.html).
+
+### Panel power savings
+
+Laptops with integrated Radeon graphics have a dedicated hardware function
+to decrease panel power consumption in exchange for color accuracy. This
+function is used when the system is on battery and the user has selected
+the "balanced" or "power-saver" profiles.
+
+If you decide that you don't like how this behaves, you can disable the function
+in one of two ways:
+
+1. Adding `amdgpu.abmlevel=0` to the kernel command line. This will disable abm
+ value changes entirely.
+2. By using `--block-action=amdgpu_panel_power` in the
+ `power-profiles-daemon` `ExecStart` command. This will allow you to
+ still change values manually in sysfs but `power-profiles-daemon` will not
+ change anything.
+
+## Multiple driver and multiple action operations
+
+Power-profiles daemon will load all supported drivers and actions by default.
+If you have a problem with a given driver or action, you can disable it by
+using `--block-action` or `--block-driver` with the name of the driver or action you want to disable
+in the environment that launches the daemon (such as the systemd unit file).
+
+For example to edit the unit:
+
+```sh
+sudo systemctl edit power-profiles-daemon.service
+```
+
+Then modify the Execstart line to the drop-in file:
+
+```text
+[Service]
+ExecStart=/usr/libexec/power-profiles-daemon --block-action=FOO
+```
+
+Then restart the service:
+
+```sh
+sudo systemctl try-restart power-profiles-daemon.service
+```
+
+## Testing
If you don't have hardware that can support the performance mode, or the degraded mode
you can manually run the `power-profiles-daemon` binary as `root` with the environment
@@ -73,8 +220,7 @@
sudo POWER_PROFILE_DAEMON_FAKE_DRIVER=1 /usr/libexec/power-profiles-daemon -r -v
```
-References
-----------
+## References
- [Use Low Power Mode to save battery life on your iPhone (iOS)](https://support.apple.com/en-us/HT205234)
- [lowPowerModeEnabled (iOS)](https://developer.apple.com/documentation/foundation/nsprocessinfo/1617047-lowpowermodeenabled?language=objc)
@@ -82,8 +228,7 @@
- [[S]ettings that use less battery (Android)](https://support.google.com/android/answer/7664692?hl=en&visit_id=637297348326801871-2263015427&rd=1)
- [EnergySaverStatus Enum (Windows)](https://docs.microsoft.com/en-us/uwp/api/windows.system.power.energysaverstatus?view=winrt-19041)
-Why power-profiles-daemon
--------------------------
+## Why power-profiles-daemon
The power-profiles-daemon project was created to help provide a solution for
two separate use cases, for desktops, laptops, and other devices running a
@@ -107,8 +252,7 @@
10 years. We would also design that API to be as easily usable to build
graphical interfaces as possible.
-Why not...
-----------
+## Why not
This section will contain explanations of why this new daemon was written
rather than re-using, or modifying an existing one. Each project obviously
@@ -150,6 +294,7 @@
much energy as possible under high-CPU usage.
### [slimbookbattery](https://launchpad.net/~slimbook)
+
This is **not** free software (*Source code available but not modifiable
without express authorization.*). The application does a lot of things in
addition to the "3 profiles" selection:
diff -Nru power-profiles-daemon-0.10.1/check-news.sh power-profiles-daemon-0.21/check-news.sh
--- power-profiles-daemon-0.10.1/check-news.sh 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/check-news.sh 2024-04-03 23:55:02.000000000 +0000
@@ -24,13 +24,13 @@
#
# Checks NEWS for the version number:
# meson.add_dist_script(
-# find_program('check-news.sh').path(),
+# find_program('check-news.sh').full_path(),
# '@0@'.format(meson.project_version())
# )
#
# Checks NEWS and data/foo.appdata.xml for the version number:
# meson.add_dist_script(
-# find_program('check-news.sh').path(),
+# find_program('check-news.sh').full_path(),
# '@0@'.format(meson.project_version()),
# 'NEWS',
# 'data/foo.appdata.xml'
@@ -48,9 +48,9 @@
# Look in the first 15 lines for NEWS files, but look
# everywhere for other types of files
if [ "$2" = "NEWS" ]; then
- DATA=`sed 15q $SRC_ROOT/"$2"`
+ DATA=$(sed 15q "$SRC_ROOT/$2")
else
- DATA=`cat $SRC_ROOT/"$2"`
+ DATA=$(cat "$SRC_ROOT/$2")
fi
case "$DATA" in
*"$VERSION"*)
@@ -71,12 +71,12 @@
shift
if [ $# -eq 0 ] ; then
- check_version $VERSION 'NEWS'
+ check_version "$VERSION" 'NEWS'
exit 0
fi
-for i in $@ ; do
- check_version $VERSION "$i"
+for i in "$@"; do
+ check_version "$VERSION" "$i"
done
-exit 0
\ No newline at end of file
+exit 0
diff -Nru power-profiles-daemon-0.10.1/data/meson.build power-profiles-daemon-0.21/data/meson.build
--- power-profiles-daemon-0.10.1/data/meson.build 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/data/meson.build 2024-04-03 23:55:02.000000000 +0000
@@ -1,32 +1,40 @@
-data_conf = configuration_data()
-data_conf.set('libexecdir', libexecdir)
-
configure_file(
input: 'power-profiles-daemon.service.in',
output: 'power-profiles-daemon.service',
- configuration: data_conf,
+ configuration: {
+ 'libexecdir': libexecdir,
+ },
install_dir: systemd_system_unit_dir,
)
-configure_file(
- input: 'net.hadess.PowerProfiles.conf.in',
- output: 'net.hadess.PowerProfiles.conf',
- configuration: data_conf,
- install_dir: dbusconfdir
-)
+foreach name, _: bus_names
+ config = {
+ 'dbus_name': name,
+ 'dbus_iface': name,
+ }
-install_data(
- 'net.hadess.PowerProfiles.service',
- install_dir: dbusservicedir
-)
+ configure_file(
+ input: 'power-profiles-daemon.dbus.conf.in',
+ output: name + '.conf',
+ configuration: config,
+ install_dir: dbusconfdir
+ )
+
+ configure_file(
+ input: 'power-profiles-daemon.dbus.service.in',
+ output: name + '.service',
+ configuration: config,
+ install_dir: dbusservicedir
+ )
+endforeach
-polkit_policy = 'net.hadess.PowerProfiles.policy'
+polkit_policy = 'power-profiles-daemon.policy'
if xmllint.found()
test(polkit_policy,
xmllint,
args: [
'--noout',
- meson.source_root() / 'data' / polkit_policy,
+ meson.project_source_root() / 'data' / polkit_policy,
])
endif
diff -Nru power-profiles-daemon-0.10.1/data/net.hadess.PowerProfiles.conf.in power-profiles-daemon-0.21/data/net.hadess.PowerProfiles.conf.in
--- power-profiles-daemon-0.10.1/data/net.hadess.PowerProfiles.conf.in 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/data/net.hadess.PowerProfiles.conf.in 1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff -Nru power-profiles-daemon-0.10.1/data/net.hadess.PowerProfiles.policy power-profiles-daemon-0.21/data/net.hadess.PowerProfiles.policy
--- power-profiles-daemon-0.10.1/data/net.hadess.PowerProfiles.policy 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/data/net.hadess.PowerProfiles.policy 1970-01-01 00:00:00.000000000 +0000
@@ -1,31 +0,0 @@
-
-
-
-
-
- power-profiles-daemon
- https://gitlab.freedesktop.org/hadess/power-profiles-daemon
-
-
- Switch Power Profile
- Privileges are required to switch power profiles.
-
- no
- no
- yes
-
-
-
-
- Hold Power Profile
- Privileges are required to hold power profiles.
-
- no
- no
- yes
-
-
-
-
diff -Nru power-profiles-daemon-0.10.1/data/net.hadess.PowerProfiles.service power-profiles-daemon-0.21/data/net.hadess.PowerProfiles.service
--- power-profiles-daemon-0.10.1/data/net.hadess.PowerProfiles.service 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/data/net.hadess.PowerProfiles.service 1970-01-01 00:00:00.000000000 +0000
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 3 as published by
-# the Free Software Foundation.
-
-[D-BUS Service]
-Name=net.hadess.PowerProfiles
-Exec=/bin/false
-User=root
-SystemdService=power-profiles-daemon.service
diff -Nru power-profiles-daemon-0.10.1/data/power-profiles-daemon.dbus.conf.in power-profiles-daemon-0.21/data/power-profiles-daemon.dbus.conf.in
--- power-profiles-daemon-0.10.1/data/power-profiles-daemon.dbus.conf.in 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/data/power-profiles-daemon.dbus.conf.in 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru power-profiles-daemon-0.10.1/data/power-profiles-daemon.dbus.service.in power-profiles-daemon-0.21/data/power-profiles-daemon.dbus.service.in
--- power-profiles-daemon-0.10.1/data/power-profiles-daemon.dbus.service.in 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/data/power-profiles-daemon.dbus.service.in 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 3 as published by
+# the Free Software Foundation.
+
+[D-BUS Service]
+Name=@dbus_name@
+Exec=/bin/false
+User=root
+SystemdService=power-profiles-daemon.service
diff -Nru power-profiles-daemon-0.10.1/data/power-profiles-daemon.policy power-profiles-daemon-0.21/data/power-profiles-daemon.policy
--- power-profiles-daemon-0.10.1/data/power-profiles-daemon.policy 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/data/power-profiles-daemon.policy 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,31 @@
+
+
+
+
+
+ power-profiles-daemon
+ https://gitlab.freedesktop.org/hadess/power-profiles-daemon
+
+
+ Switch Power Profile
+ Privileges are required to switch power profiles.
+
+ no
+ no
+ yes
+
+
+
+
+ Hold Power Profile
+ Privileges are required to hold power profiles.
+
+ no
+ no
+ yes
+
+
+
+
diff -Nru power-profiles-daemon-0.10.1/data/power-profiles-daemon.service.in power-profiles-daemon-0.21/data/power-profiles-daemon.service.in
--- power-profiles-daemon-0.10.1/data/power-profiles-daemon.service.in 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/data/power-profiles-daemon.service.in 2024-04-03 23:55:02.000000000 +0000
@@ -5,23 +5,36 @@
[Service]
Type=dbus
-BusName=net.hadess.PowerProfiles
+BusName=org.freedesktop.UPower.PowerProfiles
+# To enable debugging add a -vv to the ExecStart line
ExecStart=@libexecdir@/power-profiles-daemon
Restart=on-failure
# This always corresponds to /var/lib/power-profiles-daemon
StateDirectory=power-profiles-daemon
-#Uncomment this to enable debug
-#Environment="G_MESSAGES_DEBUG=all"
# Lockdown
+DevicePolicy=closed
+KeyringMode=private
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+PrivateTmp=yes
+ProtectClock=yes
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectProc=invisible
ProtectSystem=strict
-ProtectControlGroups=true
-ProtectHome=true
-ProtectKernelModules=true
-PrivateTmp=true
+RemoveIPC=yes
RestrictAddressFamilies=AF_UNIX AF_LOCAL AF_NETLINK
MemoryDenyWriteExecute=true
RestrictRealtime=true
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
[Install]
WantedBy=graphical.target
diff -Nru power-profiles-daemon-0.10.1/debian/changelog power-profiles-daemon-0.21/debian/changelog
--- power-profiles-daemon-0.10.1/debian/changelog 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/changelog 2024-04-09 16:21:36.000000000 +0000
@@ -1,3 +1,114 @@
+power-profiles-daemon (0.21-1~22.04.1) jammy; urgency=medium
+
+ * Backport to jammy (LP: #2008958)
+ * Disable bashcomp
+ * Remove depends on python3-shtab and systemd-dev
+
+ -- Mario Limonciello Tue, 09 Apr 2024 11:21:36 -0500
+
+power-profiles-daemon (0.21-1) unstable; urgency=medium
+
+ * New upstream release
+
+ -- Marco Trevisan (Treviño) Thu, 04 Apr 2024 02:19:26 +0200
+
+power-profiles-daemon (0.20-3) unstable; urgency=medium
+
+ * debian/control: Build-depend on systemd-dev instead of systemd
+ (Closes: #1060563)
+ * debian/patches: Upstream fix on test failing messages
+
+ -- Marco Trevisan (Treviño) Thu, 15 Feb 2024 18:53:27 +0100
+
+power-profiles-daemon (0.20-2) unstable; urgency=medium
+
+ * debian/rules: Only fix generated makefile for old argparse-manpage versions
+ * debian/patches: Restore systemd conflict with tlp.
+ Tlp is just too much, it can do too many things to be sure it's not
+ conflicting with p-p-d, so let's just enable the conflict again.
+ If someone wants TLP, then it should have only that one running.
+ Not to mention that tlp debian package already conflicts with
+ power-profiles-daemon.
+ * debian/control: Add conflict with laptop-mode-tools and tlp.
+ This is already handled by systemd, but makes it clearer that it's
+ better not having both installed at the same time at all.
+ Tlp is already conflicting with us, so enforce this from our side too.
+ * debian/patches: Refresh patch for generating manpage with upstream ones
+ * debian/patches: Cherry-pick upstream post-releases fixes
+ * debian/patches: Get upstream patches to generate bash/zsh-completions
+ * debian/control: Add build dependencies for generating bash-completions
+ * debian/rules: Explicitly enable bash-completions generation
+ * debian/rules: Enable generating and installing zsh completions in debian
+ There's not a default path for zsh completions defined, as it depends on
+ user configuration or distributions, however as per
+ /usr/share/doc/zsh-common/README.Debian in debian we can use
+ /usr/share/zsh/vendor-completions.
+
+ -- Marco Trevisan (Treviño) Thu, 15 Feb 2024 17:13:20 +0100
+
+power-profiles-daemon (0.20-1) unstable; urgency=medium
+
+ * New upstream release:
+ - New default bus name is org.freedesktop.UPower.PowerProfiles
+ - Support multiple drivers (ACPI + amd/intel ones)
+ * debian/watch: Update reference to new upstream repository
+ * debian: Update references to new repository under UPower namespace
+ * debian/copyright: Update copyright to new maintainers
+ * debian/control: Add myself to uploaders
+ * debian/control: Add python dependencies
+ While the daemon does not require python at runtime, the control tool
+ does so ensure debian picks them
+ * debian/patches: Refresh
+ * debian/rules: drop dh_missing override for --fail-missing.
+ It's default for some time now
+ * debian/patches: Add upstream patch to generate powerprofilectl manfile
+ * debian/control: Add manpage generation dependencies
+ * debian/rules: Explicitly enable manpage feature
+ * debian/rules: Fix generated manpage entries
+
+ -- Marco Trevisan (Treviño) Thu, 15 Feb 2024 05:38:30 +0100
+
+power-profiles-daemon (0.13-2) unstable; urgency=medium
+
+ [ Debian Janitor ]
+ * Add debian/upstream/metadata
+ * Avoid explicitly specifying -Wl,--as-needed linker flag
+ * Update standards version to 4.6.2, no changes needed
+
+ [ Jeremy Bícha ]
+ * Release to unstable
+
+ -- Jeremy Bícha Mon, 12 Jun 2023 13:29:15 -0400
+
+power-profiles-daemon (0.13-1) experimental; urgency=medium
+
+ * New upstream version (lp: #2022945)
+ - adds support for the AMD P-State driver that's been added to the
+ 6.3 Linux kernel
+ * debian/patches/build_older_polkit.patch:
+ - removed, it's not needed anymore now that Debian updated to a
+ recent polkit version
+ * debian/power-profiles-daemon.maintscript:
+ - remove obsolete conffile (Closes: #1021746)
+
+ -- Sebastien Bacher Tue, 06 Jun 2023 13:36:10 +0200
+
+power-profiles-daemon (0.12-1) unstable; urgency=medium
+
+ * Team upload
+ * New upstream release
+ * Add debian/docs to install NEWS and README.md
+
+ -- Jeremy Bicha Sun, 14 Aug 2022 10:04:33 -0400
+
+power-profiles-daemon (0.11.1-1) unstable; urgency=medium
+
+ * New upstream version
+ * debian/patches/build_older_polkit.patch:
+ - refresh the patch to also lower the polkit requirement update
+
+ -- Sebastien Bacher Fri, 24 Jun 2022 13:23:07 +0200
+
power-profiles-daemon (0.10.1-3) unstable; urgency=medium
* debian/patches/remove_tlp_conflict.patch:
@@ -51,7 +162,7 @@
power-profiles-daemon (0.1-2) experimental; urgency=medium
- * debian/copyright:
+ * debian/copyright:
- updated, the tests have different copyright and license
-- Sebastien Bacher Fri, 19 Feb 2021 17:01:27 +0100
diff -Nru power-profiles-daemon-0.10.1/debian/control power-profiles-daemon-0.21/debian/control
--- power-profiles-daemon-0.10.1/debian/control 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/control 2024-04-04 03:54:05.000000000 +0000
@@ -2,8 +2,10 @@
Section: admin
Priority: optional
Maintainer: Debian freedesktop.org maintainers
-Uploaders: Sebastien Bacher
+Uploaders: Sebastien Bacher , Marco Trevisan (Treviño)
Build-Depends: debhelper-compat (= 13),
+ dh-python,
+ bash-completion,
libglib2.0-dev,
libgudev-1.0-dev,
libpolkit-gobject-1-dev,
@@ -12,19 +14,22 @@
libumockdev-dev,
libxml2-utils,
meson,
+ python3,
+ python3-argparse-manpage,
python3-dbus ,
python3-dbusmock ,
- python3-gi ,
- systemd,
+ python3-gi,
umockdev ,
-Standards-Version: 4.5.1
+Standards-Version: 4.6.2
Vcs-Browser: https://salsa.debian.org/freedesktop-team/power-profiles-daemon
Vcs-Git: https://salsa.debian.org/freedesktop-team/power-profiles-daemon.git
-Homepage: https://gitlab.freedesktop.org/hadess/power-profiles-daemon
+Homepage: https://gitlab.freedesktop.org/upower/power-profiles-daemon
+X-Python3-Version: >= 3.11
Package: power-profiles-daemon
Architecture: linux-any
-Depends: ${misc:Depends}, ${shlibs:Depends},
+Conflicts: laptop-mode-tools, tlp
+Depends: ${misc:Depends}, ${shlibs:Depends}, ${python3:Depends}, python3-gi
Description: Makes power profiles handling available over D-Bus.
power-profiles-daemon offers to modify system behaviour based upon
user-selected power profiles. There are 3 different power profiles, a
@@ -37,7 +42,3 @@
"actions" can be hooked up to change the behaviour of a particular device.
For example, this can be used to disable the fast-charging for some USB
devices when in power-saver mode.
- .
- Note that power-profiles-daemon does not save the currently active
- profile across system restarts and will always start with the "balanced"
- profile selected.
diff -Nru power-profiles-daemon-0.10.1/debian/copyright power-profiles-daemon-0.21/debian/copyright
--- power-profiles-daemon-0.10.1/debian/copyright 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/copyright 2024-04-04 03:42:32.000000000 +0000
@@ -1,18 +1,26 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: power-profiles-daemon
-Source: https://gitlab.freedesktop.org/hadess/power-profiles-daemon
+Source: https://gitlab.freedesktop.org/upower/power-profiles-daemon
Files: *
Copyright: 2014-2016, 2020 Bastien Nocera
+ 2024 Marco Trevisan
+ 2024 Mario Limonciello
License: GPL-3
Files: debian/*
Copyright: 2021 Sebastien Bacher
License: GPL-3
-Files: tests/integration-test
+Files: tests/integration_test.py
Copyright: 2011 Martin Pitt
2020 Bastien Nocera
+ 2024 Marco Trevisan
+ 2024 Mario Limonciello
+License: GPL-2+
+
+Files: tests/unittest_inspector.py
+Copyright: Marco Trevisan
License: GPL-2+
Files: docs/power-profiles-daemon-docs.xml
diff -Nru power-profiles-daemon-0.10.1/debian/docs power-profiles-daemon-0.21/debian/docs
--- power-profiles-daemon-0.10.1/debian/docs 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/debian/docs 2024-04-04 03:42:32.000000000 +0000
@@ -0,0 +1,2 @@
+NEWS
+README.md
diff -Nru power-profiles-daemon-0.10.1/debian/gbp.conf power-profiles-daemon-0.21/debian/gbp.conf
--- power-profiles-daemon-0.10.1/debian/gbp.conf 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/gbp.conf 2024-04-04 03:42:32.000000000 +0000
@@ -7,6 +7,9 @@
[buildpackage]
sign-tags = True
+[import-orig]
+postimport = dch -v%(version)s New upstream release; git add debian/changelog; debcommit
+
[dch]
multimaint-merge = True
diff -Nru power-profiles-daemon-0.10.1/debian/patches/build_older_polkit.patch power-profiles-daemon-0.21/debian/patches/build_older_polkit.patch
--- power-profiles-daemon-0.10.1/debian/patches/build_older_polkit.patch 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/patches/build_older_polkit.patch 1970-01-01 00:00:00.000000000 +0000
@@ -1,29 +0,0 @@
-Index: power-profiles-daemon/src/power-profiles-daemon.c
-===================================================================
---- power-profiles-daemon.orig/src/power-profiles-daemon.c
-+++ power-profiles-daemon/src/power-profiles-daemon.c
-@@ -103,6 +103,13 @@ typedef enum {
-
- #define PROP_ALL (PROP_ACTIVE_PROFILE | PROP_INHIBITED | PROP_PROFILES | PROP_ACTIONS | PROP_DEGRADED | PROP_ACTIVE_PROFILE_HOLDS)
-
-+/* This uses a weird Auto prefix to avoid conflicts with later added polkit types. */
-+typedef PolkitAuthorizationResult AutoPolkitAuthorizationResult;
-+typedef PolkitSubject AutoPolkitSubject;
-+
-+G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoPolkitAuthorizationResult, g_object_unref)
-+G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoPolkitSubject, g_object_unref)
-+
- static const char *
- get_active_profile (PpdApp *data)
- {
-@@ -603,8 +610,8 @@ check_action_permission (PpdApp
- GError **error)
- {
- g_autoptr(GError) local_error = NULL;
-- g_autoptr(PolkitAuthorizationResult) result = NULL;
-- g_autoptr(PolkitSubject) subject = NULL;
-+ g_autoptr(AutoPolkitAuthorizationResult) result = NULL;
-+ g_autoptr(AutoPolkitSubject) subject = NULL;
-
- subject = polkit_system_bus_name_new (sender);
- result = polkit_authority_check_authorization_sync (data->auth,
diff -Nru power-profiles-daemon-0.10.1/debian/patches/remove_tlp_conflict.patch power-profiles-daemon-0.21/debian/patches/remove_tlp_conflict.patch
--- power-profiles-daemon-0.10.1/debian/patches/remove_tlp_conflict.patch 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/patches/remove_tlp_conflict.patch 1970-01-01 00:00:00.000000000 +0000
@@ -1,18 +0,0 @@
-Description: remove the systemd unit conflict on tlp.service
-we don't want that in Ubuntu since we patch tlp to not do performance modes
-changes when power-profiles-daemon is active but the change can be
-included in Debian also since the tlp maintainer made the packages conflict
-which means we can't end up installed together
-forwarded: not-needed
-Index: power-profiles-daemon/data/power-profiles-daemon.service.in
-===================================================================
---- power-profiles-daemon.orig/data/power-profiles-daemon.service.in
-+++ power-profiles-daemon/data/power-profiles-daemon.service.in
-@@ -1,6 +1,6 @@
- [Unit]
- Description=Power Profiles daemon
--Conflicts=tuned.service tlp.service auto-cpufreq.service system76-power.service
-+Conflicts=tuned.service auto-cpufreq.service system76-power.service
- Before=multi-user.target display-manager.target
-
- [Service]
diff -Nru power-profiles-daemon-0.10.1/debian/patches/series power-profiles-daemon-0.21/debian/patches/series
--- power-profiles-daemon-0.10.1/debian/patches/series 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/patches/series 1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-build_older_polkit.patch
-remove_tlp_conflict.patch
diff -Nru power-profiles-daemon-0.10.1/debian/power-profiles-daemon.maintscript power-profiles-daemon-0.21/debian/power-profiles-daemon.maintscript
--- power-profiles-daemon-0.10.1/debian/power-profiles-daemon.maintscript 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/debian/power-profiles-daemon.maintscript 2024-04-04 03:42:32.000000000 +0000
@@ -0,0 +1 @@
+rm_conffile /etc/dbus-1/system.d/net.hadess.PowerProfiles.conf 0.13-1~
diff -Nru power-profiles-daemon-0.10.1/debian/rules power-profiles-daemon-0.21/debian/rules
--- power-profiles-daemon-0.10.1/debian/rules 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/rules 2024-04-04 03:54:42.000000000 +0000
@@ -1,10 +1,21 @@
#!/usr/bin/make -f
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
-export DEB_LDFLAGS_MAINT_APPEND = -Wl,-z,defs -Wl,--as-needed
+export DEB_LDFLAGS_MAINT_APPEND = -Wl,-z,defs
%:
- dh $@
+ dh $@ --with python3
-override_dh_missing:
- dh_missing --fail-missing
+override_dh_auto_configure:
+ dh_auto_configure -- \
+ -Dmanpage=enabled \
+ -Dbashcomp=disabled \
+ $(NULL)
+
+ # Newer python3-argparse-manpage is in debian but let's wait removing this
+ # until it's there for a while, so that there's not a mismatch.
+ if dpkg --compare-versions \
+ "$$(dpkg-query --showformat='${Version}' --show python3-argparse-manpage)" lt "4.0"; then \
+ sed -i s,argparse-manpage,powerprofilesctl,g \
+ $(CURDIR)/obj-$(DEB_HOST_GNU_TYPE)/src/powerprofilesctl.1; \
+ fi
diff -Nru power-profiles-daemon-0.10.1/debian/upstream/metadata power-profiles-daemon-0.21/debian/upstream/metadata
--- power-profiles-daemon-0.10.1/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/debian/upstream/metadata 2024-04-04 03:42:32.000000000 +0000
@@ -0,0 +1,5 @@
+---
+Bug-Database: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/issues
+Bug-Submit: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/issues/new
+Repository: https://gitlab.freedesktop.org/upower/power-profiles-daemon.git
+Repository-Browse: https://gitlab.freedesktop.org/upower/power-profiles-daemon
diff -Nru power-profiles-daemon-0.10.1/debian/watch power-profiles-daemon-0.21/debian/watch
--- power-profiles-daemon-0.10.1/debian/watch 2022-03-05 17:17:30.000000000 +0000
+++ power-profiles-daemon-0.21/debian/watch 2024-04-04 03:42:32.000000000 +0000
@@ -1,3 +1,3 @@
version=4
-https://gitlab.freedesktop.org/hadess/power-profiles-daemon/tags \
+https://gitlab.freedesktop.org/upower/power-profiles-daemon/tags \
.*/@PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@
diff -Nru power-profiles-daemon-0.10.1/docs/meson.build power-profiles-daemon-0.21/docs/meson.build
--- power-profiles-daemon-0.10.1/docs/meson.build 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/docs/meson.build 2024-04-03 23:55:02.000000000 +0000
@@ -11,8 +11,8 @@
content_files += gnome.gdbus_codegen(
meson.project_name(),
- sources: meson.source_root() / 'src' / 'net.hadess.PowerProfiles.xml',
- interface_prefix: 'net.hadess',
+ sources: dbus_xml['org.freedesktop.UPower.PowerProfiles'],
+ interface_prefix: 'org.freedesktop.UPower',
namespace: 'PowerProfiles',
docbook: 'docs',
build_by_default: true,
@@ -21,12 +21,12 @@
private_headers = [
'power-profiles-daemon.h',
'ppd-action-trickle-charge.h',
- 'ppd-driver-balanced.h',
+ 'ppd-action-amdgpu-panel-power.h',
'ppd-driver-fake.h',
'ppd-driver-intel-pstate.h',
+ 'ppd-driver-amd-pstate.h',
'ppd-driver-placeholder.h',
'ppd-driver-platform-profile.h',
- 'ppd-driver-power-saver.h',
'ppd-utils.h',
'power-profiles-daemon-resources.h',
]
@@ -38,8 +38,8 @@
dependencies: libpower_profiles_daemon_dep,
gobject_typesfile: [join_paths(meson.current_source_dir(), 'power-profiles-daemon.types')],
src_dir: [
- meson.source_root() /'src',
- meson.build_root() / 'src',
+ meson.project_source_root() /'src',
+ meson.project_build_root() / 'src',
],
ignore_headers: private_headers,
)
diff -Nru power-profiles-daemon-0.10.1/docs/power-profiles-daemon-docs.xml power-profiles-daemon-0.21/docs/power-profiles-daemon-docs.xml
--- power-profiles-daemon-0.10.1/docs/power-profiles-daemon-docs.xml 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/docs/power-profiles-daemon-docs.xml 2024-04-03 23:55:02.000000000 +0000
@@ -9,20 +9,46 @@
Version &version;
- Bastien
- Nocera
-
-
- hadess@hadess.net
-
-
+ Bastien
+ Nocera
+
+
+ hadess@hadess.net
+
+
+
+
+ Mario
+ Limonciello
+
+
+ superm1@gmail.com
+
+
+
+ Marco
+ Trevisan
+
+
+ mail@3v1n0.net
+
+
+
2020
Red Hat, Inc.
+
+ 2024
+ Advanced Micro Devices, Inc
+
+
+ 2024
+ Canonical Ltd
+
@@ -63,14 +89,17 @@
Power Profiles daemon.
-
+
Internal API
+
+
+
diff -Nru power-profiles-daemon-0.10.1/docs/power-profiles-daemon-sections.txt power-profiles-daemon-0.21/docs/power-profiles-daemon-sections.txt
--- power-profiles-daemon-0.10.1/docs/power-profiles-daemon-sections.txt 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/docs/power-profiles-daemon-sections.txt 2024-04-03 23:55:02.000000000 +0000
@@ -1,4 +1,11 @@
+
+
+
ppd-action
Profile Actions
PpdActionClass
@@ -8,6 +15,15 @@
+ppd-action-amdgpu-panel-power
+AMDGPU Power Panel Saving Action
+PpdActionAmdgpuPanelPowerClass
+_PpdActionAmdgpuPanelPower
+
+PPD_TYPE_ACTION
+
+
+
ppd-driver
Profile Drivers
PpdDriverClass
@@ -16,6 +32,22 @@
PpdProfileActivationReason
PPD_TYPE_DRIVER
+PPD_TYPE_DRIVER_CPU
+PPD_TYPE_DRIVER_PLATFORM
+
+
+
+ppd-driver-cpu
+CPU Profile Drivers
+PpdDriverCpuClass
+PpdDriverCpu
+
+
+
+ppd-driver-platform
+Platform Profile Drivers
+PpdDriverPlatformClass
+PpdDriverPlatform
diff -Nru power-profiles-daemon-0.10.1/meson.build power-profiles-daemon-0.21/meson.build
--- power-profiles-daemon-0.10.1/meson.build 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/meson.build 2024-04-03 23:55:02.000000000 +0000
@@ -1,12 +1,12 @@
project('power-profiles-daemon', [ 'c' ],
- version: '0.10.1',
+ version: '0.21',
license: 'GPLv3+',
default_options: [
'buildtype=debugoptimized',
'warning_level=1',
'c_std=c99',
],
- meson_version: '>= 0.54.0')
+ meson_version: '>= 0.59.0')
cc = meson.get_compiler('c')
@@ -17,37 +17,113 @@
'-Wstrict-prototypes',
'-Werror-implicit-function-declaration',
'-Wno-pointer-sign',
- '-Wshadow'
+ '-Wshadow',
+ '-Wno-sign-compare',
+ '-Wno-cast-function-type',
+ '-Wno-unused-parameter',
+ '-Wno-missing-field-initializers',
+ '-Wno-type-limits',
])
prefix = get_option('prefix')
libexecdir = prefix / get_option('libexecdir')
bindir = get_option('bindir')
-dbusconfdir = get_option('sysconfdir') / 'dbus-1' / 'system.d'
+dbusconfdir = get_option('datadir') / 'dbus-1' / 'system.d'
dbusservicedir = get_option('datadir') / 'dbus-1' / 'system-services'
systemd_system_unit_dir = get_option('systemdsystemunitdir')
if systemd_system_unit_dir == 'auto'
systemd_dep = dependency('systemd')
- systemd_system_unit_dir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir')
+ systemd_system_unit_dir = systemd_dep.get_variable('systemdsystemunitdir')
endif
+glib_dep = dependency('glib-2.0')
+gio_unix_dep = dependency('gio-unix-2.0')
gio_dep = dependency('gio-2.0')
gudev_dep = dependency('gudev-1.0', version: '>= 234')
upower_dep = dependency('upower-glib')
-polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.91')
-polkit_policy_directory = polkit_gobject_dep.get_pkgconfig_variable('policydir')
+polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.99')
+polkit_policy_directory = polkit_gobject_dep.get_variable('policydir')
+
+python3_required_modules = []
+gi_required_modules = {}
+
+powerprofilesctl_required_gi_modules = {
+ 'GLib': '2.0',
+ 'Gio': '2.0',
+}
gnome = import('gnome')
add_global_arguments('-D_GNU_SOURCE=1', language: 'c')
add_global_arguments(common_cflags, language: 'c')
-if get_option('pylint')
- pylint = find_program('pylint-3', 'pylint3', 'pylint', required: true)
- pylint_flags = ['-d', 'C0116', '-d', 'C0114', '-d', 'W0707']
+pylint = find_program('pylint-3', 'pylint3', 'pylint', required: get_option('pylint'))
+if pylint.found()
+ nomalloc = environment({'MALLOC_PERTURB_': '0'})
+ pylint_flags = ['-d', 'C0116', '-d', 'C0114', '-d', 'W0707', '-d', 'W0706' ]
endif
xmllint = find_program('xmllint', required: false)
+argparse_manpage = find_program('argparse-manpage', required: get_option('manpage'))
+if argparse_manpage.found()
+ gi_required_modules += powerprofilesctl_required_gi_modules
+endif
+
+bus_names = {
+ 'org.freedesktop.UPower.PowerProfiles': '/org/freedesktop/UPower/PowerProfiles',
+ 'net.hadess.PowerProfiles': '/net/hadess/PowerProfiles',
+}
+
+address_sanitizer = get_option('b_sanitize') == 'address' or \
+ get_option('b_sanitize') == 'address,undefined' or \
+ get_option('b_sanitize') == 'leak'
+
+bashcomp = dependency('bash-completion', required: get_option('bashcomp'))
+zshcomp = get_option('zshcomp') != ''
+
+python = import('python')
+python3 = python.find_installation('python3')
+
+if bashcomp.found() or zshcomp
+ python3_required_modules += 'shtab'
+ gi_required_modules += powerprofilesctl_required_gi_modules
+endif
+
+if get_option('tests')
+ python3_required_modules += [
+ 'dbusmock',
+ ]
+ gi_required_modules += powerprofilesctl_required_gi_modules
+ gi_required_modules += {
+ 'UMockdev': '1.0',
+ }
+endif
+
+# Python 3 required modules
+if gi_required_modules.keys().length() > 0
+ python3_required_modules += 'gi'
+endif
+
+foreach p : python3_required_modules
+ # Source: https://docs.python.org/3/library/importlib.html#checking-if-a-module-can-be-imported
+ script = 'import importlib.util; import sys; exit(1) if importlib.util.find_spec(\''+ p +'\') is None else exit(0)'
+ if run_command(python3, '-c', script, check: false).returncode() == 0
+ message('Python3 module \'@0@\' found: YES'.format(p))
+ else
+ error('Python3 module \'@0@\' required but not found'.format(p))
+ endif
+endforeach
+
+foreach module, version : gi_required_modules
+ script = 'import gi; gi.require_version("@0@", "@1@")'.format(module, version)
+ if run_command(python3, '-c', script, check: false).returncode() == 0
+ message('Python3 module \'@0@\' found: YES @1@'.format(module, version))
+ else
+ error('''GObject Introspection module '@0@' version @1@ required but not found'''.format(
+ module, version))
+ endif
+endforeach
+
subdir('src')
subdir('data')
@@ -61,9 +137,20 @@
subdir('docs')
endif
-subdir('tests')
+if get_option('tests')
+ subdir('tests')
+endif
meson.add_dist_script(
- find_program('check-news.sh').path(),
+ find_program('check-news.sh').full_path(),
'@0@'.format(meson.project_version())
)
+
+summary({
+ 'tests': get_option('tests'),
+ 'bash-completion': bashcomp,
+ 'zsh-completion': zshcomp,
+ 'manpages': argparse_manpage.found(),
+ 'python linting': pylint.found(),
+ 'gtk_doc': get_option('gtk_doc'),
+})
diff -Nru power-profiles-daemon-0.10.1/meson_options.txt power-profiles-daemon-0.21/meson_options.txt
--- power-profiles-daemon-0.10.1/meson_options.txt 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/meson_options.txt 2024-04-03 23:55:02.000000000 +0000
@@ -7,6 +7,22 @@
value: false,
description: 'Build docs')
option('pylint',
- type: 'boolean',
- value: false,
+ type: 'feature',
+ value: 'auto',
description: 'Run pylint checks, for developers only')
+option('tests',
+ description: 'Whether to run tests',
+ type: 'boolean',
+ value: true)
+option('manpage',
+ description: 'generate powerprofilesctl man page',
+ type: 'feature',
+ value: 'auto')
+option('bashcomp',
+ description: 'generate bash completion',
+ type: 'feature',
+ value: 'auto')
+option('zshcomp',
+ description: 'path for zsh completion file',
+ type: 'string',
+ value: '')
diff -Nru power-profiles-daemon-0.10.1/src/completions/meson.build power-profiles-daemon-0.21/src/completions/meson.build
--- power-profiles-daemon-0.10.1/src/completions/meson.build 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/completions/meson.build 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,36 @@
+generate_completion = [python3, powerprofilesctl, '--print-completion']
+
+completions_common = {
+ 'env': {'PPD_COMPLETIONS_GENERATION': '1'},
+ 'capture': true,
+ 'install': true,
+}
+
+if bashcomp.found()
+ completions_dir = bashcomp.get_variable(pkgconfig: 'completionsdir',
+ pkgconfig_define: bashcomp.version().version_compare('>= 2.10') ?
+ ['datadir', get_option('datadir')] : ['prefix', prefix],
+ )
+
+ custom_target('bash-completion',
+ output: 'powerprofilesctl',
+ command: [
+ generate_completion,
+ 'bash',
+ ],
+ install_dir: completions_dir,
+ kwargs: completions_common,
+ )
+endif
+
+if zshcomp
+ custom_target('zsh-completion',
+ output: '_powerprofilesctl',
+ command: [
+ generate_completion,
+ 'zsh',
+ ],
+ install_dir: get_option('zshcomp'),
+ kwargs: completions_common,
+ )
+endif
diff -Nru power-profiles-daemon-0.10.1/src/meson.build power-profiles-daemon-0.21/src/meson.build
--- power-profiles-daemon-0.10.1/src/meson.build 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/src/meson.build 2024-04-03 23:55:02.000000000 +0000
@@ -1,17 +1,59 @@
-deps = [ gio_dep, gudev_dep, upower_dep, polkit_gobject_dep ]
+deps = [
+ gio_dep,
+ gio_unix_dep,
+ gudev_dep,
+ polkit_gobject_dep,
+ upower_dep,
+]
config_h = configuration_data()
config_h.set_quoted('VERSION', meson.project_version())
+config_h.set('POLKIT_HAS_AUTOPOINTERS', polkit_gobject_dep.version().version_compare('>= 0.114'))
config_h_files = configure_file(
output: 'config.h',
configuration: config_h
)
+dbus_xml = {}
+dbus_xml_sources = []
+resources_contents = []
+
+foreach name, path: bus_names
+ config = {
+ 'dbus_name': name,
+ 'dbus_iface': name,
+ 'dbus_path': path,
+ }
+
+ xml_source = configure_file(
+ input: 'power-profiles-daemon.dbus.xml.in',
+ output: name + '.xml',
+ configuration: config,
+ )
+ dbus_xml_sources += xml_source
+ dbus_xml += {name: xml_source}
+
+ resources_contents += '@0@.xml'.format(name)
+endforeach
+
+resources_xml = configure_file(
+ input: 'power-profiles-daemon.gresource.xml.in',
+ output: '@BASENAME@',
+ configuration: {
+ 'prefix': bus_names['org.freedesktop.UPower.PowerProfiles'],
+ 'contents': '\n'.join(resources_contents),
+ },
+)
+
resources = gnome.compile_resources(
- 'power-profiles-daemon-resources', 'power-profiles-daemon.gresource.xml',
- c_name: 'power_profiles_daemon',
- source_dir: '.',
- export: true
+ 'power-profiles-daemon-resources', resources_xml,
+ c_name: 'power_profiles_daemon',
+ dependencies: dbus_xml_sources,
+ source_dir: [
+ meson.current_source_dir(),
+ meson.current_build_dir(),
+ ],
+ export: true
)
sources = [
@@ -19,6 +61,8 @@
'ppd-utils.c',
'ppd-action.c',
'ppd-driver.c',
+ 'ppd-driver-cpu.c',
+ 'ppd-driver-platform.c',
resources,
]
@@ -45,7 +89,9 @@
sources += [
'power-profiles-daemon.c',
'ppd-action-trickle-charge.c',
+ 'ppd-action-amdgpu-panel-power.c',
'ppd-driver-intel-pstate.c',
+ 'ppd-driver-amd-pstate.c',
'ppd-driver-platform-profile.c',
'ppd-driver-placeholder.c',
'ppd-driver-fake.c',
@@ -58,23 +104,52 @@
install_dir: libexecdir
)
-python = import('python')
-py_installation = python.find_installation('python3', required: true)
-
-ppd_conf = configuration_data()
-ppd_conf.set('VERSION', meson.project_version())
-ppd_conf.set('PYTHON3', py_installation.path())
-
-script = configure_file(
- input: 'powerprofilesctl.in',
+powerprofilesctl = configure_file(
+ input: files('powerprofilesctl'),
output: 'powerprofilesctl',
- configuration: ppd_conf,
+ configuration: {
+ 'VERSION': meson.project_version(),
+ },
install_dir: get_option('bindir')
)
-if get_option('pylint')
+if pylint.found()
test('pylint-powerprofilesctl',
pylint,
- args: pylint_flags + [ script ],
+ args: pylint_flags + [ powerprofilesctl ],
+ env: nomalloc,
)
endif
+
+if argparse_manpage.found()
+ argparse_features = run_command(argparse_manpage, '--help',
+ check: true).stdout().strip()
+
+ install_man(configure_file(
+ command: [
+ argparse_manpage,
+ '--pyfile', powerprofilesctl,
+ '--function', 'get_parser',
+ argparse_features.contains('--author') ?
+ ['--author', 'Bastien Nocera', '--author', 'Mario Limonciello'] : [],
+ argparse_features.contains('--author-email') ?
+ ['--author-email', 'hadess@hadess.net', '--author-email', 'mario.limonciello@amd.com'] : [],
+ argparse_features.contains('--project-name') ?
+ ['--project-name', meson.project_name()] : [],
+ argparse_features.contains('--version') ?
+ ['--version', meson.project_version()] : [],
+ argparse_features.contains('--url') ?
+ ['--url', 'https://gitlab.freedesktop.org/upower/power-profiles-daemon'] : [],
+ argparse_features.contains('--description') ?
+ ['--manual-title', 'Power Profiles Daemon Control Program'] : [],
+ argparse_features.contains('--description') ?
+ ['--description', 'Command line utility to control Power Profiles Daemon'] : [],
+ argparse_features.contains('--format') ?
+ ['--format', 'single-commands-section'] : [],
+ ],
+ capture: true,
+ output: 'powerprofilesctl.1',
+ ))
+endif
+
+subdir('completions')
\ No newline at end of file
diff -Nru power-profiles-daemon-0.10.1/src/net.hadess.PowerProfiles.xml power-profiles-daemon-0.21/src/net.hadess.PowerProfiles.xml
--- power-profiles-daemon-0.10.1/src/net.hadess.PowerProfiles.xml 2021-10-28 09:40:21.804132700 +0000
+++ power-profiles-daemon-0.21/src/net.hadess.PowerProfiles.xml 1970-01-01 00:00:00.000000000 +0000
@@ -1,140 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff -Nru power-profiles-daemon-0.10.1/src/power-profiles-daemon.c power-profiles-daemon-0.21/src/power-profiles-daemon.c
--- power-profiles-daemon-0.10.1/src/power-profiles-daemon.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/power-profiles-daemon.c 2024-04-03 23:55:02.000000000 +0000
@@ -8,26 +8,63 @@
*
*/
+#define G_LOG_DOMAIN "Core"
+
#include "config.h"
+#include
#include
#include
+#include
+#include
#include "power-profiles-daemon-resources.h"
#include "power-profiles-daemon.h"
-#include "ppd-driver.h"
+#include "ppd-driver-cpu.h"
+#include "ppd-driver-platform.h"
#include "ppd-action.h"
#include "ppd-enums.h"
-#define POWER_PROFILES_DBUS_NAME "net.hadess.PowerProfiles"
-#define POWER_PROFILES_DBUS_PATH "/net/hadess/PowerProfiles"
+#define POWER_PROFILES_DBUS_NAME "org.freedesktop.UPower.PowerProfiles"
+#define POWER_PROFILES_DBUS_PATH "/org/freedesktop/UPower/PowerProfiles"
#define POWER_PROFILES_IFACE_NAME POWER_PROFILES_DBUS_NAME
+#define POWER_PROFILES_LEGACY_DBUS_NAME "net.hadess.PowerProfiles"
+#define POWER_PROFILES_LEGACY_DBUS_PATH "/net/hadess/PowerProfiles"
+#define POWER_PROFILES_LEGACY_IFACE_NAME POWER_PROFILES_LEGACY_DBUS_NAME
+
+#define POWER_PROFILES_POLICY_NAMESPACE "org.freedesktop.UPower.PowerProfiles"
+
+#define POWER_PROFILES_RESOURCES_PATH "/org/freedesktop/UPower/PowerProfiles"
+
+#define UPOWER_DBUS_NAME "org.freedesktop.UPower"
+#define UPOWER_DBUS_PATH "/org/freedesktop/UPower"
+#define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower"
+
+#define LOGIND_DBUS_NAME "org.freedesktop.login1"
+#define LOGIND_DBUS_PATH "/org/freedesktop/login1"
+#define LOGIND_DBUS_INTERFACE "org.freedesktop.login1.Manager"
+
+#ifndef POLKIT_HAS_AUTOPOINTERS
+/* FIXME: Remove this once we're fine to depend on polkit 0.114 */
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref)
+#endif
+
+typedef struct {
+ GOptionGroup *group;
+ GLogLevelFlags log_level;
+ gboolean replace;
+ GStrv blocked_drivers;
+ GStrv blocked_actions;
+} DebugOptions;
+
typedef struct {
GMainLoop *main_loop;
- GDBusNodeInfo *introspection_data;
GDBusConnection *connection;
+ GCancellable *cancellable;
guint name_id;
+ guint legacy_name_id;
gboolean was_started;
int ret;
@@ -39,9 +76,20 @@
PpdProfile active_profile;
PpdProfile selected_profile;
GPtrArray *probed_drivers;
- PpdDriver *driver;
+ PpdDriverCpu *cpu_driver;
+ PpdDriverPlatform *platform_driver;
GPtrArray *actions;
GHashTable *profile_holds;
+
+ GDBusProxy *upower_proxy;
+ gulong upower_watch_id;
+ gulong upower_properties_id;
+ PpdPowerChangedReason power_changed_reason;
+
+ guint logind_sleep_signal_id;
+
+ GStrv blocked_drivers;
+ GStrv blocked_actions;
} PpdApp;
typedef struct {
@@ -49,9 +97,19 @@
char *reason;
char *application_id;
char *requester;
+ char *requester_iface;
} ProfileHold;
static void
+debug_options_free(DebugOptions *options)
+{
+ g_strfreev (options->blocked_drivers);
+ g_strfreev (options->blocked_actions);
+ g_free (options);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (DebugOptions, debug_options_free)
+
+static void
profile_hold_free (ProfileHold *hold)
{
if (hold == NULL)
@@ -59,6 +117,7 @@
g_free (hold->reason);
g_free (hold->application_id);
g_free (hold->requester);
+ g_free (hold->requester_iface);
g_free (hold);
}
@@ -66,15 +125,15 @@
static void stop_profile_drivers (PpdApp *data);
static void start_profile_drivers (PpdApp *data);
-
-#define GET_DRIVER(p) (ppd_driver_get_profiles (data->driver) & p ? data->driver : NULL)
-#define ACTIVE_DRIVER (data->driver)
+static void upower_battery_set_power_changed_reason (PpdApp *, PpdPowerChangedReason);
/* profile drivers and actions */
#include "ppd-action-trickle-charge.h"
+#include "ppd-action-amdgpu-panel-power.h"
#include "ppd-driver-placeholder.h"
#include "ppd-driver-platform-profile.h"
#include "ppd-driver-intel-pstate.h"
+#include "ppd-driver-amd-pstate.h"
#include "ppd-driver-fake.h"
typedef GType (*GTypeGetFunc) (void);
@@ -84,12 +143,14 @@
ppd_driver_fake_get_type,
ppd_driver_platform_profile_get_type,
ppd_driver_intel_pstate_get_type,
+ ppd_driver_amd_pstate_get_type,
/* Generic profile driver */
ppd_driver_placeholder_get_type,
/* Actions */
ppd_action_trickle_charge_get_type,
+ ppd_action_amdgpu_panel_power_get_type,
};
typedef enum {
@@ -98,10 +159,34 @@
PROP_PROFILES = 1 << 2,
PROP_ACTIONS = 1 << 3,
PROP_DEGRADED = 1 << 4,
- PROP_ACTIVE_PROFILE_HOLDS = 1 << 5
+ PROP_ACTIVE_PROFILE_HOLDS = 1 << 5,
+ PROP_VERSION = 1 << 6,
} PropertiesMask;
-#define PROP_ALL (PROP_ACTIVE_PROFILE | PROP_INHIBITED | PROP_PROFILES | PROP_ACTIONS | PROP_DEGRADED | PROP_ACTIVE_PROFILE_HOLDS)
+#define PROP_ALL (PROP_ACTIVE_PROFILE | \
+ PROP_INHIBITED | \
+ PROP_PROFILES | \
+ PROP_ACTIONS | \
+ PROP_DEGRADED | \
+ PROP_ACTIVE_PROFILE_HOLDS | \
+ PROP_VERSION)
+
+static gboolean
+driver_profile_support (PpdDriver *driver,
+ PpdProfile profile)
+{
+ if (!PPD_IS_DRIVER (driver))
+ return FALSE;
+ return (ppd_driver_get_profiles (driver) & profile) != 0;
+}
+
+static gboolean
+get_profile_available (PpdApp *data,
+ PpdProfile profile)
+{
+ return driver_profile_support (PPD_DRIVER (data->cpu_driver), profile) ||
+ driver_profile_support (PPD_DRIVER (data->platform_driver), profile);
+}
static const char *
get_active_profile (PpdApp *data)
@@ -109,18 +194,28 @@
return ppd_profile_to_str (data->active_profile);
}
-static const char *
+static char *
get_performance_degraded (PpdApp *data)
{
- const char *ret;
- PpdDriver *driver;
+ const gchar *cpu_degraded = NULL;
+ const gchar *platform_degraded = NULL;
+
+ if (driver_profile_support (PPD_DRIVER (data->platform_driver), PPD_PROFILE_PERFORMANCE))
+ platform_degraded = ppd_driver_get_performance_degraded (PPD_DRIVER (data->platform_driver));
+
+ if (driver_profile_support (PPD_DRIVER (data->cpu_driver), PPD_PROFILE_PERFORMANCE))
+ cpu_degraded = ppd_driver_get_performance_degraded (PPD_DRIVER (data->cpu_driver));
+
+ if (!cpu_degraded && !platform_degraded)
+ return g_strdup ("");
- driver = GET_DRIVER(PPD_PROFILE_PERFORMANCE);
- if (!driver)
- return "";
- ret = ppd_driver_get_performance_degraded (driver);
- g_assert (ret != NULL);
- return ret;
+ if (!cpu_degraded)
+ return g_strdup (platform_degraded);
+
+ if (!platform_degraded)
+ return g_strdup (cpu_degraded);
+
+ return g_strjoin (",", cpu_degraded, platform_degraded, NULL);
}
static GVariant *
@@ -132,17 +227,40 @@
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
for (i = 0; i < NUM_PROFILES; i++) {
- PpdDriver *driver = GET_DRIVER(1 << i);
+ PpdDriver *platform_driver = PPD_DRIVER (data->platform_driver);
+ PpdDriver *cpu_driver = PPD_DRIVER (data->cpu_driver);
+ PpdProfile profile = 1 << i;
GVariantBuilder asv_builder;
+ gboolean cpu, platform;
+ const gchar *driver = NULL;
- if (driver == NULL)
+ /* check if any of the drivers support */
+ if (!get_profile_available (data, profile))
continue;
g_variant_builder_init (&asv_builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&asv_builder, "{sv}", "Profile",
- g_variant_new_string (ppd_profile_to_str (1 << i)));
- g_variant_builder_add (&asv_builder, "{sv}", "Driver",
- g_variant_new_string (ppd_driver_get_driver_name (driver)));
+ g_variant_new_string (ppd_profile_to_str (profile)));
+ cpu = driver_profile_support (cpu_driver, profile);
+ platform = driver_profile_support (platform_driver, profile);
+ if (cpu)
+ g_variant_builder_add (&asv_builder, "{sv}", "CpuDriver",
+ g_variant_new_string (ppd_driver_get_driver_name (cpu_driver)));
+ if (platform)
+ g_variant_builder_add (&asv_builder, "{sv}", "PlatformDriver",
+ g_variant_new_string (ppd_driver_get_driver_name (platform_driver)));
+
+ /* compatibility with older API */
+ if (cpu && platform)
+ driver = "multiple";
+ else if (cpu)
+ driver = ppd_driver_get_driver_name (cpu_driver);
+ else if (platform)
+ driver = ppd_driver_get_driver_name (platform_driver);
+
+ if (driver)
+ g_variant_builder_add (&asv_builder, "{sv}", "Driver",
+ g_variant_new_string (driver));
g_variant_builder_add (&builder, "a{sv}", &asv_builder);
}
@@ -195,18 +313,20 @@
}
static void
-send_dbus_event (PpdApp *data,
- PropertiesMask mask)
+send_dbus_event_iface (PpdApp *data,
+ PropertiesMask mask,
+ const gchar *iface,
+ const gchar *path)
{
GVariantBuilder props_builder;
GVariant *props_changed = NULL;
- g_assert (data->connection);
+ g_return_if_fail (data->connection);
if (mask == 0)
return;
- g_assert ((mask & PROP_ALL) != 0);
+ g_return_if_fail ((mask & PROP_ALL) != 0);
g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
@@ -219,8 +339,9 @@
g_variant_new_string (""));
}
if (mask & PROP_DEGRADED) {
+ gchar *degraded = get_performance_degraded (data);
g_variant_builder_add (&props_builder, "{sv}", "PerformanceDegraded",
- g_variant_new_string (get_performance_degraded (data)));
+ g_variant_new_take_string (g_steal_pointer (°raded)));
}
if (mask & PROP_PROFILES) {
g_variant_builder_add (&props_builder, "{sv}", "Profiles",
@@ -234,26 +355,53 @@
g_variant_builder_add (&props_builder, "{sv}", "ActiveProfileHolds",
get_profile_holds_variant (data));
}
+ if (mask & PROP_VERSION) {
+ g_variant_builder_add (&props_builder, "{sv}", "Version",
+ g_variant_new_string (VERSION));
+ }
- props_changed = g_variant_new ("(s@a{sv}@as)", POWER_PROFILES_IFACE_NAME,
+ props_changed = g_variant_new ("(s@a{sv}@as)", iface,
g_variant_builder_end (&props_builder),
g_variant_new_strv (NULL, 0));
g_dbus_connection_emit_signal (data->connection,
NULL,
- POWER_PROFILES_DBUS_PATH,
+ path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
props_changed, NULL);
}
static void
+send_dbus_event (PpdApp *data,
+ PropertiesMask mask)
+{
+ send_dbus_event_iface (data, mask,
+ POWER_PROFILES_IFACE_NAME,
+ POWER_PROFILES_DBUS_PATH);
+ send_dbus_event_iface (data, mask,
+ POWER_PROFILES_LEGACY_IFACE_NAME,
+ POWER_PROFILES_LEGACY_DBUS_PATH);
+}
+
+static void
save_configuration (PpdApp *data)
{
g_autoptr(GError) error = NULL;
- g_key_file_set_string (data->config, "State", "Driver", ppd_driver_get_driver_name (data->driver));
- g_key_file_set_string (data->config, "State", "Profile", ppd_profile_to_str (data->active_profile));
+ if (PPD_IS_DRIVER_CPU (data->cpu_driver)) {
+ g_key_file_set_string (data->config, "State", "CpuDriver",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)));
+ }
+
+ if (PPD_IS_DRIVER_PLATFORM (data->platform_driver)) {
+ g_key_file_set_string (data->config, "State", "PlatformDriver",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)));
+ }
+
+ g_key_file_set_string (data->config, "State", "Profile",
+ ppd_profile_to_str (data->active_profile));
+
if (!g_key_file_save_to_file (data->config, data->config_path, &error))
g_warning ("Could not save configuration file '%s': %s", data->config_path, error->message);
}
@@ -261,16 +409,26 @@
static gboolean
apply_configuration (PpdApp *data)
{
- g_autofree char *driver = NULL;
+ g_autofree char *platform_driver = NULL;
g_autofree char *profile_str = NULL;
+ g_autofree char *cpu_driver = NULL;
PpdProfile profile;
- driver = g_key_file_get_string (data->config, "State", "Driver", NULL);
- if (g_strcmp0 (ppd_driver_get_driver_name (data->driver), driver) != 0)
+ cpu_driver = g_key_file_get_string (data->config, "State", "CpuDriver", NULL);
+ if (PPD_IS_DRIVER_CPU (data->cpu_driver) &&
+ g_strcmp0 (ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), cpu_driver) != 0)
+ return FALSE;
+
+ platform_driver = g_key_file_get_string (data->config, "State", "PlatformDriver", NULL);
+
+ if (PPD_IS_DRIVER_PLATFORM (data->platform_driver) &&
+ g_strcmp0 (ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)), platform_driver) != 0)
return FALSE;
+
profile_str = g_key_file_get_string (data->config, "State", "Profile", NULL);
if (profile_str == NULL)
return FALSE;
+
profile = ppd_profile_from_str (profile_str);
if (profile == PPD_PROFILE_UNSET) {
g_debug ("Resetting invalid configuration profile '%s'", profile_str);
@@ -308,12 +466,10 @@
for (i = 0; i < actions->len; i++) {
g_autoptr(GError) error = NULL;
PpdAction *action;
- gboolean ret;
action = g_ptr_array_index (actions, i);
- ret = ppd_action_activate_profile (action, profile, &error);
- if (!ret)
+ if (!ppd_action_activate_profile (action, profile, &error))
g_warning ("Failed to activate action '%s' to profile %s: %s",
ppd_profile_to_str (profile),
ppd_action_get_action_name (action),
@@ -321,22 +477,53 @@
}
}
-static void
-activate_target_profile (PpdApp *data,
- PpdProfile target_profile,
- PpdProfileActivationReason reason)
+static gboolean
+activate_target_profile (PpdApp *data,
+ PpdProfile target_profile,
+ PpdProfileActivationReason reason,
+ GError **error)
{
- g_autoptr(GError) error = NULL;
+ PpdProfile current_profile = data->active_profile;
- g_debug ("Setting active profile '%s' for reason '%s' (current: '%s')",
+ g_info ("Setting active profile '%s' for reason '%s' (current: '%s')",
ppd_profile_to_str (target_profile),
ppd_profile_activation_reason_to_str (reason),
- ppd_profile_to_str (data->active_profile));
+ ppd_profile_to_str (current_profile));
+
+ /* Try CPU first */
+ if (driver_profile_support (PPD_DRIVER (data->cpu_driver), target_profile) &&
+ !ppd_driver_activate_profile (PPD_DRIVER (data->cpu_driver),
+ target_profile, reason, error)) {
+ g_prefix_error (error, "Failed to activate CPU driver '%s': ",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)));
+ return FALSE;
+ }
+
+ /* Then try platform */
+ if (driver_profile_support (PPD_DRIVER (data->platform_driver), target_profile) &&
+ !ppd_driver_activate_profile (PPD_DRIVER (data->platform_driver),
+ target_profile, reason, error)) {
+ g_autoptr(GError) recovery_error = NULL;
+
+ g_prefix_error (error, "Failed to activate platform driver '%s': ",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)));
+
+ if (!PPD_IS_DRIVER (data->cpu_driver))
+ return FALSE;
- if (!ppd_driver_activate_profile (data->driver, target_profile, reason, &error)) {
- g_warning ("Failed to activate driver '%s': %s",
- ppd_driver_get_driver_name (data->driver),
- error->message);
+ g_debug ("Reverting CPU driver '%s' to profile '%s'",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)),
+ ppd_profile_to_str (current_profile));
+
+ if (!ppd_driver_activate_profile (PPD_DRIVER (data->cpu_driver),
+ current_profile, PPD_PROFILE_ACTIVATION_REASON_INTERNAL,
+ &recovery_error)) {
+ g_warning ("Failed to revert CPU driver '%s': %s",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)),
+ recovery_error->message);
+ }
+
+ return FALSE;
}
actions_activate_profile (data->actions, target_profile);
@@ -346,6 +533,23 @@
if (reason == PPD_PROFILE_ACTIVATION_REASON_USER ||
reason == PPD_PROFILE_ACTIVATION_REASON_INTERNAL)
save_configuration (data);
+
+ return TRUE;
+}
+
+static void
+release_hold_notify (PpdApp *data,
+ ProfileHold *hold,
+ guint cookie)
+{
+ const char *req_path = POWER_PROFILES_DBUS_PATH;
+
+ if (g_strcmp0 (hold->requester_iface, POWER_PROFILES_LEGACY_IFACE_NAME) == 0)
+ req_path = POWER_PROFILES_LEGACY_DBUS_PATH;
+
+ g_dbus_connection_emit_signal (data->connection, hold->requester, req_path,
+ hold->requester_iface, "ProfileReleased",
+ g_variant_new ("(u)", cookie), NULL);
}
static void
@@ -359,9 +563,7 @@
ProfileHold *hold = value;
guint cookie = GPOINTER_TO_UINT (key);
- g_dbus_connection_emit_signal (data->connection, hold->requester, POWER_PROFILES_DBUS_PATH,
- POWER_PROFILES_IFACE_NAME, "ProfileReleased",
- g_variant_new ("(u)", cookie), NULL);
+ release_hold_notify (data, hold, cookie);
g_bus_unwatch_name (cookie);
}
g_hash_table_remove_all (data->profile_holds);
@@ -381,6 +583,11 @@
"Invalid profile name '%s'", profile);
return FALSE;
}
+ if (!get_profile_available (data, target_profile)) {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "Cannot switch to unavailable profile '%s'", profile);
+ return FALSE;
+ }
if (target_profile == data->active_profile)
return TRUE;
@@ -394,7 +601,8 @@
mask |= PROP_ACTIVE_PROFILE_HOLDS;
}
- activate_target_profile (data, target_profile, PPD_PROFILE_ACTIVATION_REASON_USER);
+ if (!activate_target_profile (data, target_profile, PPD_PROFILE_ACTIVATION_REASON_USER, error))
+ return FALSE;
data->selected_profile = target_profile;
send_dbus_event (data, mask);
@@ -459,7 +667,7 @@
if (new_profile == data->active_profile)
return;
- activate_target_profile (data, new_profile, PPD_PROFILE_ACTIVATION_REASON_INTERNAL);
+ activate_target_profile (data, new_profile, PPD_PROFILE_ACTIVATION_REASON_INTERNAL, NULL);
send_dbus_event (data, PROP_ACTIVE_PROFILE);
}
@@ -473,25 +681,26 @@
hold = g_hash_table_lookup (data->profile_holds, GUINT_TO_POINTER (cookie));
if (!hold) {
- g_debug("No hold with cookie %d", cookie);
+ g_debug ("No hold with cookie %d", cookie);
return;
}
g_bus_unwatch_name (cookie);
hold_profile = hold->profile;
+ release_hold_notify (data, hold, cookie);
g_hash_table_remove (data->profile_holds, GUINT_TO_POINTER (cookie));
if (g_hash_table_size (data->profile_holds) == 0 &&
hold_profile != data->selected_profile) {
g_debug ("No profile holds anymore going back to last manually activated profile");
- activate_target_profile (data, data->selected_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD);
+ activate_target_profile (data, data->selected_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD, NULL);
mask |= PROP_ACTIVE_PROFILE;
} else if (hold_profile == data->active_profile) {
next_profile = effective_hold_profile (data);
if (next_profile != PPD_PROFILE_UNSET &&
next_profile != data->active_profile) {
g_debug ("Next profile is %s", ppd_profile_to_str (next_profile));
- activate_target_profile (data, next_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD);
+ activate_target_profile (data, next_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD, NULL);
mask |= PROP_ACTIVE_PROFILE;
}
}
@@ -548,8 +757,14 @@
profile = ppd_profile_from_str (profile_name);
if (profile != PPD_PROFILE_PERFORMANCE &&
profile != PPD_PROFILE_POWER_SAVER) {
+ g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+ "Only profiles 'performance' and 'power-saver' can be a hold profile");
+ return;
+ }
+ if (!get_profile_available (data, profile)) {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
- "Only profiles 'performance' and 'power-saver' can be a hold profile");
+ "Cannot hold profile '%s' as it is not available",
+ profile_name);
return;
}
@@ -558,8 +773,9 @@
hold->reason = g_strdup (reason);
hold->application_id = g_strdup (application_id);
hold->requester = g_strdup (g_dbus_method_invocation_get_sender (invocation));
+ hold->requester_iface = g_strdup (g_dbus_method_invocation_get_interface_name (invocation));
- g_debug ("%s(%s) requesting to hold profile '%s', reason: '%s'", application_id,
+ g_debug ("%s (%s) requesting to hold profile '%s', reason: '%s'", application_id,
hold->requester, profile_name, reason);
watch_id = g_bus_watch_name_on_connection (data->connection, hold->requester,
G_BUS_NAME_WATCHER_FLAGS_NONE, NULL,
@@ -572,7 +788,7 @@
PpdProfile target_profile = effective_hold_profile (data);
if (target_profile != PPD_PROFILE_UNSET &&
target_profile != data->active_profile) {
- activate_target_profile (data, target_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD);
+ activate_target_profile (data, target_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD, NULL);
mask |= PROP_ACTIVE_PROFILE;
}
}
@@ -637,7 +853,7 @@
{
PpdApp *data = user_data;
- g_assert (data->connection);
+ g_return_val_if_fail (data->connection, NULL);
if (g_strcmp0 (property_name, "ActiveProfile") == 0)
return g_variant_new_string (get_active_profile (data));
@@ -647,10 +863,14 @@
return get_profiles_variant (data);
if (g_strcmp0 (property_name, "Actions") == 0)
return get_actions_variant (data);
- if (g_strcmp0 (property_name, "PerformanceDegraded") == 0)
- return g_variant_new_string (get_performance_degraded (data));
+ if (g_strcmp0 (property_name, "PerformanceDegraded") == 0) {
+ gchar *degraded = get_performance_degraded (data);
+ return g_variant_new_take_string (g_steal_pointer (°raded));
+ }
if (g_strcmp0 (property_name, "ActiveProfileHolds") == 0)
return get_profile_holds_variant (data);
+ if (g_strcmp0 (property_name, "Version") == 0)
+ return g_variant_new_string (VERSION);
return NULL;
}
@@ -667,14 +887,16 @@
PpdApp *data = user_data;
const char *profile;
- g_assert (data->connection);
+ g_return_val_if_fail (data->connection, FALSE);
if (g_strcmp0 (property_name, "ActiveProfile") != 0) {
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
"No such property: %s", property_name);
return FALSE;
}
- if (!check_action_permission (data, sender, "net.hadess.PowerProfiles.switch-profile", error))
+ if (!check_action_permission (data, sender,
+ POWER_PROFILES_POLICY_NAMESPACE ".switch-profile",
+ error))
return FALSE;
g_variant_get (value, "&s", &profile);
@@ -692,9 +914,10 @@
gpointer user_data)
{
PpdApp *data = user_data;
- g_assert (data->connection);
+ g_return_if_fail (data->connection);
- if (g_strcmp0 (interface_name, POWER_PROFILES_IFACE_NAME) != 0) {
+ if (!g_str_equal (interface_name, POWER_PROFILES_IFACE_NAME) &&
+ !g_str_equal (interface_name, POWER_PROFILES_LEGACY_IFACE_NAME)) {
g_dbus_method_invocation_return_error (invocation,G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_INTERFACE,
"Unknown interface %s", interface_name);
return;
@@ -704,7 +927,7 @@
g_autoptr(GError) local_error = NULL;
if (!check_action_permission (data,
g_dbus_method_invocation_get_sender (invocation),
- "net.hadess.PowerProfiles.hold-profile",
+ POWER_PROFILES_POLICY_NAMESPACE ".hold-profile",
&local_error)) {
g_dbus_method_invocation_return_gerror (invocation, local_error);
return;
@@ -727,16 +950,42 @@
handle_set_property
};
+typedef struct {
+ PpdApp *app;
+ GBusNameOwnerFlags flags;
+ GDBusInterfaceInfo *interface;
+ GDBusInterfaceInfo *legacy_interface;
+} PpdBusOwnData;
+
+static void
+ppd_bus_own_data_free (PpdBusOwnData *data)
+{
+ g_clear_pointer (&data->interface, g_dbus_interface_info_unref);
+ g_clear_pointer (&data->legacy_interface, g_dbus_interface_info_unref);
+ g_free (data);
+}
+
static void
name_lost_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- PpdApp *data = user_data;
- g_debug ("power-profiles-daemon is already running, or it cannot own its D-Bus name. Verify installation.");
- if (!data->was_started)
- data->ret = 1;
- g_main_loop_quit (data->main_loop);
+ PpdBusOwnData *data = user_data;
+ PpdApp *app = data->app;
+
+ g_warning ("power-profiles-daemon is already running, or it cannot own its D-Bus name. Verify installation.");
+ if (!app->was_started)
+ app->ret = EXIT_FAILURE;
+
+ g_main_loop_quit (app->main_loop);
+}
+
+static void
+legacy_name_acquired_handler (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ g_debug ("Name '%s' acquired", name);
}
static void
@@ -744,72 +993,362 @@
const gchar *name,
gpointer user_data)
{
- PpdApp *data = user_data;
+ PpdBusOwnData *data = user_data;
g_dbus_connection_register_object (connection,
POWER_PROFILES_DBUS_PATH,
- data->introspection_data->interfaces[0],
+ data->interface,
+ &interface_vtable,
+ data->app,
+ NULL,
+ NULL);
+
+ g_dbus_connection_register_object (connection,
+ POWER_PROFILES_LEGACY_DBUS_PATH,
+ data->legacy_interface,
&interface_vtable,
- data,
+ data->app,
NULL,
NULL);
- data->connection = g_object_ref (connection);
+ data->app->legacy_name_id = g_bus_own_name_on_connection (connection,
+ POWER_PROFILES_LEGACY_DBUS_NAME,
+ data->flags,
+ legacy_name_acquired_handler,
+ name_lost_handler,
+ data,
+ NULL);
+
+ data->app->connection = g_object_ref (connection);
+}
+
+static void
+upower_battery_update_state_from_value (PpdApp *data,
+ GVariant *battery_val)
+{
+ PpdPowerChangedReason reason;
+
+ if (!battery_val)
+ reason = PPD_POWER_CHANGED_REASON_UNKNOWN;
+ else if (g_variant_get_boolean (battery_val))
+ reason = PPD_POWER_CHANGED_REASON_BATTERY;
+ else
+ reason = PPD_POWER_CHANGED_REASON_AC;
+
+ upower_battery_set_power_changed_reason (data, reason);
+}
+
+static void
+upower_battery_update_state (PpdApp *data)
+{
+ g_autoptr(GVariant) battery_val = NULL;
+
+ battery_val = g_dbus_proxy_get_cached_property (data->upower_proxy, "OnBattery");
+ upower_battery_update_state_from_value (data, battery_val);
+}
+
+static void
+upower_properties_changed (GDBusProxy *proxy,
+ GVariant *changed_properties,
+ GStrv invalidated_properties,
+ PpdApp *data)
+{
+ g_auto(GVariantDict) props_dict = G_VARIANT_DICT_INIT (changed_properties);
+ g_autoptr(GVariant) battery_val = NULL;
+
+ battery_val = g_variant_dict_lookup_value (&props_dict, "OnBattery",
+ G_VARIANT_TYPE_BOOLEAN);
+
+ if (battery_val)
+ upower_battery_update_state_from_value (data, battery_val);
+}
+
+static void
+upower_battery_set_power_changed_reason (PpdApp *data,
+ PpdPowerChangedReason reason)
+{
+ if (data->power_changed_reason == reason)
+ return;
+
+ data->power_changed_reason = reason;
+ g_info ("Power Changed because of reason %s",
+ ppd_power_changed_reason_to_str (reason));
+
+ for (guint i = 0; i < data->actions->len; i++) {
+ g_autoptr(GError) error = NULL;
+ PpdAction *action;
+
+ action = g_ptr_array_index (data->actions, i);
+
+ if (!ppd_action_power_changed (action, reason, &error)) {
+ g_warning ("failed to update action %s: %s",
+ ppd_action_get_action_name (action),
+ error->message);
+ g_clear_error (&error);
+ continue;
+ }
+ }
+
+ if (PPD_IS_DRIVER_CPU (data->cpu_driver)) {
+ g_autoptr(GError) error = NULL;
+
+ if (!ppd_driver_power_changed (PPD_DRIVER (data->cpu_driver), reason, &error)) {
+ g_warning ("failed to update driver %s: %s",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)),
+ error->message);
+ }
+ }
+
+ if (PPD_IS_DRIVER_PLATFORM (data->platform_driver)) {
+ g_autoptr(GError) error = NULL;
+
+ if (!ppd_driver_power_changed (PPD_DRIVER (data->platform_driver), reason, &error)) {
+ g_warning ("failed to update driver %s: %s",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)),
+ error->message);
+ }
+ }
+}
+
+static void
+upower_name_owner_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ PpdApp *data = user_data;
+ GDBusProxy *upower_proxy = G_DBUS_PROXY (object);
+ g_autofree char *name_owner = NULL;
+
+ name_owner = g_dbus_proxy_get_name_owner (upower_proxy);
+
+ if (name_owner != NULL) {
+ g_debug ("%s appeared", UPOWER_DBUS_NAME);
+ upower_battery_update_state (data);
+ return;
+ }
+
+ g_debug ("%s vanished", UPOWER_DBUS_NAME);
+ upower_battery_set_power_changed_reason (data, PPD_POWER_CHANGED_REASON_UNKNOWN);
+}
+
+static void
+on_upower_proxy_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ PpdApp *data = user_data;
+ g_autoptr(GDBusProxy) upower_proxy = NULL;
+ g_autoptr(GError) error = NULL;
+
+ upower_proxy = g_dbus_proxy_new_finish (res, &error);
+
+ if (upower_proxy == NULL) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ g_warning ("failed to connect to upower: %s", error->message);
+ return;
+ }
+
+ g_return_if_fail (data->upower_proxy == NULL);
+ data->upower_proxy = g_steal_pointer (&upower_proxy);
+
+ data->upower_properties_id = g_signal_connect (data->upower_proxy,
+ "g-properties-changed",
+ G_CALLBACK (upower_properties_changed),
+ data);
+
+ data->upower_watch_id = g_signal_connect (data->upower_proxy,
+ "notify::g-name-owner",
+ G_CALLBACK (upower_name_owner_changed),
+ data);
+
+ upower_battery_update_state (data);
+}
+
+static void
+on_logind_prepare_for_sleep_cb (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ PpdApp *data = user_data;
+ gboolean start;
+
+ g_variant_get (parameters, "(b)", &start);
+
+ if (start)
+ g_debug ("System preparing for suspend");
+ else
+ g_debug ("System woke up from suspend");
+
+ if (PPD_IS_DRIVER_CPU (data->cpu_driver)) {
+ g_autoptr(GError) error = NULL;
+
+ if (!ppd_driver_prepare_to_sleep (PPD_DRIVER (data->cpu_driver), start, &error)) {
+ g_warning ("failed to notify driver %s: %s",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)),
+ error->message);
+ }
+ }
+
+ if (PPD_IS_DRIVER_PLATFORM (data->platform_driver)) {
+ g_autoptr(GError) error = NULL;
+
+ if (!ppd_driver_prepare_to_sleep (PPD_DRIVER (data->platform_driver), start, &error)) {
+ g_warning ("failed to notify driver %s: %s",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)),
+ error->message);
+ }
+ }
}
static gboolean
has_required_drivers (PpdApp *data)
{
- PpdDriver *driver;
-
- driver = GET_DRIVER (PPD_PROFILE_BALANCED);
- if (!driver || !G_IS_OBJECT (driver))
+ if (!PPD_IS_DRIVER_CPU (data->cpu_driver) &&
+ !PPD_IS_DRIVER_PLATFORM (data->platform_driver))
return FALSE;
- driver = GET_DRIVER (PPD_PROFILE_POWER_SAVER);
- if (!driver || !G_IS_OBJECT (driver))
+
+ if (!get_profile_available (data, PPD_PROFILE_BALANCED | PPD_PROFILE_POWER_SAVER))
return FALSE;
return TRUE;
}
static void
+restart_profile_drivers (PpdApp *data)
+{
+ stop_profile_drivers (data);
+ start_profile_drivers (data);
+}
+
+static void
driver_probe_request_cb (PpdDriver *driver,
gpointer user_data)
{
PpdApp *data = user_data;
- stop_profile_drivers (data);
- start_profile_drivers (data);
+ restart_profile_drivers (data);
+}
+
+static void
+disconnect_array_objects_signals_by_data (GPtrArray *array,
+ void *data)
+{
+ for (guint i = 0; i < array->len; ++i) {
+ GObject *object = g_ptr_array_index (array, i);
+
+ g_signal_handlers_disconnect_by_data (object, data);
+ }
+}
+
+static void
+maybe_disconnect_object_by_data (void *object,
+ void *data)
+{
+ if (!G_IS_OBJECT (object))
+ return;
+
+ g_signal_handlers_disconnect_by_data (object, data);
}
static void
stop_profile_drivers (PpdApp *data)
{
+ if (data->logind_sleep_signal_id) {
+ g_dbus_connection_signal_unsubscribe (data->connection, data->logind_sleep_signal_id);
+ data->logind_sleep_signal_id = 0;
+ }
+
+ upower_battery_set_power_changed_reason (data, PPD_POWER_CHANGED_REASON_UNKNOWN);
release_all_profile_holds (data);
+ g_cancellable_cancel (data->cancellable);
+ disconnect_array_objects_signals_by_data (data->probed_drivers, data);
g_ptr_array_set_size (data->probed_drivers, 0);
+ disconnect_array_objects_signals_by_data (data->actions, data);
g_ptr_array_set_size (data->actions, 0);
- g_clear_object (&data->driver);
+ g_clear_signal_handler (&data->upower_watch_id, data->upower_proxy);
+ g_clear_signal_handler (&data->upower_properties_id, data->upower_proxy);
+ g_clear_object (&data->cancellable);
+ maybe_disconnect_object_by_data (data->upower_proxy, data);
+ g_clear_object (&data->upower_proxy);
+ maybe_disconnect_object_by_data (data->cpu_driver, data);
+ g_clear_object (&data->cpu_driver);
+ maybe_disconnect_object_by_data (data->platform_driver, data);
+ g_clear_object (&data->platform_driver);
+}
+
+static gboolean
+action_blocked (PpdApp *app, PpdAction *action)
+{
+ const gchar *action_name = ppd_action_get_action_name (action);
+ gboolean blocked;
+
+ if (app->blocked_actions == NULL || g_strv_length (app->blocked_actions) == 0)
+ return FALSE;
+
+ blocked = g_strv_contains ((const gchar *const *) app->blocked_actions, action_name);
+
+ if (blocked)
+ g_debug ("Action '%s' is blocked", action_name);
+ return blocked;
+}
+
+static gboolean
+driver_blocked (PpdApp *app, PpdDriver *driver)
+{
+ const gchar *driver_name = ppd_driver_get_driver_name (driver);
+ gboolean blocked;
+
+ if (app->blocked_drivers == NULL || g_strv_length (app->blocked_drivers) == 0)
+ return FALSE;
+
+ blocked = g_strv_contains ((const gchar *const *) app->blocked_drivers, driver_name);
+ if (blocked)
+ g_debug ("Driver '%s' is blocked", driver_name);
+ return blocked;
}
static void
start_profile_drivers (PpdApp *data)
{
guint i;
+ g_autoptr(GError) initial_error = NULL;
+ gboolean needs_battery_monitor = FALSE;
+ gboolean needs_suspend_monitor = FALSE;
+
+ data->cancellable = g_cancellable_new ();
for (i = 0; i < G_N_ELEMENTS (objects); i++) {
- GObject *object;
+ g_autoptr(GObject) object = NULL;
+
+ object = g_object_new (objects[i] (), NULL);
- object = g_object_new (objects[i](), NULL);
if (PPD_IS_DRIVER (object)) {
- PpdDriver *driver = PPD_DRIVER (object);
+ g_autoptr(PpdDriver) driver = PPD_DRIVER (g_steal_pointer (&object));
PpdProfile profiles;
PpdProbeResult result;
g_debug ("Handling driver '%s'", ppd_driver_get_driver_name (driver));
+ if (driver_blocked (data, driver)) {
+ g_debug ("Driver '%s' is blocked, skipping", ppd_driver_get_driver_name (driver));
+ continue;
+ }
+
+ if (PPD_IS_DRIVER_CPU (data->cpu_driver) && PPD_IS_DRIVER_CPU (driver)) {
+ g_debug ("CPU driver '%s' already probed, skipping driver '%s'",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)),
+ ppd_driver_get_driver_name (driver));
+ continue;
+ }
- if (data->driver != NULL) {
- g_debug ("Driver '%s' already probed, skipping driver '%s'",
- ppd_driver_get_driver_name (data->driver),
+ if (PPD_IS_DRIVER_PLATFORM (data->platform_driver) && PPD_IS_DRIVER_PLATFORM (driver)) {
+ g_debug ("Platform driver '%s' already probed, skipping driver '%s'",
+ ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)),
ppd_driver_get_driver_name (driver));
continue;
}
@@ -819,111 +1358,191 @@
g_warning ("Profile Driver '%s' implements invalid profiles '0x%X'",
ppd_driver_get_driver_name (driver),
profiles);
- g_object_unref (object);
continue;
}
result = ppd_driver_probe (driver);
if (result == PPD_PROBE_RESULT_FAIL) {
- g_debug ("probe() failed for driver %s, skipping",
+ g_debug ("probe () failed for driver %s, skipping",
ppd_driver_get_driver_name (driver));
- g_object_unref (object);
continue;
- } else if (result == PPD_PROBE_RESULT_DEFER) {
+ }
+
+ if (result == PPD_PROBE_RESULT_DEFER) {
g_signal_connect (G_OBJECT (driver), "probe-request",
G_CALLBACK (driver_probe_request_cb), data);
- g_ptr_array_add (data->probed_drivers, driver);
+ g_ptr_array_add (data->probed_drivers, g_steal_pointer (&driver));
continue;
}
- data->driver = driver;
+ if (PPD_IS_DRIVER_CPU (driver))
+ g_set_object (&data->cpu_driver, PPD_DRIVER_CPU (driver));
+ else if (PPD_IS_DRIVER_PLATFORM (driver))
+ g_set_object (&data->platform_driver, PPD_DRIVER_PLATFORM (driver));
+ else
+ g_return_if_reached ();
+
+ if (PPD_DRIVER_GET_CLASS (driver)->power_changed != NULL)
+ needs_battery_monitor = TRUE;
+
+ if (PPD_DRIVER_GET_CLASS (driver)->prepare_to_sleep != NULL)
+ needs_suspend_monitor = TRUE;
+ g_info ("Driver '%s' loaded", ppd_driver_get_driver_name (driver));
g_signal_connect (G_OBJECT (driver), "notify::performance-degraded",
G_CALLBACK (driver_performance_degraded_changed_cb), data);
g_signal_connect (G_OBJECT (driver), "profile-changed",
G_CALLBACK (driver_profile_changed_cb), data);
- } else if (PPD_IS_ACTION (object)) {
- PpdAction *action = PPD_ACTION (object);
+ continue;
+ }
+
+ if (PPD_IS_ACTION (object)) {
+ g_autoptr(PpdAction) action = PPD_ACTION (g_steal_pointer (&object));
g_debug ("Handling action '%s'", ppd_action_get_action_name (action));
- if (!ppd_action_probe (action)) {
- g_debug ("probe() failed for action '%s', skipping",
+ if (action_blocked (data, action)) {
+ g_debug ("Action '%s' is blocked, skipping", ppd_action_get_action_name (action));
+ continue;
+ }
+
+ if (ppd_action_probe (action) == PPD_PROBE_RESULT_FAIL) {
+ g_debug ("probe () failed for action '%s', skipping",
ppd_action_get_action_name (action));
- g_object_unref (object);
continue;
}
- g_ptr_array_add (data->actions, action);
- } else {
- g_assert_not_reached ();
+ if (PPD_ACTION_GET_CLASS (action)->power_changed != NULL)
+ needs_battery_monitor = TRUE;
+
+ g_info ("Action '%s' loaded", ppd_action_get_action_name (action));
+ g_ptr_array_add (data->actions, g_steal_pointer (&action));
+ continue;
}
+
+ g_return_if_reached ();
}
if (!has_required_drivers (data)) {
+ data->ret = EXIT_FAILURE;
g_warning ("Some non-optional profile drivers are missing, programmer error");
- goto bail;
+ g_main_loop_quit (data->main_loop);
}
/* Set initial state either from configuration, or using the currently selected profile */
apply_configuration (data);
- activate_target_profile (data, data->active_profile, PPD_PROFILE_ACTIVATION_REASON_RESET);
+ if (!activate_target_profile (data, data->active_profile, PPD_PROFILE_ACTIVATION_REASON_RESET, &initial_error))
+ g_warning ("Failed to activate initial profile: %s", initial_error->message);
send_dbus_event (data, PROP_ALL);
-
data->was_started = TRUE;
- return;
+ if (needs_battery_monitor) {
+ /* start watching for power changes */
+ g_debug ("Battery state monitor required, connecting to upower...");
+ g_dbus_proxy_new (data->connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ NULL,
+ UPOWER_DBUS_NAME,
+ UPOWER_DBUS_PATH,
+ UPOWER_DBUS_INTERFACE,
+ data->cancellable,
+ on_upower_proxy_cb,
+ data);
+ } else {
+ g_debug ("No battery state monitor required by any driver, let's skip it");
+ }
-bail:
- data->ret = 1;
- g_debug ("Exiting because some non recoverable error occurred during startup");
- g_main_loop_quit (data->main_loop);
+ if (needs_suspend_monitor) {
+ g_debug ("Suspension state monitor required, monitoring logind...");
+ data->logind_sleep_signal_id =
+ g_dbus_connection_signal_subscribe (data->connection,
+ LOGIND_DBUS_NAME,
+ LOGIND_DBUS_INTERFACE,
+ "PrepareForSleep",
+ LOGIND_DBUS_PATH,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_logind_prepare_for_sleep_cb,
+ data,
+ NULL);
+ } else {
+ g_debug ("No suspension monitor required by any driver, let's skip it");
+ }
}
void
-restart_profile_drivers (void)
+restart_profile_drivers_for_default_app (void)
{
- stop_profile_drivers (ppd_app);
- start_profile_drivers (ppd_app);
+ restart_profile_drivers (ppd_app);
}
+
static void
name_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- PpdApp *data = user_data;
+ PpdBusOwnData *data = user_data;
- start_profile_drivers (data);
+ g_debug ("Name '%s' acquired", name);
+
+ start_profile_drivers (data->app);
}
static gboolean
-setup_dbus (PpdApp *data,
- gboolean replace)
-{
- GBytes *bytes;
- GBusNameOwnerFlags flags;
+setup_dbus (PpdApp *data,
+ gboolean replace,
+ GError **error)
+{
+ g_autoptr(GBytes) iface_data = NULL;
+ g_autoptr(GBytes) legacy_iface_data = NULL;
+ g_autoptr(GDBusNodeInfo) introspection_data = NULL;
+ g_autoptr(GDBusNodeInfo) legacy_introspection_data = NULL;
+ PpdBusOwnData *own_data;
+
+ iface_data = g_resources_lookup_data (POWER_PROFILES_RESOURCES_PATH "/"
+ POWER_PROFILES_DBUS_NAME ".xml",
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ error);
+ if (!iface_data)
+ return FALSE;
+
+ legacy_iface_data = g_resources_lookup_data (POWER_PROFILES_RESOURCES_PATH "/"
+ POWER_PROFILES_LEGACY_DBUS_NAME ".xml",
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ error);
+ if (!legacy_iface_data)
+ return FALSE;
+
+ introspection_data = g_dbus_node_info_new_for_xml (g_bytes_get_data (iface_data, NULL),
+ error);
+ if (!introspection_data)
+ return FALSE;
+
+ legacy_introspection_data = g_dbus_node_info_new_for_xml (g_bytes_get_data (legacy_iface_data, NULL),
+ error);
+ if (!legacy_introspection_data)
+ return FALSE;
- bytes = g_resources_lookup_data ("/net/hadess/PowerProfiles/net.hadess.PowerProfiles.xml",
- G_RESOURCE_LOOKUP_FLAGS_NONE,
- NULL);
- data->introspection_data = g_dbus_node_info_new_for_xml (g_bytes_get_data (bytes, NULL), NULL);
- g_bytes_unref (bytes);
- g_assert (data->introspection_data != NULL);
+ own_data = g_new0 (PpdBusOwnData, 1);
+ own_data->app = data;
+ own_data->interface = g_dbus_interface_info_ref (introspection_data->interfaces[0]);
+ own_data->legacy_interface = g_dbus_interface_info_ref (legacy_introspection_data->interfaces[0]);
- flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
+ own_data->flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
if (replace)
- flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
+ own_data->flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
data->name_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
POWER_PROFILES_DBUS_NAME,
- flags,
+ own_data->flags,
bus_acquired_handler,
name_acquired_handler,
name_lost_handler,
- data,
- NULL);
+ own_data,
+ (GDestroyNotify) ppd_bus_own_data_free);
return TRUE;
}
@@ -934,62 +1553,207 @@
if (data == NULL)
return;
- if (data->name_id != 0) {
- g_bus_unown_name (data->name_id);
- data->name_id = 0;
- }
+ stop_profile_drivers (data);
+
+ g_clear_handle_id (&data->name_id, g_bus_unown_name);
+ g_clear_handle_id (&data->legacy_name_id, g_bus_unown_name);
+ g_strfreev (data->blocked_drivers);
+ g_strfreev (data->blocked_actions);
g_clear_pointer (&data->config_path, g_free);
g_clear_pointer (&data->config, g_key_file_unref);
- g_ptr_array_free (data->probed_drivers, TRUE);
- g_ptr_array_free (data->actions, TRUE);
- g_clear_object (&data->driver);
+ g_clear_pointer (&data->probed_drivers, g_ptr_array_unref);
+ g_clear_pointer (&data->actions, g_ptr_array_unref);
+ g_clear_object (&data->cpu_driver);
+ g_clear_object (&data->platform_driver);
g_hash_table_destroy (data->profile_holds);
g_clear_object (&data->auth);
g_clear_pointer (&data->main_loop, g_main_loop_unref);
- g_clear_pointer (&data->introspection_data, g_dbus_node_info_unref);
g_clear_object (&data->connection);
g_free (data);
ppd_app = NULL;
}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (PpdApp, free_app_data)
+
void
main_loop_quit (void)
{
g_main_loop_quit (ppd_app->main_loop);
}
+static inline gboolean
+use_colored_ouput (void)
+{
+ if (g_getenv ("NO_COLOR"))
+ return FALSE;
+ return isatty (fileno (stdout));
+}
+
+static void
+debug_handler_cb (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ DebugOptions *data = user_data;
+ g_autoptr(GString) domain = NULL;
+ gboolean use_color;
+ gint color;
+
+ /* not in verbose mode */
+ if (log_level > data->log_level)
+ return;
+
+ domain = g_string_new (log_domain);
+ use_color = use_colored_ouput ();
+
+ for (gsize i = domain->len; i < 15; i++)
+ g_string_append_c (domain, ' ');
+ g_print ("%s", domain->str);
+
+ switch (log_level) {
+ case G_LOG_LEVEL_ERROR:
+ case G_LOG_LEVEL_CRITICAL:
+ case G_LOG_LEVEL_WARNING:
+ color = 31; /* red */
+ break;
+ default:
+ color = 34; /* blue */
+ break;
+ }
+
+ if (use_color)
+ g_print ("%c[%dm%s\n%c[%dm", 0x1B, color, message, 0x1B, 0);
+ else
+ g_print ("%s\n", message);
+}
+
+static gboolean
+quit_signal_callback (gpointer user_data)
+{
+ PpdApp *data = user_data;
+
+ g_main_loop_quit (data->main_loop);
+ return FALSE;
+}
+
+static gboolean
+verbose_arg_cb (const gchar *option_name,
+ const gchar *value,
+ gpointer user_data,
+ GError **error)
+{
+ DebugOptions *data = user_data;
+
+ if (data->log_level == G_LOG_LEVEL_MESSAGE) {
+ data->log_level = G_LOG_LEVEL_INFO;
+ return TRUE;
+ }
+ if (data->log_level == G_LOG_LEVEL_INFO) {
+ data->log_level = G_LOG_LEVEL_DEBUG;
+ return TRUE;
+ }
+ g_set_error_literal (error,
+ G_OPTION_ERROR,
+ G_OPTION_ERROR_FAILED,
+ "No further debug level supported");
+ return FALSE;
+}
+
+static gboolean
+debug_pre_parse_hook (GOptionContext *context,
+ GOptionGroup *group,
+ gpointer user_data,
+ GError **error)
+{
+ DebugOptions *data = user_data;
+
+ const GOptionEntry options[] = {
+ {
+ "verbose",
+ 'v',
+ G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK,
+ (GOptionArgFunc)verbose_arg_cb,
+ "Show extra debugging information",
+ NULL,
+ },
+ {
+ "replace",
+ 'r',
+ G_OPTION_FLAG_NONE,
+ G_OPTION_ARG_NONE,
+ &data->replace,
+ "Replace the running instance of power-profiles-daemon",
+ NULL,
+ },
+ {
+ "block-driver",
+ 0,
+ G_OPTION_FLAG_NONE,
+ G_OPTION_ARG_STRING_ARRAY,
+ &data->blocked_drivers,
+ "Block driver(s) from loading",
+ NULL,
+ },
+ {
+ "block-action",
+ 0,
+ G_OPTION_FLAG_NONE,
+ G_OPTION_ARG_STRING_ARRAY,
+ &data->blocked_actions,
+ "Block action(s) from loading",
+ NULL,
+ },
+ { NULL }
+ };
+ g_option_group_add_entries (group, options);
+
+ return TRUE;
+}
+
+static gboolean
+debug_post_parse_hook (GOptionContext *context,
+ GOptionGroup *group,
+ gpointer user_data,
+ GError **error)
+{
+ DebugOptions *debug = (DebugOptions *)user_data;
+
+ g_log_set_default_handler (debug_handler_cb, debug);
+
+ return TRUE;
+}
+
int main (int argc, char **argv)
{
- PpdApp *data;
- int ret = 0;
+ g_autoptr(DebugOptions) debug_options = g_new0 (DebugOptions, 1);
+ g_autoptr(PpdApp) data = NULL;
g_autoptr(GOptionContext) option_context = NULL;
g_autoptr(GError) error = NULL;
- gboolean verbose = FALSE;
- gboolean replace = FALSE;
- const GOptionEntry options[] = {
- { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Show extra debugging information", NULL },
- { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace the running instance of power-profiles-daemon", NULL },
- { NULL}
- };
+
+ debug_options->log_level = G_LOG_LEVEL_MESSAGE;
+ debug_options->group = g_option_group_new ("debug",
+ "Debugging Options",
+ "Show debugging options",
+ debug_options,
+ NULL);
+ g_option_group_set_parse_hooks (debug_options->group,
+ debug_pre_parse_hook,
+ debug_post_parse_hook);
setlocale (LC_ALL, "");
option_context = g_option_context_new ("");
- g_option_context_add_main_entries (option_context, options, NULL);
+ g_option_context_add_group (option_context, debug_options->group);
- ret = g_option_context_parse (option_context, &argc, &argv, &error);
- if (!ret) {
+ if (!g_option_context_parse (option_context, &argc, &argv, &error)) {
g_print ("Failed to parse arguments: %s\n", error->message);
return EXIT_FAILURE;
}
- if (verbose)
- g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
-
- g_debug ("Starting power-profiles-daemon version "VERSION);
-
data = g_new0 (PpdApp, 1);
data->main_loop = g_main_loop_new (NULL, TRUE);
data->auth = polkit_authority_get_sync (NULL, NULL);
@@ -998,15 +1762,24 @@
data->profile_holds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) profile_hold_free);
data->active_profile = PPD_PROFILE_BALANCED;
data->selected_profile = PPD_PROFILE_BALANCED;
+ data->blocked_drivers = g_steal_pointer (&debug_options->blocked_drivers);
+ data->blocked_actions = g_steal_pointer (&debug_options->blocked_actions);
+
+ g_unix_signal_add (SIGTERM, quit_signal_callback, data);
+ g_unix_signal_add (SIGINT, quit_signal_callback, data);
+
+ g_info ("Starting power-profiles-daemon version "VERSION);
+
load_configuration (data);
ppd_app = data;
/* Set up D-Bus */
- setup_dbus (data, replace);
+ if (!setup_dbus (data, debug_options->replace, &error)) {
+ g_error ("Failed to start dbus: %s", error->message);
+ return EXIT_FAILURE;
+ }
g_main_loop_run (data->main_loop);
- ret = data->ret;
- free_app_data (data);
- return ret;
+ return data->ret;
}
diff -Nru power-profiles-daemon-0.10.1/src/power-profiles-daemon.dbus.xml.in power-profiles-daemon-0.21/src/power-profiles-daemon.dbus.xml.in
--- power-profiles-daemon-0.10.1/src/power-profiles-daemon.dbus.xml.in 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/power-profiles-daemon.dbus.xml.in 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru power-profiles-daemon-0.10.1/src/power-profiles-daemon.gresource.xml power-profiles-daemon-0.21/src/power-profiles-daemon.gresource.xml
--- power-profiles-daemon-0.10.1/src/power-profiles-daemon.gresource.xml 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/power-profiles-daemon.gresource.xml 1970-01-01 00:00:00.000000000 +0000
@@ -1,7 +0,0 @@
-
-
-
- net.hadess.PowerProfiles.xml
-
-
-
diff -Nru power-profiles-daemon-0.10.1/src/power-profiles-daemon.gresource.xml.in power-profiles-daemon-0.21/src/power-profiles-daemon.gresource.xml.in
--- power-profiles-daemon-0.10.1/src/power-profiles-daemon.gresource.xml.in 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/power-profiles-daemon.gresource.xml.in 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,6 @@
+
+
+
+ @contents@
+
+
diff -Nru power-profiles-daemon-0.10.1/src/powerprofilesctl power-profiles-daemon-0.21/src/powerprofilesctl
--- power-profiles-daemon-0.10.1/src/powerprofilesctl 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/powerprofilesctl 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,260 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import signal
+import subprocess
+import sys
+from gi.repository import Gio, GLib
+
+PP_NAME = "org.freedesktop.UPower.PowerProfiles"
+PP_PATH = "/org/freedesktop/UPower/PowerProfiles"
+PP_IFACE = "org.freedesktop.UPower.PowerProfiles"
+PROPERTIES_IFACE = "org.freedesktop.DBus.Properties"
+
+
+def get_proxy():
+ bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+ return Gio.DBusProxy.new_sync(
+ bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PROPERTIES_IFACE, None
+ )
+
+
+def command(func):
+ def wrapper(*args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except GLib.Error as error:
+ sys.stderr.write(
+ f"Failed to communicate with power-profiles-daemon: {error}\n"
+ )
+ sys.exit(1)
+ except ValueError as error:
+ sys.stderr.write(f"Error: {error}\n")
+ sys.exit(1)
+
+ return wrapper
+
+
+@command
+def _version(_args):
+ client_version = "@VERSION@"
+ try:
+ proxy = get_proxy()
+ daemon_ver = proxy.Get("(ss)", PP_IFACE, "Version")
+ except GLib.Error:
+ daemon_ver = "unknown"
+ print(f"client: {client_version}\ndaemon: {daemon_ver}")
+
+
+@command
+def _get(_args):
+ proxy = get_proxy()
+ profile = proxy.Get("(ss)", PP_IFACE, "ActiveProfile")
+ print(profile)
+
+
+@command
+def _set(args):
+ proxy = get_proxy()
+ proxy.Set(
+ "(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0])
+ )
+
+
+def get_profiles_property(prop):
+ proxy = get_proxy()
+ return proxy.Get("(ss)", PP_IFACE, prop)
+
+
+@command
+def _list(_args):
+ profiles = get_profiles_property("Profiles")
+ reason = get_proxy().Get("(ss)", PP_IFACE, "PerformanceDegraded")
+ degraded = reason != ""
+ active = get_proxy().Get("(ss)", PP_IFACE, "ActiveProfile")
+
+ index = 0
+ for profile in reversed(profiles):
+ if index > 0:
+ print("")
+ marker = "*" if profile["Profile"] == active else " "
+ print(f'{marker} {profile["Profile"]}:')
+ for driver in ["CpuDriver", "PlatformDriver"]:
+ if driver not in profile:
+ continue
+ value = profile[driver]
+ print(f" {driver}:\t{value}")
+ if profile["Profile"] == "performance":
+ print(" Degraded: ", f"yes ({reason})" if degraded else "no")
+ index += 1
+
+
+@command
+def _list_holds(_args):
+ holds = get_profiles_property("ActiveProfileHolds")
+
+ index = 0
+ for hold in holds:
+ if index > 0:
+ print("")
+ print("Hold:")
+ print(" Profile: ", hold["Profile"])
+ print(" Application ID: ", hold["ApplicationId"])
+ print(" Reason: ", hold["Reason"])
+ index += 1
+
+
+@command
+def _launch(args):
+ reason = args.reason
+ profile = args.profile
+ appid = args.appid
+ if not args.arguments:
+ raise ValueError("No command to launch")
+ if not args.appid:
+ appid = args.arguments[0]
+ if not profile:
+ profile = "performance"
+ if not reason:
+ reason = f"Running {args.appid}"
+ ret = 0
+ bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+ proxy = Gio.DBusProxy.new_sync(
+ bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PP_IFACE, None
+ )
+ cookie = proxy.HoldProfile("(sss)", profile, reason, appid)
+
+ # print (f'Got {cookie} for {profile} hold')
+ with subprocess.Popen(args.arguments) as launched_app:
+ # Redirect the same signal to the child
+ def receive_signal(signum, _stack):
+ launched_app.send_signal(signum)
+
+ redirected_signals = [
+ signal.SIGTERM,
+ signal.SIGINT,
+ signal.SIGABRT,
+ ]
+
+ for sig in redirected_signals:
+ signal.signal(sig, receive_signal)
+
+ try:
+ launched_app.wait()
+ ret = launched_app.returncode
+ except KeyboardInterrupt:
+ ret = launched_app.returncode
+
+ for sig in redirected_signals:
+ signal.signal(sig, signal.SIG_DFL)
+
+ proxy.ReleaseProfile("(u)", cookie)
+
+ if ret < 0:
+ # Use standard POSIX signal exit code.
+ os.kill(os.getpid(), -ret)
+ return
+
+ sys.exit(ret)
+
+
+def get_parser():
+ parser = argparse.ArgumentParser(
+ epilog="Use “powerprofilesctl COMMAND --help” to get detailed help for individual commands",
+ )
+ subparsers = parser.add_subparsers(help="Individual command help", dest="command")
+ parser_list = subparsers.add_parser("list", help="List available power profiles")
+ parser_list.set_defaults(func=_list)
+ parser_list_holds = subparsers.add_parser(
+ "list-holds", help="List current power profile holds"
+ )
+ parser_list_holds.set_defaults(func=_list_holds)
+ parser_get = subparsers.add_parser(
+ "get", help="Print the currently active power profile"
+ )
+ parser_get.set_defaults(func=_get)
+ parser_set = subparsers.add_parser(
+ "set", help="Set the currently active power profile"
+ )
+ parser_set.add_argument(
+ "profile",
+ nargs=1,
+ help="Profile to use for set command",
+ )
+ parser_set.set_defaults(func=_set)
+ parser_launch = subparsers.add_parser(
+ "launch",
+ help="Launch a command while holding a power profile",
+ description="Launch the command while holding a power profile,"
+ "either performance, or power-saver. By default, the profile hold "
+ "is for the performance profile, but it might not be available on "
+ "all systems. See the list command for a list of available profiles.",
+ )
+ parser_launch.add_argument(
+ "arguments",
+ nargs="*",
+ help="Command to launch",
+ )
+ parser_launch.add_argument(
+ "--profile", "-p", required=False, help="Profile to use for launch command"
+ )
+ parser_launch.add_argument(
+ "--reason", "-r", required=False, help="Reason to use for launch command"
+ )
+ parser_launch.add_argument(
+ "--appid", "-i", required=False, help="AppId to use for launch command"
+ )
+ parser_launch.set_defaults(func=_launch)
+ parser_version = subparsers.add_parser(
+ "version", help="Print version information and exit"
+ )
+ parser_version.set_defaults(func=_version)
+
+ if not os.getenv("PPD_COMPLETIONS_GENERATION"):
+ return parser
+
+ try:
+ import shtab # pylint: disable=import-outside-toplevel
+
+ shtab.add_argument_to(parser, ["--print-completion"]) # magic!
+ except ImportError:
+ pass
+
+ return parser
+
+
+def check_unknown_args(args, unknown_args, cmd):
+ if cmd != "launch":
+ return False
+
+ for idx, unknown_arg in enumerate(unknown_args):
+ arg = args[idx]
+ if arg == cmd:
+ return True
+ if unknown_arg == arg:
+ return False
+
+ return True
+
+
+def main():
+ parser = get_parser()
+ args, unknown = parser.parse_known_args()
+ # default behavior is to run list if no command is given
+ if not args.command:
+ args.func = _list
+
+ if check_unknown_args(sys.argv[1:], unknown, args.command):
+ args.arguments += unknown
+ unknown = []
+
+ if unknown:
+ msg = argparse._("unrecognized arguments: %s")
+ parser.error(msg % " ".join(unknown))
+
+ args.func(args)
+
+
+if __name__ == "__main__":
+ main()
diff -Nru power-profiles-daemon-0.10.1/src/powerprofilesctl.in power-profiles-daemon-0.21/src/powerprofilesctl.in
--- power-profiles-daemon-0.10.1/src/powerprofilesctl.in 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/powerprofilesctl.in 1970-01-01 00:00:00.000000000 +0000
@@ -1,262 +0,0 @@
-#!@PYTHON3@
-
-import signal
-import subprocess
-import sys
-from gi.repository import Gio, GLib
-
-VERSION = '@VERSION@'
-
-def usage_main():
- print('Usage:')
- print(' powerprofilesctl COMMAND [ARGS…]')
- print('')
- print('Commands:')
- print(' help Print help')
- print(' version Print version')
- print(' get Print the currently active power profile')
- print(' set Set the currently active power profile')
- print(' list List available power profiles')
- print(' list-holds List current power profile holds')
- print(' launch Launch a command while holding a power profile')
- print('')
- print('Use “powerprofilesctl help COMMAND” to get detailed help.')
-
-def usage_version():
- print('Usage:')
- print(' powerprofilesctl version')
- print('')
- print('Print version information and exit.')
-
-def usage_get():
- print('Usage:')
- print(' powerprofilesctl get')
- print('')
- print('Print the currently active power profile.')
-
-def usage_set():
- print('Usage:')
- print(' powerprofilesctl set PROFILE')
- print('')
- print('Set the currently active power profile. Must be one of the ')
- print('available profiles.')
-
-def usage_list():
- print('Usage:')
- print(' powerprofilesctl list')
- print('')
- print('List available power profiles.')
-
-def usage_list_holds():
- print('Usage:')
- print(' powerprofilesctl list-holds')
- print('')
- print('List current power profile holds.')
-
-def usage_launch():
- print('Usage:')
- print(' powerprofilesctl launch [COMMAND…]')
- print('')
- print('Launch a command while holding a power profile.')
- print('')
- print('Options:')
- print(' -p, --profile=PROFILE The power profile to hold')
- print(' -r, --reason=REASON The reason for the profile hold')
- print(' -i, --appid=APP-ID The application ID for the profile hold')
- print('')
- print('Launch the command while holding a power profile, either performance, ')
- print('or power-saver. By default, the profile hold is for the performance ')
- print('profile, but it might not be available on all systems. See the list ')
- print('command for a list of available profiles.')
-
-def usage(_command=None):
- if not _command:
- usage_main()
- elif _command == 'get':
- usage_get()
- elif _command == 'set':
- usage_set()
- elif _command == 'list':
- usage_list()
- elif _command == 'list-holds':
- usage_list_holds()
- elif _command == 'launch':
- usage_launch()
- elif _command == 'version':
- usage_version()
- else:
- usage_main()
-
-def version():
- print (VERSION)
-
-def get_proxy():
- try:
- bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
- proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
- 'net.hadess.PowerProfiles',
- '/net/hadess/PowerProfiles',
- 'org.freedesktop.DBus.Properties', None)
- except:
- raise SystemError
- return proxy
-
-def _get():
- proxy = get_proxy()
- profile = proxy.Get('(ss)', 'net.hadess.PowerProfiles', 'ActiveProfile')
- print(profile)
-
-def _set(profile):
- proxy = get_proxy()
- proxy.Set('(ssv)',
- 'net.hadess.PowerProfiles',
- 'ActiveProfile',
- GLib.Variant.new_string(profile))
-
-def get_profiles_property(prop):
- try:
- proxy = get_proxy()
- except:
- raise SystemError
-
- profiles = None
- try:
- profiles = proxy.Get('(ss)', 'net.hadess.PowerProfiles', prop)
- except:
- raise ReferenceError
- else:
- return profiles
-
-def _list():
- try:
- profiles = get_profiles_property('Profiles')
- reason = get_proxy().Get('(ss)', 'net.hadess.PowerProfiles', 'PerformanceDegraded')
- degraded = (reason != '')
- active = get_proxy().Get('(ss)', 'net.hadess.PowerProfiles', 'ActiveProfile')
- except:
- print("Couldn\'t get Profiles: ", sys.exc_info()[0])
- raise SystemError
- else:
- index = 0
- for profile in reversed(profiles):
- if index > 0:
- print('')
- marker = '*' if profile['Profile'] == active else ' '
- print(f'{marker} {profile["Profile"]}:')
- print(' Driver: ', profile['Driver'])
- if profile['Profile'] == 'performance':
- print(' Degraded: ', f'yes ({reason})' if degraded else 'no')
- index += 1
-
-def _list_holds():
- try:
- holds = get_profiles_property('ActiveProfileHolds')
- except:
- # print("Couldn\'t get ActiveProfileHolds: ", sys.exc_info()[0])
- raise SystemError
- else:
- index = 0
- for hold in holds:
- if index > 0:
- print('')
- print('Hold:')
- print(' Profile: ', hold['Profile'])
- print(' Application ID: ', hold['ApplicationId'])
- print(' Reason: ', hold['Reason'])
- index += 1
-
-def _launch(args, profile, appid, reason):
- try:
- bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
- proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
- 'net.hadess.PowerProfiles',
- '/net/hadess/PowerProfiles',
- 'net.hadess.PowerProfiles', None)
- except:
- raise SystemError
-
- cookie = proxy.HoldProfile('(sss)', profile, reason, appid)
-
- # Kill child when we go away
- def receive_signal(_signum, _stack):
- launched_app.terminate()
- signal.signal(signal.SIGTERM, receive_signal)
-
- # print (f'Got {cookie} for {profile} hold')
- with subprocess.Popen(args) as launched_app:
- launched_app.wait()
- proxy.ReleaseProfile('(u)', cookie)
-
-def main(): # pylint: disable=too-many-branches, disable=too-many-statements
- args = None
- if len(sys.argv) == 1:
- command = 'list'
- elif len(sys.argv) >= 2:
- command = sys.argv[1]
- if command == '--help':
- command = 'help'
- if command == '--version':
- command = 'version'
- else:
- args = sys.argv[2:]
-
- if command == 'help':
- if len(args) > 0:
- usage(args[0])
- else:
- usage(None)
- elif command == 'version':
- version()
- elif command == 'get':
- _get()
- elif command == 'set':
- if len(args) != 1:
- usage_set()
- sys.exit(1)
- _set(args[0])
- elif command == 'list':
- _list()
- elif command == 'list-holds':
- _list_holds()
- elif command == 'launch':
- if len(args) == 0:
- sys.exit(0)
- profile = None
- reason = None
- appid = None
- while True:
- if args[0] == '--':
- args = args[1:]
- break
- if args[0][:9] == '--profile' or args[0] == '-p':
- if args[0][:10] == '--profile=':
- args = args[0].split('=') + args[1:]
- profile = args[1]
- args = args[2:]
- continue
- if args[0][:8] == '--reason' or args[0] == '-r':
- if args[0][:9] == '--reason=':
- args = args[0].split('=') + args[1:]
- reason = args[1]
- args = args[2:]
- continue
- if args[0][:7] == '--appid' or args[0] == '-i':
- if args[0][:8] == '--appid=':
- args = args[0].split('=') + args[1:]
- appid = args[1]
- args = args[2:]
- continue
- break
-
- if len(args) < 1:
- sys.exit(0)
- if not appid:
- appid = args[0]
- if not reason:
- reason = 'Running ' + appid
- if not profile:
- profile = 'performance'
- _launch(args, profile, appid, reason)
-
-if __name__ == '__main__':
- main()
diff -Nru power-profiles-daemon-0.10.1/src/ppd-action-amdgpu-panel-power.c power-profiles-daemon-0.21/src/ppd-action-amdgpu-panel-power.c
--- power-profiles-daemon-0.10.1/src/ppd-action-amdgpu-panel-power.c 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-action-amdgpu-panel-power.c 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2024 Advanced Micro Devices
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#define G_LOG_DOMAIN "AmdgpuAction"
+
+#include "config.h"
+
+#include
+
+#include "ppd-action-amdgpu-panel-power.h"
+#include "ppd-profile.h"
+#include "ppd-utils.h"
+
+#define PROC_CPUINFO_PATH "/proc/cpuinfo"
+
+#define PANEL_POWER_SYSFS_NAME "amdgpu/panel_power_savings"
+#define PANEL_STATUS_SYSFS_NAME "status"
+
+#define UPOWER_DBUS_NAME "org.freedesktop.UPower"
+#define UPOWER_DBUS_PATH "/org/freedesktop/UPower"
+#define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower"
+
+/**
+ * SECTION:ppd-action-amdgpu-panel-power
+ * @Short_description: Power savings for eDP connected displays
+ * @Title: AMDGPU Panel power action
+ *
+ * The AMDGPU panel power action utilizes the sysfs attribute present on some DRM
+ * connectors for amdgpu called "panel_power_savings". This will use an AMD specific
+ * hardware feature for a power savings profile for the panel.
+ *
+ */
+
+struct _PpdActionAmdgpuPanelPower
+{
+ PpdAction parent_instance;
+ PpdProfile last_profile;
+
+ GUdevClient *client;
+
+ gint panel_power_saving;
+ gboolean valid_battery;
+ gboolean on_battery;
+};
+
+G_DEFINE_TYPE (PpdActionAmdgpuPanelPower, ppd_action_amdgpu_panel_power, PPD_TYPE_ACTION)
+
+static GObject*
+ppd_action_amdgpu_panel_power_constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+
+ object = G_OBJECT_CLASS (ppd_action_amdgpu_panel_power_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ g_object_set (object,
+ "action-name", "amdgpu_panel_power",
+ NULL);
+
+ return object;
+}
+
+static gboolean
+panel_connected (GUdevDevice *device)
+{
+ const char *value;
+ g_autofree gchar *stripped = NULL;
+
+ value = g_udev_device_get_sysfs_attr_uncached (device, PANEL_STATUS_SYSFS_NAME);
+ if (!value)
+ return FALSE;
+ stripped = g_strchomp (g_strdup (value));
+
+ return g_strcmp0 (stripped, "connected") == 0;
+}
+
+static gboolean
+set_panel_power (PpdActionAmdgpuPanelPower *self, gint power, GError **error)
+{
+ GList *devices, *l;
+
+ devices = g_udev_client_query_by_subsystem (self->client, "drm");
+ if (devices == NULL) {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "no drm devices found");
+ return FALSE;
+ }
+
+ for (l = devices; l != NULL; l = l->next) {
+ GUdevDevice *dev = l->data;
+ const char *value;
+ guint64 parsed;
+
+ value = g_udev_device_get_devtype (dev);
+ if (g_strcmp0 (value, "drm_connector") != 0)
+ continue;
+
+ if (!panel_connected (dev))
+ continue;
+
+ value = g_udev_device_get_sysfs_attr_uncached (dev, PANEL_POWER_SYSFS_NAME);
+ if (!value)
+ continue;
+
+ parsed = g_ascii_strtoull (value, NULL, 10);
+
+ /* overflow check */
+ if (parsed == G_MAXUINT64) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "cannot parse %s as caused overflow",
+ value);
+ return FALSE;
+ }
+
+ if (parsed == power)
+ continue;
+
+ if (!ppd_utils_write_sysfs_int (dev, PANEL_POWER_SYSFS_NAME, power, error))
+ return FALSE;
+
+ break;
+ }
+
+ g_list_free_full (devices, g_object_unref);
+
+ return TRUE;
+}
+
+static gboolean
+ppd_action_amdgpu_panel_update_target (PpdActionAmdgpuPanelPower *self,
+ GError **error)
+{
+ gint target = 0;
+
+ /* only activate if we know that we're on battery */
+ if (self->on_battery) {
+ switch (self->last_profile) {
+ case PPD_PROFILE_POWER_SAVER:
+ target = 3;
+ break;
+ case PPD_PROFILE_BALANCED:
+ target = 1;
+ break;
+ case PPD_PROFILE_PERFORMANCE:
+ target = 0;
+ break;
+ }
+ }
+
+ if (!set_panel_power (self, target, error))
+ return FALSE;
+ self->panel_power_saving = target;
+
+ return TRUE;
+}
+
+static gboolean
+ppd_action_amdgpu_panel_power_activate_profile (PpdAction *action,
+ PpdProfile profile,
+ GError **error)
+{
+ PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action);
+ self->last_profile = profile;
+
+ if (!self->valid_battery) {
+ g_debug ("upower not available; battery data might be stale");
+ return TRUE;
+ }
+
+ return ppd_action_amdgpu_panel_update_target (self, error);
+}
+
+static gboolean
+ppd_action_amdgpu_panel_power_power_changed (PpdAction *action,
+ PpdPowerChangedReason reason,
+ GError **error)
+{
+ PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action);
+
+ switch (reason) {
+ case PPD_POWER_CHANGED_REASON_UNKNOWN:
+ self->valid_battery = FALSE;
+ return TRUE;
+ case PPD_POWER_CHANGED_REASON_AC:
+ self->on_battery = FALSE;
+ break;
+ case PPD_POWER_CHANGED_REASON_BATTERY:
+ self->on_battery = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ self->valid_battery = TRUE;
+ return ppd_action_amdgpu_panel_update_target (self, error);
+}
+
+static void
+udev_uevent_cb (GUdevClient *client,
+ gchar *action,
+ GUdevDevice *device,
+ gpointer user_data)
+{
+ PpdActionAmdgpuPanelPower *self = user_data;
+
+ if (!g_str_equal (action, "add"))
+ return;
+
+ if (!g_udev_device_has_sysfs_attr (device, PANEL_POWER_SYSFS_NAME))
+ return;
+
+ if (!panel_connected (device))
+ return;
+
+ g_debug ("Updating panel power saving for '%s' to '%d'",
+ g_udev_device_get_sysfs_path (device),
+ self->panel_power_saving);
+ ppd_utils_write_sysfs_int (device, PANEL_POWER_SYSFS_NAME,
+ self->panel_power_saving, NULL);
+}
+
+static PpdProbeResult
+ppd_action_amdgpu_panel_power_probe (PpdAction *action)
+{
+ g_autofree gchar *cpuinfo_path = NULL;
+ g_autofree gchar *cpuinfo = NULL;
+ g_auto(GStrv) lines = NULL;
+
+ cpuinfo_path = ppd_utils_get_sysfs_path (PROC_CPUINFO_PATH);
+ if (!g_file_get_contents (cpuinfo_path, &cpuinfo, NULL, NULL))
+ return PPD_PROBE_RESULT_FAIL;
+
+ lines = g_strsplit (cpuinfo, "\n", -1);
+
+ for (gchar **line = lines; *line != NULL; line++) {
+ if (g_str_has_prefix (*line, "vendor_id") &&
+ strchr (*line, ':')) {
+ g_auto(GStrv) sections = g_strsplit (*line, ":", 2);
+
+ if (g_strv_length (sections) < 2)
+ continue;
+ if (g_strcmp0 (g_strstrip (sections[1]), "AuthenticAMD") == 0)
+ return PPD_PROBE_RESULT_SUCCESS;
+ }
+ }
+
+
+ return PPD_PROBE_RESULT_FAIL;
+}
+
+static void
+ppd_action_amdgpu_panel_power_finalize (GObject *object)
+{
+ PpdActionAmdgpuPanelPower *action;
+
+ action = PPD_ACTION_AMDGPU_PANEL_POWER (object);
+ g_clear_object (&action->client);
+ G_OBJECT_CLASS (ppd_action_amdgpu_panel_power_parent_class)->finalize (object);
+}
+
+static void
+ppd_action_amdgpu_panel_power_class_init (PpdActionAmdgpuPanelPowerClass *klass)
+{
+ GObjectClass *object_class;
+ PpdActionClass *driver_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+ object_class->constructor = ppd_action_amdgpu_panel_power_constructor;
+ object_class->finalize = ppd_action_amdgpu_panel_power_finalize;
+
+ driver_class = PPD_ACTION_CLASS(klass);
+ driver_class->probe = ppd_action_amdgpu_panel_power_probe;
+ driver_class->activate_profile = ppd_action_amdgpu_panel_power_activate_profile;
+ driver_class->power_changed = ppd_action_amdgpu_panel_power_power_changed;
+}
+
+static void
+ppd_action_amdgpu_panel_power_init (PpdActionAmdgpuPanelPower *self)
+{
+ const gchar * const subsystem[] = { "drm", NULL };
+
+ self->client = g_udev_client_new (subsystem);
+ g_signal_connect_object (G_OBJECT (self->client), "uevent",
+ G_CALLBACK (udev_uevent_cb), self, 0);
+}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-action-amdgpu-panel-power.h power-profiles-daemon-0.21/src/ppd-action-amdgpu-panel-power.h
--- power-profiles-daemon-0.10.1/src/ppd-action-amdgpu-panel-power.h 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-action-amdgpu-panel-power.h 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2024 Advanced Micro Devices
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#pragma once
+
+#include "ppd-action.h"
+
+#define PPD_TYPE_ACTION_AMDGPU_PANEL_POWER (ppd_action_amdgpu_panel_power_get_type())
+G_DECLARE_FINAL_TYPE(PpdActionAmdgpuPanelPower, ppd_action_amdgpu_panel_power, PPD, ACTION_AMDGPU_PANEL_POWER, PpdAction)
diff -Nru power-profiles-daemon-0.10.1/src/ppd-action-trickle-charge.c power-profiles-daemon-0.21/src/ppd-action-trickle-charge.c
--- power-profiles-daemon-0.10.1/src/ppd-action-trickle-charge.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-action-trickle-charge.c 2024-04-03 23:55:02.000000000 +0000
@@ -7,6 +7,8 @@
*
*/
+#define G_LOG_DOMAIN "TrickleChargeAction"
+
#include
#include "ppd-action-trickle-charge.h"
@@ -54,8 +56,16 @@
for (l = devices; l != NULL; l = l->next) {
GUdevDevice *dev = l->data;
+ const char *value;
+
+ if (g_strcmp0 (g_udev_device_get_sysfs_attr (dev, "scope"), "Device") != 0)
+ continue;
+
+ value = g_udev_device_get_sysfs_attr_uncached (dev, CHARGE_TYPE_SYSFS_NAME);
+ if (!value)
+ continue;
- if (!g_udev_device_has_sysfs_attr (dev, CHARGE_TYPE_SYSFS_NAME))
+ if (g_strcmp0 (charge_type, value) == 0)
continue;
ppd_utils_write_sysfs (dev, CHARGE_TYPE_SYSFS_NAME, charge_type, NULL);
@@ -122,11 +132,11 @@
GObjectClass *object_class;
PpdActionClass *driver_class;
- object_class = G_OBJECT_CLASS(klass);
+ object_class = G_OBJECT_CLASS (klass);
object_class->constructor = ppd_action_trickle_charge_constructor;
object_class->finalize = ppd_action_trickle_charge_finalize;
- driver_class = PPD_ACTION_CLASS(klass);
+ driver_class = PPD_ACTION_CLASS (klass);
driver_class->activate_profile = ppd_action_trickle_charge_activate_profile;
}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-action-trickle-charge.h power-profiles-daemon-0.21/src/ppd-action-trickle-charge.h
--- power-profiles-daemon-0.10.1/src/ppd-action-trickle-charge.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-action-trickle-charge.h 2024-04-03 23:55:02.000000000 +0000
@@ -11,5 +11,5 @@
#include "ppd-action.h"
-#define PPD_TYPE_ACTION_TRICKLE_CHARGE (ppd_action_trickle_charge_get_type())
-G_DECLARE_FINAL_TYPE(PpdActionTrickleCharge, ppd_action_trickle_charge, PPD, ACTION_TRICKLE_CHARGE, PpdAction)
+#define PPD_TYPE_ACTION_TRICKLE_CHARGE (ppd_action_trickle_charge_get_type ())
+G_DECLARE_FINAL_TYPE (PpdActionTrickleCharge, ppd_action_trickle_charge, PPD, ACTION_TRICKLE_CHARGE, PpdAction)
diff -Nru power-profiles-daemon-0.10.1/src/ppd-action.c power-profiles-daemon-0.21/src/ppd-action.c
--- power-profiles-daemon-0.10.1/src/ppd-action.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-action.c 2024-04-03 23:55:02.000000000 +0000
@@ -7,6 +7,8 @@
*
*/
+#define G_LOG_DOMAIN "Action"
+
#include "ppd-action.h"
#include "ppd-enums.h"
@@ -58,11 +60,11 @@
switch (property_id) {
case PROP_ACTION_NAME:
- g_assert (priv->action_name == NULL);
+ g_return_if_fail (priv->action_name == NULL);
priv->action_name = g_value_dup_string (value);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
@@ -80,7 +82,7 @@
g_value_set_string (value, priv->action_name);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
@@ -100,7 +102,7 @@
{
GObjectClass *object_class;
- object_class = G_OBJECT_CLASS(klass);
+ object_class = G_OBJECT_CLASS (klass);
object_class->finalize = ppd_action_finalize;
object_class->get_property = ppd_action_get_property;
object_class->set_property = ppd_action_set_property;
@@ -111,7 +113,7 @@
* A unique action name, only used for debugging.
*/
g_object_class_install_property (object_class, PROP_ACTION_NAME,
- g_param_spec_string("action-name",
+ g_param_spec_string ("action-name",
"Action name",
"Action name",
NULL,
@@ -123,13 +125,13 @@
{
}
-gboolean
+PpdProbeResult
ppd_action_probe (PpdAction *action)
{
g_return_val_if_fail (PPD_IS_ACTION (action), FALSE);
if (!PPD_ACTION_GET_CLASS (action)->probe)
- return TRUE;
+ return PPD_PROBE_RESULT_SUCCESS;
return PPD_ACTION_GET_CLASS (action)->probe (action);
}
@@ -147,6 +149,18 @@
return PPD_ACTION_GET_CLASS (action)->activate_profile (action, profile, error);
}
+gboolean ppd_action_power_changed (PpdAction *action,
+ PpdPowerChangedReason reason,
+ GError **error)
+{
+ g_return_val_if_fail (PPD_IS_ACTION (action), FALSE);
+
+ if (!PPD_ACTION_GET_CLASS (action)->power_changed)
+ return TRUE;
+
+ return PPD_ACTION_GET_CLASS (action)->power_changed (action, reason, error);
+}
+
const char *
ppd_action_get_action_name (PpdAction *action)
{
diff -Nru power-profiles-daemon-0.10.1/src/ppd-action.h power-profiles-daemon-0.21/src/ppd-action.h
--- power-profiles-daemon-0.10.1/src/ppd-action.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-action.h 2024-04-03 23:55:02.000000000 +0000
@@ -12,8 +12,8 @@
#include
#include "ppd-profile.h"
-#define PPD_TYPE_ACTION (ppd_action_get_type())
-G_DECLARE_DERIVABLE_TYPE(PpdAction, ppd_action, PPD, ACTION, GObject)
+#define PPD_TYPE_ACTION (ppd_action_get_type ())
+G_DECLARE_DERIVABLE_TYPE (PpdAction, ppd_action, PPD, ACTION, GObject)
/**
* PpdActionClass:
@@ -28,14 +28,19 @@
{
GObjectClass parent_class;
- gboolean (* probe) (PpdAction *action);
- gboolean (* activate_profile) (PpdAction *action,
- PpdProfile profile,
- GError **error);
+ PpdProbeResult (* probe) (PpdAction *action);
+ gboolean (* activate_profile) (PpdAction *action,
+ PpdProfile profile,
+ GError **error);
+ gboolean (* power_changed) (PpdAction *action,
+ PpdPowerChangedReason reason,
+ GError **error);
+
};
#ifndef __GTK_DOC_IGNORE__
-gboolean ppd_action_probe (PpdAction *action);
+PpdProbeResult ppd_action_probe (PpdAction *action);
gboolean ppd_action_activate_profile (PpdAction *action, PpdProfile profile, GError **error);
+gboolean ppd_action_power_changed (PpdAction *action, PpdPowerChangedReason reason, GError **error);
const char *ppd_action_get_action_name (PpdAction *action);
#endif
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-amd-pstate.c power-profiles-daemon-0.21/src/ppd-driver-amd-pstate.c
--- power-profiles-daemon-0.10.1/src/ppd-driver-amd-pstate.c 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-amd-pstate.c 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2020 Bastien Nocera
+ * Copyright (c) 2022 Prajna Sariputra
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#define G_LOG_DOMAIN "CpuDriver"
+
+#include
+
+#include "ppd-utils.h"
+#include "ppd-driver-amd-pstate.h"
+
+#define CPUFREQ_POLICY_DIR "/sys/devices/system/cpu/cpufreq/"
+#define PSTATE_STATUS_PATH "/sys/devices/system/cpu/amd_pstate/status"
+#define ACPI_PM_PROFILE "/sys/firmware/acpi/pm_profile"
+
+enum acpi_preferred_pm_profiles {
+ PM_UNSPECIFIED = 0,
+ PM_DESKTOP = 1,
+ PM_MOBILE = 2,
+ PM_WORKSTATION = 3,
+ PM_ENTERPRISE_SERVER = 4,
+ PM_SOHO_SERVER = 5,
+ PM_APPLIANCE_PC = 6,
+ PM_PERFORMANCE_SERVER = 7,
+ PM_TABLET = 8,
+ NR_PM_PROFILES = 9
+};
+
+struct _PpdDriverAmdPstate
+{
+ PpdDriverCpu parent_instance;
+
+ PpdProfile activated_profile;
+ GPtrArray *epp_devices; /* Array of paths */
+ gboolean on_battery;
+};
+
+G_DEFINE_TYPE (PpdDriverAmdPstate, ppd_driver_amd_pstate, PPD_TYPE_DRIVER_CPU)
+
+static gboolean ppd_driver_amd_pstate_activate_profile (PpdDriver *driver,
+ PpdProfile profile,
+ PpdProfileActivationReason reason,
+ GError **error);
+
+static GObject*
+ppd_driver_amd_pstate_constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+
+ object = G_OBJECT_CLASS (ppd_driver_amd_pstate_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ g_object_set (object,
+ "driver-name", "amd_pstate",
+ "profiles", PPD_PROFILE_PERFORMANCE | PPD_PROFILE_BALANCED | PPD_PROFILE_POWER_SAVER,
+ NULL);
+
+ return object;
+}
+
+static PpdProbeResult
+probe_epp (PpdDriverAmdPstate *pstate)
+{
+ g_autoptr(GDir) dir = NULL;
+ g_autofree char *policy_dir = NULL;
+ g_autofree char *pstate_status_path = NULL;
+ g_autofree char *status = NULL;
+ g_autofree char *pm_profile_path = NULL;
+ g_autofree char *pm_profile_str = NULL;
+ guint64 pm_profile;
+ const char *dirname;
+
+ /* Verify that AMD P-State is running in active mode */
+ pstate_status_path = ppd_utils_get_sysfs_path (PSTATE_STATUS_PATH);
+ if (!g_file_get_contents (pstate_status_path, &status, NULL, NULL))
+ return PPD_PROBE_RESULT_FAIL;
+ status = g_strchomp (status);
+ if (g_strcmp0 (status, "active") != 0) {
+ g_debug ("AMD P-State is not running in active mode");
+ return PPD_PROBE_RESULT_FAIL;
+ }
+
+ policy_dir = ppd_utils_get_sysfs_path (CPUFREQ_POLICY_DIR);
+ dir = g_dir_open (policy_dir, 0, NULL);
+ if (!dir) {
+ g_debug ("Could not open %s", policy_dir);
+ return PPD_PROBE_RESULT_FAIL;
+ }
+
+ /* only run on things that we know aren't servers */
+ pm_profile_path = ppd_utils_get_sysfs_path (ACPI_PM_PROFILE);
+ if (!g_file_get_contents (pm_profile_path, &pm_profile_str, NULL, NULL))
+ return PPD_PROBE_RESULT_FAIL;
+ pm_profile = g_ascii_strtoull (pm_profile_str, NULL, 10);
+ switch (pm_profile) {
+ case PM_UNSPECIFIED:
+ case PM_ENTERPRISE_SERVER:
+ case PM_SOHO_SERVER:
+ case PM_PERFORMANCE_SERVER:
+ g_debug ("AMD-P-State not supported on PM profile %" G_GUINT64_FORMAT, pm_profile);
+ return PPD_PROBE_RESULT_FAIL;
+ default:
+ break;
+ }
+
+ while ((dirname = g_dir_read_name (dir)) != NULL) {
+ g_autofree char *base = NULL;
+ g_autofree char *path = NULL;
+ g_autoptr(GError) error = NULL;
+
+ base = g_build_filename (policy_dir,
+ dirname,
+ NULL);
+
+ path = g_build_filename (base,
+ "energy_performance_preference",
+ NULL);
+ if (!g_file_test (path, G_FILE_TEST_EXISTS))
+ continue;
+
+ if (!pstate->epp_devices)
+ pstate->epp_devices = g_ptr_array_new_with_free_func (g_free);
+
+ g_ptr_array_add (pstate->epp_devices, g_steal_pointer (&base));
+ }
+
+ if (pstate->epp_devices && pstate->epp_devices->len)
+ return PPD_PROBE_RESULT_SUCCESS;
+
+ return PPD_PROBE_RESULT_FAIL;
+}
+
+static PpdProbeResult
+ppd_driver_amd_pstate_probe (PpdDriver *driver)
+{
+ PpdDriverAmdPstate *pstate = PPD_DRIVER_AMD_PSTATE (driver);
+ PpdProbeResult ret;
+
+ ret = probe_epp (pstate);
+
+ g_debug ("%s p-state settings",
+ ret == PPD_PROBE_RESULT_SUCCESS ? "Found" : "Didn't find");
+ return ret;
+}
+
+static const char *
+profile_to_gov_pref (PpdProfile profile)
+{
+ switch (profile) {
+ case PPD_PROFILE_POWER_SAVER:
+ return "powersave";
+ case PPD_PROFILE_BALANCED:
+ return "powersave";
+ case PPD_PROFILE_PERFORMANCE:
+ return "performance";
+ }
+
+ g_return_val_if_reached (NULL);
+}
+
+static const char *
+profile_to_epp_pref (PpdProfile profile, gboolean battery)
+{
+ /* Note that we don't check "energy_performance_available_preferences"
+ * as all the values are always available */
+ switch (profile) {
+ case PPD_PROFILE_POWER_SAVER:
+ return "power";
+ case PPD_PROFILE_BALANCED:
+ return battery ? "balance_power" : "balance_performance";
+ case PPD_PROFILE_PERFORMANCE:
+ return "performance";
+ }
+
+ g_return_val_if_reached (NULL);
+}
+
+static gboolean
+apply_pref_to_devices (GPtrArray *devices,
+ PpdProfile profile,
+ gboolean battery,
+ GError **error)
+{
+ const char *epp_pref;
+ const char *gov_pref;
+
+ if (profile == PPD_PROFILE_UNSET)
+ return TRUE;
+
+ epp_pref = profile_to_epp_pref (profile, battery);
+ gov_pref = profile_to_gov_pref (profile);
+
+ for (guint i = 0; i < devices->len; ++i) {
+ const char *base = g_ptr_array_index (devices, i);
+ g_autofree char *epp = NULL;
+ g_autofree char *gov = NULL;
+
+ gov = g_build_filename (base,
+ "scaling_governor",
+ NULL);
+
+ if (!ppd_utils_write (gov, gov_pref, error))
+ return FALSE;
+
+ epp = g_build_filename (base,
+ "energy_performance_preference",
+ NULL);
+
+ if (!ppd_utils_write (epp, epp_pref, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ppd_driver_amd_pstate_activate_profile (PpdDriver *driver,
+ PpdProfile profile,
+ PpdProfileActivationReason reason,
+ GError **error)
+{
+ PpdDriverAmdPstate *pstate = PPD_DRIVER_AMD_PSTATE (driver);
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (pstate->epp_devices != NULL, FALSE);
+ g_return_val_if_fail (pstate->epp_devices->len != 0, FALSE);
+
+ ret = apply_pref_to_devices (pstate->epp_devices, profile, pstate->on_battery, error);
+ if (!ret && pstate->activated_profile != PPD_PROFILE_UNSET) {
+ g_autoptr(GError) error_local = NULL;
+ /* reset back to previous */
+ if (!apply_pref_to_devices (pstate->epp_devices,
+ pstate->activated_profile,
+ pstate->on_battery,
+ &error_local))
+ g_warning ("failed to restore previous profile: %s", error_local->message);
+ return ret;
+ }
+
+ if (ret)
+ pstate->activated_profile = profile;
+
+ return ret;
+}
+
+static gboolean
+ppd_driver_amd_pstate_power_changed (PpdDriver *driver,
+ PpdPowerChangedReason reason,
+ GError **error)
+{
+ PpdDriverAmdPstate *pstate = PPD_DRIVER_AMD_PSTATE (driver);
+
+ switch (reason) {
+ case PPD_POWER_CHANGED_REASON_UNKNOWN:
+ case PPD_POWER_CHANGED_REASON_AC:
+ pstate->on_battery = FALSE;
+ break;
+ case PPD_POWER_CHANGED_REASON_BATTERY:
+ pstate->on_battery = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return apply_pref_to_devices (pstate->epp_devices,
+ pstate->activated_profile,
+ pstate->on_battery,
+ error);
+}
+
+static void
+ppd_driver_amd_pstate_finalize (GObject *object)
+{
+ PpdDriverAmdPstate *driver;
+
+ driver = PPD_DRIVER_AMD_PSTATE (object);
+ g_clear_pointer (&driver->epp_devices, g_ptr_array_unref);
+ G_OBJECT_CLASS (ppd_driver_amd_pstate_parent_class)->finalize (object);
+}
+
+static void
+ppd_driver_amd_pstate_class_init (PpdDriverAmdPstateClass *klass)
+{
+ GObjectClass *object_class;
+ PpdDriverClass *driver_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->constructor = ppd_driver_amd_pstate_constructor;
+ object_class->finalize = ppd_driver_amd_pstate_finalize;
+
+ driver_class = PPD_DRIVER_CLASS (klass);
+ driver_class->probe = ppd_driver_amd_pstate_probe;
+ driver_class->activate_profile = ppd_driver_amd_pstate_activate_profile;
+ driver_class->power_changed = ppd_driver_amd_pstate_power_changed;
+}
+
+static void
+ppd_driver_amd_pstate_init (PpdDriverAmdPstate *self)
+{
+}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-amd-pstate.h power-profiles-daemon-0.21/src/ppd-driver-amd-pstate.h
--- power-profiles-daemon-0.10.1/src/ppd-driver-amd-pstate.h 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-amd-pstate.h 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2020 Bastien Nocera
+ * Copyright (c) 2022 Prajna Sariputra
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#pragma once
+
+#include "ppd-driver-cpu.h"
+
+#define PPD_TYPE_DRIVER_AMD_PSTATE (ppd_driver_amd_pstate_get_type ())
+G_DECLARE_FINAL_TYPE (PpdDriverAmdPstate, ppd_driver_amd_pstate, PPD, DRIVER_AMD_PSTATE, PpdDriverCpu)
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-cpu.c power-profiles-daemon-0.21/src/ppd-driver-cpu.c
--- power-profiles-daemon-0.10.1/src/ppd-driver-cpu.c 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-cpu.c 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023 Mario Limonciello
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#define G_LOG_DOMAIN "CpuDriver"
+
+#include "ppd-driver-cpu.h"
+
+/**
+ * SECTION:ppd-driver-cpu
+ * @Short_description: CPU Drivers
+ * @Title: CPU Profile Drivers
+ *
+ * Profile drivers are the implementation of the different profiles for
+ * the whole system. A driver will need to implement support for `power-saver`
+ * and `balanced` at a minimum.
+ *
+ * CPU drivers are typically used to change specifically the CPU efficiency
+ * to match the desired platform state.
+ */
+
+G_DEFINE_TYPE (PpdDriverCpu, ppd_driver_cpu, PPD_TYPE_DRIVER)
+
+static void
+ppd_driver_cpu_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (ppd_driver_cpu_parent_class)->finalize (object);
+}
+
+static void
+ppd_driver_cpu_class_init (PpdDriverCpuClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = ppd_driver_cpu_finalize;
+}
+
+static void
+ppd_driver_cpu_init (PpdDriverCpu *self)
+{
+}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-cpu.h power-profiles-daemon-0.21/src/ppd-driver-cpu.h
--- power-profiles-daemon-0.10.1/src/ppd-driver-cpu.h 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-cpu.h 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 Mario Limonciello
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#pragma once
+
+#include "ppd-driver.h"
+
+#define PPD_TYPE_DRIVER_CPU (ppd_driver_cpu_get_type ())
+G_DECLARE_DERIVABLE_TYPE (PpdDriverCpu, ppd_driver_cpu, PPD, DRIVER_CPU, PpdDriver)
+
+/**
+ * PpdDriverCpuClass:
+ * @parent_class: The parent class.
+ *
+ * New CPU drivers should derive from #PpdDriverCpu and implement
+ * both @probe () and @activate_profile.
+ */
+struct _PpdDriverCpuClass
+{
+ PpdDriverClass parent_class;
+};
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-fake.c power-profiles-daemon-0.21/src/ppd-driver-fake.c
--- power-profiles-daemon-0.10.1/src/ppd-driver-fake.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-fake.c 2024-04-03 23:55:02.000000000 +0000
@@ -14,11 +14,11 @@
#include
extern void main_loop_quit (void);
-void restart_profile_drivers (void);
+void restart_profile_drivers_for_default_app (void);
struct _PpdDriverFake
{
- PpdDriver parent_instance;
+ PpdDriverPlatform parent_instance;
gboolean tio_set;
struct termios old_tio;
@@ -27,7 +27,7 @@
gboolean degraded;
};
-G_DEFINE_TYPE (PpdDriverFake, ppd_driver_fake, PPD_TYPE_DRIVER)
+G_DEFINE_TYPE (PpdDriverFake, ppd_driver_fake, PPD_TYPE_DRIVER_PLATFORM)
static GObject*
ppd_driver_fake_constructor (GType type,
@@ -88,7 +88,7 @@
break;
case 'r':
g_print ("Restarting profile drivers\n");
- restart_profile_drivers ();
+ restart_profile_drivers_for_default_app ();
break;
case 'q':
case 'x':
@@ -107,10 +107,10 @@
{
struct termios new_tio;
- tcgetattr(STDIN_FILENO, &fake->old_tio);
+ tcgetattr (STDIN_FILENO, &fake->old_tio);
new_tio = fake->old_tio;
new_tio.c_lflag &=(~ICANON & ~ECHO);
- tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
+ tcsetattr (STDIN_FILENO, TCSANOW, &new_tio);
fake->channel = g_io_channel_unix_new (STDIN_FILENO);
if (!fake->channel) {
@@ -150,6 +150,10 @@
if (!envvar_set ("POWER_PROFILE_DAEMON_FAKE_DRIVER"))
return PPD_PROBE_RESULT_FAIL;
+ /* don't activate stdin unless interactive */
+ if (isatty (fileno (stdout)) == 0)
+ return PPD_PROBE_RESULT_SUCCESS;
+
fake = PPD_DRIVER_FAKE (driver);
if (!setup_keyboard (fake))
return PPD_PROBE_RESULT_FAIL;
@@ -180,7 +184,7 @@
g_clear_pointer (&fake->channel, g_io_channel_unref);
g_clear_handle_id (&fake->watch_id, g_source_remove);
if (fake->tio_set)
- tcsetattr(STDIN_FILENO, TCSANOW, &fake->old_tio);
+ tcsetattr (STDIN_FILENO, TCSANOW, &fake->old_tio);
G_OBJECT_CLASS (ppd_driver_fake_parent_class)->finalize (object);
}
@@ -190,11 +194,11 @@
GObjectClass *object_class;
PpdDriverClass *driver_class;
- object_class = G_OBJECT_CLASS(klass);
+ object_class = G_OBJECT_CLASS (klass);
object_class->constructor = ppd_driver_fake_constructor;
object_class->finalize = ppd_driver_fake_finalize;
- driver_class = PPD_DRIVER_CLASS(klass);
+ driver_class = PPD_DRIVER_CLASS (klass);
driver_class->probe = ppd_driver_fake_probe;
driver_class->activate_profile = ppd_driver_fake_activate_profile;
}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-fake.h power-profiles-daemon-0.21/src/ppd-driver-fake.h
--- power-profiles-daemon-0.10.1/src/ppd-driver-fake.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-fake.h 2024-04-03 23:55:02.000000000 +0000
@@ -9,7 +9,7 @@
#pragma once
-#include "ppd-driver.h"
+#include "ppd-driver-platform.h"
-#define PPD_TYPE_DRIVER_FAKE (ppd_driver_fake_get_type())
-G_DECLARE_FINAL_TYPE(PpdDriverFake, ppd_driver_fake, PPD, DRIVER_FAKE, PpdDriver)
+#define PPD_TYPE_DRIVER_FAKE (ppd_driver_fake_get_type ())
+G_DECLARE_FINAL_TYPE (PpdDriverFake, ppd_driver_fake, PPD, DRIVER_FAKE, PpdDriverPlatform)
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-intel-pstate.c power-profiles-daemon-0.21/src/ppd-driver-intel-pstate.c
--- power-profiles-daemon-0.10.1/src/ppd-driver-intel-pstate.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-intel-pstate.c 2024-04-03 23:55:02.000000000 +0000
@@ -7,25 +7,37 @@
*
*/
+#define G_LOG_DOMAIN "CpuDriver"
+
#include
#include "ppd-utils.h"
#include "ppd-driver-intel-pstate.h"
+#define CPU_DIR "/sys/devices/system/cpu/"
#define CPUFREQ_POLICY_DIR "/sys/devices/system/cpu/cpufreq/"
+#define DEFAULT_CPU_FREQ_SCALING_GOV "powersave"
+#define PSTATE_STATUS_PATH "/sys/devices/system/cpu/intel_pstate/status"
#define NO_TURBO_PATH "/sys/devices/system/cpu/intel_pstate/no_turbo"
+#define TURBO_PCT_PATH "/sys/devices/system/cpu/intel_pstate/turbo_pct"
+
+#define SYSTEMD_DBUS_NAME "org.freedesktop.login1"
+#define SYSTEMD_DBUS_PATH "/org/freedesktop/login1"
+#define SYSTEMD_DBUS_INTERFACE "org.freedesktop.login1.Manager"
struct _PpdDriverIntelPstate
{
- PpdDriver parent_instance;
+ PpdDriverCpu parent_instance;
PpdProfile activated_profile;
- GList *devices; /* GList of paths */
+ GPtrArray *epp_devices; /* Array of paths */
+ GPtrArray *epb_devices; /* Array of paths */
GFileMonitor *no_turbo_mon;
char *no_turbo_path;
+ gboolean on_battery;
};
-G_DEFINE_TYPE (PpdDriverIntelPstate, ppd_driver_intel_pstate, PPD_TYPE_DRIVER)
+G_DEFINE_TYPE (PpdDriverIntelPstate, ppd_driver_intel_pstate, PPD_TYPE_DRIVER_CPU)
static gboolean ppd_driver_intel_pstate_activate_profile (PpdDriver *driver,
PpdProfile profile,
@@ -78,8 +90,12 @@
g_autofree char *path = NULL;
path = g_file_get_path (file);
- g_debug ("File monitor change happened for '%s'", path);
- update_no_turbo (pstate);
+ g_debug ("File monitor change happened for '%s' (event type %d)", path, event_type);
+
+ g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED);
+
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+ update_no_turbo (pstate);
}
static GFileMonitor *
@@ -97,31 +113,114 @@
return g_file_monitor (no_turbo, G_FILE_MONITOR_NONE, NULL, NULL);
}
-static GDir *
-open_policy_dir (void)
+static gboolean
+sys_has_turbo (void)
{
- g_autofree char *dir = NULL;
- dir = ppd_utils_get_sysfs_path (CPUFREQ_POLICY_DIR);
- g_debug ("Opening policy dir '%s'", dir);
- return g_dir_open (dir, 0, NULL);
+ g_autofree char *turbo_pct_path = NULL;
+ g_autofree char *contents = NULL;
+ gboolean has_turbo = TRUE;
+
+ turbo_pct_path = ppd_utils_get_sysfs_path (TURBO_PCT_PATH);
+ if (g_file_get_contents (turbo_pct_path, &contents, NULL, NULL)) {
+ contents = g_strchomp (contents);
+ if (g_strcmp0 (contents, "0") == 0)
+ has_turbo = FALSE;
+ }
+
+ return has_turbo;
}
static gboolean
-ppd_driver_intel_pstate_probe (PpdDriver *driver)
+ppd_driver_intel_pstate_prepare_for_sleep (PpdDriver *driver,
+ gboolean start,
+ GError **error)
{
PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
+ g_autoptr(GError) local_error = NULL;
+
+ if (start)
+ return TRUE;
+
+ g_debug ("Re-applying energy_perf_bias");
+ if (!ppd_driver_intel_pstate_activate_profile (PPD_DRIVER (pstate),
+ pstate->activated_profile,
+ PPD_PROFILE_ACTIVATION_REASON_RESUME,
+ &local_error)) {
+ g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
+ "Could not reapply energy_perf_bias preference on resume: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static PpdProbeResult
+probe_epb (PpdDriverIntelPstate *pstate)
+{
g_autoptr(GDir) dir = NULL;
g_autofree char *policy_dir = NULL;
const char *dirname;
- PpdProbeResult ret = PPD_PROBE_RESULT_FAIL;
- dir = open_policy_dir ();
- if (!dir)
- goto out;
+ policy_dir = ppd_utils_get_sysfs_path (CPU_DIR);
+ dir = g_dir_open (policy_dir, 0, NULL);
+ if (!dir) {
+ g_debug ("Could not open %s", CPU_DIR);
+ return PPD_PROBE_RESULT_FAIL;
+ }
+
+ while ((dirname = g_dir_read_name (dir)) != NULL) {
+ g_autofree char *path = NULL;
+
+ path = g_build_filename (policy_dir,
+ dirname,
+ "power",
+ "energy_perf_bias",
+ NULL);
+ if (!g_file_test (path, G_FILE_TEST_EXISTS))
+ continue;
+
+ if (!pstate->epb_devices)
+ pstate->epb_devices = g_ptr_array_new_with_free_func (g_free);
+
+ g_ptr_array_add (pstate->epb_devices, g_steal_pointer (&path));
+ }
+
+ if (pstate->epb_devices && pstate->epb_devices->len)
+ return PPD_PROBE_RESULT_SUCCESS;
+
+ return PPD_PROBE_RESULT_FAIL;
+}
+
+static PpdProbeResult
+probe_epp (PpdDriverIntelPstate *pstate)
+{
+ g_autoptr(GDir) dir = NULL;
+ g_autofree char *policy_dir = NULL;
+ g_autofree char *pstate_status_path = NULL;
+ g_autofree char *status = NULL;
+ const char *dirname;
+
+ /* Verify that Intel P-State is running in active mode */
+ pstate_status_path = ppd_utils_get_sysfs_path (PSTATE_STATUS_PATH);
+ if (!g_file_get_contents (pstate_status_path, &status, NULL, NULL))
+ return PPD_PROBE_RESULT_FAIL;
+ status = g_strchomp (status);
+ if (g_strcmp0 (status, "active") != 0) {
+ g_debug ("Intel P-State is running in passive mode");
+ return PPD_PROBE_RESULT_FAIL;
+ }
policy_dir = ppd_utils_get_sysfs_path (CPUFREQ_POLICY_DIR);
+ dir = g_dir_open (policy_dir, 0, NULL);
+ if (!dir) {
+ g_debug ("Could not open %s", policy_dir);
+ return PPD_PROBE_RESULT_FAIL;
+ }
+
while ((dirname = g_dir_read_name (dir)) != NULL) {
g_autofree char *path = NULL;
+ g_autofree char *gov_path = NULL;
+ g_autoptr(GError) error = NULL;
path = g_build_filename (policy_dir,
dirname,
@@ -130,30 +229,70 @@
if (!g_file_test (path, G_FILE_TEST_EXISTS))
continue;
- pstate->devices = g_list_prepend (pstate->devices, g_steal_pointer (&path));
- ret = PPD_PROBE_RESULT_SUCCESS;
+ /* Force a scaling_governor where the preference can be written */
+ gov_path = g_build_filename (policy_dir,
+ dirname,
+ "scaling_governor",
+ NULL);
+ if (!ppd_utils_write (gov_path, DEFAULT_CPU_FREQ_SCALING_GOV, &error)) {
+ g_warning ("Could not change scaling governor %s to '%s'", dirname, DEFAULT_CPU_FREQ_SCALING_GOV);
+ continue;
+ }
+
+ if (!pstate->epp_devices)
+ pstate->epp_devices = g_ptr_array_new_with_free_func (g_free);
+
+ g_ptr_array_add (pstate->epp_devices, g_steal_pointer (&path));
}
+ if (pstate->epp_devices && pstate->epp_devices->len)
+ return PPD_PROBE_RESULT_SUCCESS;
+
+ return PPD_PROBE_RESULT_FAIL;
+}
+
+static PpdProbeResult
+ppd_driver_intel_pstate_probe (PpdDriver *driver)
+{
+ PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
+ PpdProbeResult ret = PPD_PROBE_RESULT_FAIL;
+ PpdProbeResult epp_ret, epb_ret;
+ gboolean has_turbo;
+
+ epp_ret = probe_epp (pstate);
+ epb_ret = probe_epb (pstate);
+ ret = (epp_ret == PPD_PROBE_RESULT_SUCCESS) ? epp_ret : epb_ret;
+
if (ret != PPD_PROBE_RESULT_SUCCESS)
goto out;
- /* Monitor the first "no_turbo" */
- pstate->no_turbo_path = ppd_utils_get_sysfs_path (NO_TURBO_PATH);
- pstate->no_turbo_mon = monitor_no_turbo_prop (pstate->no_turbo_path);
- if (pstate->no_turbo_mon) {
- g_signal_connect (G_OBJECT (pstate->no_turbo_mon), "changed",
- G_CALLBACK (no_turbo_changed), pstate);
+ has_turbo = sys_has_turbo ();
+ if (has_turbo) {
+ /* Monitor the first "no_turbo" */
+ pstate->no_turbo_path = ppd_utils_get_sysfs_path (NO_TURBO_PATH);
+ pstate->no_turbo_mon = monitor_no_turbo_prop (pstate->no_turbo_path);
+ if (pstate->no_turbo_mon) {
+ g_signal_connect_object (G_OBJECT (pstate->no_turbo_mon), "changed",
+ G_CALLBACK (no_turbo_changed), pstate, 0);
+ }
+ update_no_turbo (pstate);
}
- update_no_turbo (pstate);
out:
- g_debug ("%s p-state settings",
+ g_debug ("%s Intel p-state settings",
ret == PPD_PROBE_RESULT_SUCCESS ? "Found" : "Didn't find");
+ if (ret == PPD_PROBE_RESULT_SUCCESS) {
+ g_debug ("\tEnergy Performance Preference: %s",
+ epp_ret == PPD_PROBE_RESULT_SUCCESS ? "yes" : "no");
+ g_debug ("\tEnergy Performance Bias: %s",
+ epp_ret == PPD_PROBE_RESULT_SUCCESS ? "yes" : "no");
+ g_debug ("\tHas Turbo: %s", has_turbo ? "yes" : "no");
+ }
return ret;
}
static const char *
-profile_to_pref (PpdProfile profile)
+profile_to_epp_pref (PpdProfile profile, gboolean battery)
{
/* Note that we don't check "energy_performance_available_preferences"
* as all the values are always available */
@@ -161,41 +300,96 @@
case PPD_PROFILE_POWER_SAVER:
return "power";
case PPD_PROFILE_BALANCED:
- return "balance_performance";
+ return battery ? "balance_power" : "balance_performance";
case PPD_PROFILE_PERFORMANCE:
return "performance";
}
- g_assert_not_reached ();
+ g_return_val_if_reached (NULL);
+}
+
+static const char *
+profile_to_epb_pref (PpdProfile profile, gboolean battery)
+{
+ /* From arch/x86/include/asm/msr-index.h
+ * See ENERGY_PERF_BIAS_* */
+ switch (profile) {
+ case PPD_PROFILE_POWER_SAVER:
+ return "15";
+ case PPD_PROFILE_BALANCED:
+ return battery ? "8" : "6";
+ case PPD_PROFILE_PERFORMANCE:
+ return "0";
+ }
+
+ g_return_val_if_reached (NULL);
}
static gboolean
-ppd_driver_intel_pstate_activate_profile (PpdDriver *driver,
- PpdProfile profile,
- PpdProfileActivationReason reason,
- GError **error)
+apply_pref_to_devices (PpdDriver *driver,
+ PpdProfile profile,
+ GError **error)
{
PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
- gboolean ret = TRUE;
- const char *pref;
- GList *l;
- g_return_val_if_fail (pstate->devices != NULL, FALSE);
+ if (profile == PPD_PROFILE_UNSET)
+ return TRUE;
- pref = profile_to_pref (profile);
+ g_return_val_if_fail (pstate->epp_devices != NULL ||
+ pstate->epb_devices != NULL, FALSE);
+ g_return_val_if_fail ((pstate->epp_devices && pstate->epp_devices->len != 0) ||
+ (pstate->epb_devices && pstate->epb_devices->len != 0), FALSE);
- for (l = pstate->devices; l != NULL; l = l->next) {
- const char *path = l->data;
+ if (pstate->epp_devices) {
+ const char *epp_pref = profile_to_epp_pref (profile, pstate->on_battery);
- ret = ppd_utils_write (path, pref, error);
- if (!ret)
- break;
+ if (!ppd_utils_write_files (pstate->epp_devices, epp_pref, error))
+ return FALSE;
}
- if (ret)
- pstate->activated_profile = profile;
+ if (pstate->epb_devices) {
+ const char *epb_pref = profile_to_epb_pref (profile, pstate->on_battery);
- return ret;
+ if (!ppd_utils_write_files (pstate->epb_devices, epb_pref, error))
+ return FALSE;
+ }
+
+ pstate->activated_profile = profile;
+
+ return TRUE;
+}
+
+static gboolean
+ppd_driver_intel_pstate_power_changed (PpdDriver *driver,
+ PpdPowerChangedReason reason,
+ GError **error)
+{
+ PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
+
+ switch (reason) {
+ case PPD_POWER_CHANGED_REASON_UNKNOWN:
+ case PPD_POWER_CHANGED_REASON_AC:
+ pstate->on_battery = FALSE;
+ break;
+ case PPD_POWER_CHANGED_REASON_BATTERY:
+ pstate->on_battery = TRUE;
+ break;
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ return apply_pref_to_devices (driver,
+ pstate->activated_profile,
+ error);
+}
+
+static gboolean
+ppd_driver_intel_pstate_activate_profile (PpdDriver *driver,
+ PpdProfile profile,
+ PpdProfileActivationReason reason,
+ GError **error)
+{
+ return apply_pref_to_devices (driver, profile, error);
}
static void
@@ -204,7 +398,9 @@
PpdDriverIntelPstate *driver;
driver = PPD_DRIVER_INTEL_PSTATE (object);
- g_clear_list (&driver->devices, g_free);
+
+ g_clear_pointer (&driver->epp_devices, g_ptr_array_unref);
+ g_clear_pointer (&driver->epb_devices, g_ptr_array_unref);
g_clear_pointer (&driver->no_turbo_path, g_free);
g_clear_object (&driver->no_turbo_mon);
G_OBJECT_CLASS (ppd_driver_intel_pstate_parent_class)->finalize (object);
@@ -216,13 +412,15 @@
GObjectClass *object_class;
PpdDriverClass *driver_class;
- object_class = G_OBJECT_CLASS(klass);
+ object_class = G_OBJECT_CLASS (klass);
object_class->constructor = ppd_driver_intel_pstate_constructor;
object_class->finalize = ppd_driver_intel_pstate_finalize;
- driver_class = PPD_DRIVER_CLASS(klass);
+ driver_class = PPD_DRIVER_CLASS (klass);
driver_class->probe = ppd_driver_intel_pstate_probe;
driver_class->activate_profile = ppd_driver_intel_pstate_activate_profile;
+ driver_class->prepare_to_sleep = ppd_driver_intel_pstate_prepare_for_sleep;
+ driver_class->power_changed = ppd_driver_intel_pstate_power_changed;
}
static void
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-intel-pstate.h power-profiles-daemon-0.21/src/ppd-driver-intel-pstate.h
--- power-profiles-daemon-0.10.1/src/ppd-driver-intel-pstate.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-intel-pstate.h 2024-04-03 23:55:02.000000000 +0000
@@ -9,7 +9,7 @@
#pragma once
-#include "ppd-driver.h"
+#include "ppd-driver-cpu.h"
-#define PPD_TYPE_DRIVER_INTEL_PSTATE (ppd_driver_intel_pstate_get_type())
-G_DECLARE_FINAL_TYPE(PpdDriverIntelPstate, ppd_driver_intel_pstate, PPD, DRIVER_INTEL_PSTATE, PpdDriver)
+#define PPD_TYPE_DRIVER_INTEL_PSTATE (ppd_driver_intel_pstate_get_type ())
+G_DECLARE_FINAL_TYPE (PpdDriverIntelPstate, ppd_driver_intel_pstate, PPD, DRIVER_INTEL_PSTATE, PpdDriverCpu)
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-placeholder.c power-profiles-daemon-0.21/src/ppd-driver-placeholder.c
--- power-profiles-daemon-0.10.1/src/ppd-driver-placeholder.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-placeholder.c 2024-04-03 23:55:02.000000000 +0000
@@ -7,14 +7,16 @@
*
*/
+#define G_LOG_DOMAIN "PlatformDriver"
+
#include "ppd-driver-placeholder.h"
struct _PpdDriverPlaceholder
{
- PpdDriver parent_instance;
+ PpdDriverPlatform parent_instance;
};
-G_DEFINE_TYPE (PpdDriverPlaceholder, ppd_driver_placeholder, PPD_TYPE_DRIVER)
+G_DEFINE_TYPE (PpdDriverPlaceholder, ppd_driver_placeholder, PPD_TYPE_DRIVER_PLATFORM)
static GObject*
ppd_driver_placeholder_constructor (GType type,
@@ -39,7 +41,7 @@
{
GObjectClass *object_class;
- object_class = G_OBJECT_CLASS(klass);
+ object_class = G_OBJECT_CLASS (klass);
object_class->constructor = ppd_driver_placeholder_constructor;
}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-placeholder.h power-profiles-daemon-0.21/src/ppd-driver-placeholder.h
--- power-profiles-daemon-0.10.1/src/ppd-driver-placeholder.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-placeholder.h 2024-04-03 23:55:02.000000000 +0000
@@ -9,7 +9,7 @@
#pragma once
-#include "ppd-driver.h"
+#include "ppd-driver-platform.h"
-#define PPD_TYPE_DRIVER_PLACEHOLDER (ppd_driver_placeholder_get_type())
-G_DECLARE_FINAL_TYPE(PpdDriverPlaceholder, ppd_driver_placeholder, PPD, DRIVER_PLACEHOLDER, PpdDriver)
+#define PPD_TYPE_DRIVER_PLACEHOLDER (ppd_driver_placeholder_get_type ())
+G_DECLARE_FINAL_TYPE (PpdDriverPlaceholder, ppd_driver_placeholder, PPD, DRIVER_PLACEHOLDER, PpdDriverPlatform)
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-platform-profile.c power-profiles-daemon-0.21/src/ppd-driver-platform-profile.c
--- power-profiles-daemon-0.10.1/src/ppd-driver-platform-profile.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-platform-profile.c 2024-04-03 23:55:02.000000000 +0000
@@ -7,6 +7,8 @@
*
*/
+#define G_LOG_DOMAIN "PlatformDriver"
+
#include
#include
@@ -26,12 +28,13 @@
int lapmode;
PpdProfile acpi_platform_profile;
char **profile_choices;
+ gboolean has_low_power;
GFileMonitor *lapmode_mon;
GFileMonitor *acpi_platform_profile_mon;
- guint acpi_platform_profile_changed_id;
+ gulong acpi_platform_profile_changed_id;
};
-G_DEFINE_TYPE (PpdDriverPlatformProfile, ppd_driver_platform_profile, PPD_TYPE_DRIVER)
+G_DEFINE_TYPE (PpdDriverPlatformProfile, ppd_driver_platform_profile, PPD_TYPE_DRIVER_PLATFORM)
static GObject*
ppd_driver_platform_profile_constructor (GType type,
@@ -57,10 +60,10 @@
{
switch (profile) {
case PPD_PROFILE_POWER_SAVER:
+ if (!self->has_low_power)
+ return "balanced";
if (g_strv_contains ((const char * const*) self->profile_choices, "low-power"))
return "low-power";
- if (g_strv_contains ((const char * const*) self->profile_choices, "cool"))
- return "cool";
return "quiet";
case PPD_PROFILE_BALANCED:
return "balanced";
@@ -68,7 +71,8 @@
return "performance";
}
- g_assert_not_reached ();
+ g_debug ("Unhandled ACPI platform profile '%d'", profile);
+ g_return_val_if_reached (NULL);
}
static PpdProfile
@@ -79,18 +83,17 @@
switch (str[0]) {
case 'l': /* low-power */
- case 'c': /* cool */
case 'q': /* quiet */
return PPD_PROFILE_POWER_SAVER;
+ case 'c': /* cool */
case 'b':
return PPD_PROFILE_BALANCED;
case 'p':
return PPD_PROFILE_PERFORMANCE;
default:
- g_debug ("Got unsupported performance_profile value '%s'", str);
+ g_debug ("Unhandled ACPI platform profile '%c'", str[0]);
+ g_return_val_if_reached (PPD_PROFILE_UNSET);
}
-
- return PPD_PROFILE_UNSET;
}
static PpdProfile
@@ -142,12 +145,15 @@
{
const char * const *choices = (const char * const*) self->profile_choices;
- if ((g_strv_contains (choices, "low-power") ||
- g_strv_contains (choices, "cool") ||
- g_strv_contains (choices, "quiet")) &&
- g_strv_contains (choices, "balanced") &&
- g_strv_contains (choices, "performance"))
+ if (g_strv_contains (choices, "balanced") &&
+ g_strv_contains (choices, "performance")) {
+ if (g_strv_contains (choices, "low-power") ||
+ g_strv_contains (choices, "quiet"))
+ self->has_low_power = TRUE;
+ else
+ g_debug ("No \"low-power\" profile for device, will be emulated");
return PPD_PROBE_RESULT_SUCCESS;
+ }
return PPD_PROBE_RESULT_DEFER;
}
@@ -191,8 +197,12 @@
gpointer user_data)
{
PpdDriverPlatformProfile *self = user_data;
- g_debug (LAPMODE_SYSFS_NAME " attribute changed");
- update_dytc_lapmode_state (self);
+
+ g_debug (LAPMODE_SYSFS_NAME " attribute changed (event: %d)", event_type);
+ g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED);
+
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+ update_dytc_lapmode_state (self);
}
static void
@@ -203,12 +213,17 @@
gpointer user_data)
{
PpdDriverPlatformProfile *self = user_data;
- g_debug (ACPI_PLATFORM_PROFILE_PATH " changed");
+
+ g_debug (ACPI_PLATFORM_PROFILE_PATH " changed (%d)", event_type);
if (self->probe_result == PPD_PROBE_RESULT_DEFER) {
g_signal_emit_by_name (G_OBJECT (self), "probe-request", 0);
return;
}
- update_acpi_platform_profile_state (self);
+
+ g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED);
+
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+ update_acpi_platform_profile_state (self);
}
static gboolean
@@ -218,7 +233,9 @@
GError **error)
{
PpdDriverPlatformProfile *self = PPD_DRIVER_PLATFORM_PROFILE (driver);
+ g_autoptr(GError) local_error = NULL;
g_autofree char *platform_profile_path = NULL;
+ const char *platform_profile_value;
g_return_val_if_fail (self->acpi_platform_profile_mon, FALSE);
@@ -228,10 +245,21 @@
return TRUE;
}
+ platform_profile_value = profile_to_acpi_platform_profile_value (self, profile);
+ if (self->acpi_platform_profile == acpi_platform_profile_value_to_profile (platform_profile_value)) {
+ g_debug ("Not switching to platform_profile %s, emulating for %s, already there",
+ platform_profile_value,
+ ppd_profile_to_str (profile));
+ return TRUE;
+ }
+
g_signal_handler_block (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id);
platform_profile_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_PATH);
- if (!ppd_utils_write (platform_profile_path, profile_to_acpi_platform_profile_value (self, profile), error)) {
- g_debug ("Failed to write to acpi_platform_profile: %s", (* error)->message);
+ if (!ppd_utils_write (platform_profile_path,
+ profile_to_acpi_platform_profile_value (self, profile), &local_error)) {
+ g_debug ("Failed to write to acpi_platform_profile: %s", local_error->message);
+ g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
+ "Failed to write to acpi_platform_profile: ");
g_signal_handler_unblock (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id);
return FALSE;
}
@@ -301,8 +329,8 @@
self->lapmode_mon = ppd_utils_monitor_sysfs_attr (self->device,
LAPMODE_SYSFS_NAME,
NULL);
- g_signal_connect (G_OBJECT (self->lapmode_mon), "changed",
- G_CALLBACK (lapmode_changed), self);
+ g_signal_connect_object (G_OBJECT (self->lapmode_mon), "changed",
+ G_CALLBACK (lapmode_changed), self, 0);
update_dytc_lapmode_state (self);
out:
@@ -319,6 +347,8 @@
PpdDriverPlatformProfile *driver;
driver = PPD_DRIVER_PLATFORM_PROFILE (object);
+ g_clear_signal_handler (&driver->acpi_platform_profile_changed_id,
+ driver->acpi_platform_profile_mon);
g_clear_pointer (&driver->profile_choices, g_strfreev);
g_clear_object (&driver->device);
g_clear_object (&driver->lapmode_mon);
@@ -332,11 +362,11 @@
GObjectClass *object_class;
PpdDriverClass *driver_class;
- object_class = G_OBJECT_CLASS(klass);
+ object_class = G_OBJECT_CLASS (klass);
object_class->constructor = ppd_driver_platform_profile_constructor;
object_class->finalize = ppd_driver_platform_profile_finalize;
- driver_class = PPD_DRIVER_CLASS(klass);
+ driver_class = PPD_DRIVER_CLASS (klass);
driver_class->probe = ppd_driver_platform_profile_probe;
driver_class->activate_profile = ppd_driver_platform_profile_activate_profile;
}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-platform-profile.h power-profiles-daemon-0.21/src/ppd-driver-platform-profile.h
--- power-profiles-daemon-0.10.1/src/ppd-driver-platform-profile.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-platform-profile.h 2024-04-03 23:55:02.000000000 +0000
@@ -9,7 +9,7 @@
#pragma once
-#include "ppd-driver.h"
+#include "ppd-driver-platform.h"
-#define PPD_TYPE_DRIVER_PLATFORM_PROFILE (ppd_driver_platform_profile_get_type())
-G_DECLARE_FINAL_TYPE(PpdDriverPlatformProfile, ppd_driver_platform_profile, PPD, DRIVER_PLATFORM_PROFILE, PpdDriver)
+#define PPD_TYPE_DRIVER_PLATFORM_PROFILE (ppd_driver_platform_profile_get_type ())
+G_DECLARE_FINAL_TYPE (PpdDriverPlatformProfile, ppd_driver_platform_profile, PPD, DRIVER_PLATFORM_PROFILE, PpdDriverPlatform)
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-platform.c power-profiles-daemon-0.21/src/ppd-driver-platform.c
--- power-profiles-daemon-0.10.1/src/ppd-driver-platform.c 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-platform.c 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 Mario Limonciello
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#define G_LOG_DOMAIN "PlatformDriver"
+
+#include "ppd-driver-platform.h"
+
+G_DEFINE_TYPE (PpdDriverPlatform, ppd_driver_platform, PPD_TYPE_DRIVER)
+
+/**
+ * SECTION:ppd-driver-platform
+ * @Short_description: Profile Drivers
+ * @Title: Platform Profile Drivers
+ *
+ * Platform drivers are the implementation of the different profiles for
+ * the whole system. A driver will need to implement support for `power-saver`
+ * and `balanced` at a minimum.
+ *
+ * If no system-specific platform driver is available, a placeholder driver
+ * will be put in place, and the `performance` profile will be unavailable.
+ *
+ * There should not be a need to implement system-specific drivers, as the
+ * [`platform_profile`] (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-platform_profile)
+ * kernel API offers a way to implement system-specific profiles which
+ * `power-profiles-daemon` can consume.
+ *
+ * When a driver implements the `performance` profile, it might set the
+ * #PpdDriver:performance-degraded property if the profile isn't running to
+ * its fullest performance for any reason, such as thermal limits being
+ * reached, or because a part of the user's body is too close for safety,
+ * for example.
+ */
+
+static void
+ppd_driver_platform_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (ppd_driver_platform_parent_class)->finalize (object);
+}
+
+static void
+ppd_driver_platform_class_init (PpdDriverPlatformClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = ppd_driver_platform_finalize;
+}
+
+static void
+ppd_driver_platform_init (PpdDriverPlatform *self)
+{
+}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver-platform.h power-profiles-daemon-0.21/src/ppd-driver-platform.h
--- power-profiles-daemon-0.10.1/src/ppd-driver-platform.h 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver-platform.h 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 Mario Limonciello
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#pragma once
+
+#include "ppd-driver.h"
+
+#define PPD_TYPE_DRIVER_PLATFORM (ppd_driver_platform_get_type ())
+G_DECLARE_DERIVABLE_TYPE (PpdDriverPlatform, ppd_driver_platform, PPD, DRIVER_PLATFORM, PpdDriver)
+
+/**
+ * PpdDriverPlatformClass:
+ * @parent_class: The parent class.
+ *
+ * New Platform drivers should derive from #PpdDriverPlatform and implement
+ * at least one of @probe () and @activate_profile.
+ */
+struct _PpdDriverPlatformClass
+{
+ PpdDriverClass parent_class;
+};
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver.c power-profiles-daemon-0.21/src/ppd-driver.c
--- power-profiles-daemon-0.10.1/src/ppd-driver.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver.c 2024-04-03 23:55:02.000000000 +0000
@@ -19,13 +19,7 @@
* the whole system. A driver will need to implement support `power-saver`
* and `balanced` at a minimum.
*
- * If no system-specific driver is available, a placeholder driver
- * will be put in place, and the `performance` profile will be unavailable.
- *
- * There should not be a need to implement system-specific drivers, as the
- * [`platform_profile`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-platform_profile)
- * kernel API offers a way to implement system-specific profiles which
- * `power-profiles-daemon` can consume.
+ * All drivers should be derived from either #PpdDriverCpu or #PpdDriverPlatform
*
* When a driver implements the `performance` profile, it might set the
* #PpdDriver:performance-degraded property if the profile isn't running to
@@ -69,20 +63,28 @@
PpdDriver *driver = PPD_DRIVER (object);
PpdDriverPrivate *priv = PPD_DRIVER_GET_PRIVATE (driver);
+ g_return_if_fail (PPD_IS_DRIVER (object));
+
switch (property_id) {
case PROP_DRIVER_NAME:
- g_assert (priv->driver_name == NULL);
+ g_return_if_fail (priv->driver_name == NULL);
priv->driver_name = g_value_dup_string (value);
break;
case PROP_PROFILES:
priv->profiles = g_value_get_flags (value);
break;
case PROP_PERFORMANCE_DEGRADED:
- g_clear_pointer (&priv->performance_degraded, g_free);
- priv->performance_degraded = g_value_dup_string (value);
+ {
+ const char *degraded = g_value_get_string (value);
+
+ g_clear_pointer (&priv->performance_degraded, g_free);
+
+ if (degraded != NULL && *degraded != '\0')
+ priv->performance_degraded = g_strdup (degraded);
+ }
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
@@ -106,7 +108,7 @@
g_value_set_string (value, priv->performance_degraded);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
@@ -127,7 +129,7 @@
{
GObjectClass *object_class;
- object_class = G_OBJECT_CLASS(klass);
+ object_class = G_OBJECT_CLASS (klass);
object_class->finalize = ppd_driver_finalize;
object_class->get_property = ppd_driver_get_property;
object_class->set_property = ppd_driver_set_property;
@@ -173,7 +175,7 @@
* A unique driver name, only used for debugging.
*/
g_object_class_install_property (object_class, PROP_DRIVER_NAME,
- g_param_spec_string("driver-name",
+ g_param_spec_string ("driver-name",
"Driver name",
"Profile driver name",
NULL,
@@ -185,21 +187,20 @@
* The bitmask of #PpdProfiles implemented by this driver.
*/
g_object_class_install_property (object_class, PROP_PROFILES,
- g_param_spec_flags("profiles",
+ g_param_spec_flags ("profiles",
"Profiles",
"Profiles implemented by this driver",
PPD_TYPE_PROFILE,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
/**
- * PpdDriver:performance-degraded:
+ * PpdPlatformDriver:performance-degraded:
*
* If set to a non-%NULL value, the reason why the performance profile is unavailable.
* The value must be one of the options listed in the D-Bus API reference.
*/
g_object_class_install_property (object_class, PROP_PERFORMANCE_DEGRADED,
- g_param_spec_string("performance-degraded",
+ g_param_spec_string ("performance-degraded",
"Performance Degraded",
"Why the performance profile is degraded, if set",
NULL,
@@ -237,6 +238,32 @@
return PPD_DRIVER_GET_CLASS (driver)->activate_profile (driver, profile, reason, error);
}
+gboolean
+ppd_driver_power_changed (PpdDriver *driver,
+ PpdPowerChangedReason reason,
+ GError **error)
+{
+ g_return_val_if_fail (PPD_IS_DRIVER (driver), FALSE);
+
+ if (!PPD_DRIVER_GET_CLASS (driver)->power_changed)
+ return TRUE;
+
+ return PPD_DRIVER_GET_CLASS (driver)->power_changed (driver, reason, error);
+}
+
+gboolean
+ppd_driver_prepare_to_sleep (PpdDriver *driver,
+ gboolean start,
+ GError **error)
+{
+ g_return_val_if_fail (PPD_IS_DRIVER (driver), FALSE);
+
+ if (!PPD_DRIVER_GET_CLASS (driver)->prepare_to_sleep)
+ return TRUE;
+
+ return PPD_DRIVER_GET_CLASS (driver)->prepare_to_sleep (driver, start, error);
+}
+
const char *
ppd_driver_get_driver_name (PpdDriver *driver)
{
@@ -278,7 +305,7 @@
g_return_val_if_fail (PPD_IS_DRIVER (driver), NULL);
priv = PPD_DRIVER_GET_PRIVATE (driver);
- return priv->performance_degraded ? priv->performance_degraded : "";
+ return priv->performance_degraded;
}
gboolean
@@ -315,9 +342,11 @@
return "reset";
case PPD_PROFILE_ACTIVATION_REASON_USER:
return "user";
+ case PPD_PROFILE_ACTIVATION_REASON_RESUME:
+ return "resume";
case PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD:
return "program-hold";
default:
- g_assert_not_reached ();
+ g_return_val_if_reached (NULL);
}
}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-driver.h power-profiles-daemon-0.21/src/ppd-driver.h
--- power-profiles-daemon-0.10.1/src/ppd-driver.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-driver.h 2024-04-03 23:55:02.000000000 +0000
@@ -12,26 +12,8 @@
#include
#include "ppd-profile.h"
-#define PPD_TYPE_DRIVER (ppd_driver_get_type())
-G_DECLARE_DERIVABLE_TYPE(PpdDriver, ppd_driver, PPD, DRIVER, GObject)
-
-/**
- * PpdProbeResult:
- * @PPD_PROBE_RESULT_UNSET: unset
- * @PPD_PROBE_RESULT_DEFER: driver should be kept alive, as kernel
- * support might appear.
- * @PPD_PROBE_RESULT_FAIL: driver failed to load.
- * @PPD_PROBE_RESULT_SUCCESS: driver successfully loaded.
- *
- * Those are the three possible values returned by a driver probe,
- * along with an unset value for convenience.
- */
-typedef enum {
- PPD_PROBE_RESULT_UNSET = -2,
- PPD_PROBE_RESULT_DEFER = -1,
- PPD_PROBE_RESULT_FAIL = 0,
- PPD_PROBE_RESULT_SUCCESS = 1
-} PpdProbeResult;
+#define PPD_TYPE_DRIVER (ppd_driver_get_type ())
+G_DECLARE_DERIVABLE_TYPE (PpdDriver, ppd_driver, PPD, DRIVER, GObject)
/**
* PpdProfileActivationReason:
@@ -41,6 +23,8 @@
* because drivers are getting reprobed.
* @PPD_PROFILE_ACTIVATION_REASON_USER: setting profile because the user
* requested it.
+ * @PPD_PROFILE_ACTIVATION_REASON_RESUME: setting profile because preference
+ * is lost during suspend.
* @PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD: setting profile because a program
* requested it through the `HoldProfile` method.
*
@@ -52,6 +36,7 @@
PPD_PROFILE_ACTIVATION_REASON_INTERNAL = 0,
PPD_PROFILE_ACTIVATION_REASON_RESET,
PPD_PROFILE_ACTIVATION_REASON_USER,
+ PPD_PROFILE_ACTIVATION_REASON_RESUME,
PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD
} PpdProfileActivationReason;
@@ -60,9 +45,11 @@
* @parent_class: The parent class.
* @probe: Called by the daemon on startup.
* @activate_profile: Called by the daemon for every profile change.
+ * @power_changed: Called by the daemon when power adapter status changes
*
- * New profile drivers should derive from #PpdDriver and implement
- * at least one of probe() and @activate_profile.
+ * New profile drivers should not derive from #PpdDriver. They should
+ * derive from the child from #PpdDriverCpu or #PpdDriverPlatform drivers
+ * and implement at least one of probe () and @activate_profile.
*/
struct _PpdDriverClass
{
@@ -73,12 +60,20 @@
PpdProfile profile,
PpdProfileActivationReason reason,
GError **error);
+ gboolean (* power_changed) (PpdDriver *driver,
+ PpdPowerChangedReason reason,
+ GError **error);
+ gboolean (* prepare_to_sleep) (PpdDriver *driver,
+ gboolean start,
+ GError **error);
};
#ifndef __GTK_DOC_IGNORE__
PpdProbeResult ppd_driver_probe (PpdDriver *driver);
gboolean ppd_driver_activate_profile (PpdDriver *driver,
PpdProfile profile, PpdProfileActivationReason reason, GError **error);
+gboolean ppd_driver_power_changed (PpdDriver *driver, PpdPowerChangedReason reason, GError **error);
+gboolean ppd_driver_prepare_to_sleep (PpdDriver *driver, gboolean start, GError **error);
const char *ppd_driver_get_driver_name (PpdDriver *driver);
PpdProfile ppd_driver_get_profiles (PpdDriver *driver);
const char *ppd_driver_get_performance_degraded (PpdDriver *driver);
diff -Nru power-profiles-daemon-0.10.1/src/ppd-profile.c power-profiles-daemon-0.21/src/ppd-profile.c
--- power-profiles-daemon-0.10.1/src/ppd-profile.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-profile.c 2024-04-03 23:55:02.000000000 +0000
@@ -13,31 +13,37 @@
const char *
ppd_profile_to_str (PpdProfile profile)
{
- GFlagsClass *klass = g_type_class_ref (PPD_TYPE_PROFILE);
+ g_autoptr(GFlagsClass) klass = g_type_class_ref (PPD_TYPE_PROFILE);
GFlagsValue *value = g_flags_get_first_value (klass, profile);
const gchar *name = value ? value->value_nick : "";
- g_type_class_unref (klass);
return name;
}
PpdProfile
ppd_profile_from_str (const char *str)
{
- GFlagsClass *klass = g_type_class_ref (PPD_TYPE_PROFILE);
+ g_autoptr(GFlagsClass) klass = g_type_class_ref (PPD_TYPE_PROFILE);
GFlagsValue *value = g_flags_get_value_by_nick (klass, str);
PpdProfile profile = value ? value->value : PPD_PROFILE_UNSET;
- g_type_class_unref (klass);
return profile;
}
gboolean
ppd_profile_has_single_flag (PpdProfile profile)
{
- GFlagsClass *klass = g_type_class_ref (PPD_TYPE_PROFILE);
+ g_autoptr(GFlagsClass) klass = g_type_class_ref (PPD_TYPE_PROFILE);
GFlagsValue *value = g_flags_get_first_value (klass, profile);
- gboolean ret = FALSE;
if (value && value->value == profile)
- ret = TRUE;
- g_type_class_unref (klass);
- return ret;
+ return TRUE;
+
+ return FALSE;
+}
+
+const char *
+ppd_power_changed_reason_to_str (PpdPowerChangedReason reason)
+{
+ g_autoptr(GEnumClass) klass = g_type_class_ref (PPD_TYPE_POWER_CHANGED_REASON);
+ GEnumValue *value = g_enum_get_value (klass, reason);
+ const gchar *name = value ? value->value_nick : "";
+ return name;
}
diff -Nru power-profiles-daemon-0.10.1/src/ppd-profile.h power-profiles-daemon-0.21/src/ppd-profile.h
--- power-profiles-daemon-0.10.1/src/ppd-profile.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-profile.h 2024-04-03 23:55:02.000000000 +0000
@@ -12,6 +12,24 @@
#include
/**
+ * PpdProbeResult:
+ * @PPD_PROBE_RESULT_UNSET: unset
+ * @PPD_PROBE_RESULT_DEFER: driver should be kept alive, as kernel
+ * support might appear.
+ * @PPD_PROBE_RESULT_FAIL: driver failed to load.
+ * @PPD_PROBE_RESULT_SUCCESS: driver successfully loaded.
+ *
+ * Those are the three possible values returned by a driver probe,
+ * along with an unset value for convenience.
+ */
+typedef enum {
+ PPD_PROBE_RESULT_UNSET = -2,
+ PPD_PROBE_RESULT_DEFER = -1,
+ PPD_PROBE_RESULT_FAIL = 0,
+ PPD_PROBE_RESULT_SUCCESS = 1
+} PpdProbeResult;
+
+/**
* PpdProfile:
* @PPD_PROFILE_POWER_SAVER: "power-saver", the battery saving profile
* @PPD_PROFILE_BALANCED: balanced, the default profile
@@ -27,9 +45,27 @@
PPD_PROFILE_PERFORMANCE = 1 << 2
} PpdProfile;
+/**
+ * PpdPowerChangedReason:
+ * @PPD_POWER_CHANGED_REASON_UNKNOWN: the power state is now unknown.
+ * This can happen if the power notification service is no longer available.
+ * @PPD_POWER_CHANGED_REASON_AC: the power source is now AC.
+ * @PPD_POWER_CHANGED_REASON_BATTERY: the power source is battery.
+
+ * Drivers or actions can use this information to decide what to do within a
+ * given profile.
+ */
+typedef enum{
+ PPD_POWER_CHANGED_REASON_UNKNOWN = 0,
+ PPD_POWER_CHANGED_REASON_AC,
+ PPD_POWER_CHANGED_REASON_BATTERY,
+} PpdPowerChangedReason;
+
#define PPD_PROFILE_ALL (PPD_PROFILE_BALANCED | PPD_PROFILE_POWER_SAVER | PPD_PROFILE_PERFORMANCE)
#define PPD_PROFILE_UNSET (0)
const char *ppd_profile_to_str (PpdProfile profile);
PpdProfile ppd_profile_from_str (const char *str);
gboolean ppd_profile_has_single_flag (PpdProfile profile);
+
+const char *ppd_power_changed_reason_to_str (PpdPowerChangedReason reason);
diff -Nru power-profiles-daemon-0.10.1/src/ppd-utils.c power-profiles-daemon-0.21/src/ppd-utils.c
--- power-profiles-daemon-0.10.1/src/ppd-utils.c 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-utils.c 2024-04-03 23:55:02.000000000 +0000
@@ -7,8 +7,12 @@
*
*/
+#define G_LOG_DOMAIN "Utils"
+
#include "ppd-utils.h"
+#include
#include
+#include
#include
#include
@@ -24,31 +28,65 @@
return g_build_filename (root, filename, NULL);
}
-gboolean ppd_utils_write (const char *filename,
- const char *value,
- GError **error)
+gboolean
+ppd_utils_write (const char *filename,
+ const char *value,
+ GError **error)
{
- FILE *sysfsfp;
- int ret;
+#if GLIB_CHECK_VERSION (2, 76, 0)
+ g_autofd
+#endif
+ int fd = -1;
+ size_t size;
g_return_val_if_fail (filename, FALSE);
g_return_val_if_fail (value, FALSE);
- sysfsfp = fopen (filename, "w");
- if (sysfsfp == NULL) {
+ g_debug ("Writing '%s' to '%s'", value, filename);
+
+ fd = g_open (filename, O_WRONLY | O_TRUNC | O_SYNC);
+ if (fd == -1) {
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
"Could not open '%s' for writing", filename);
g_debug ("Could not open for writing '%s'", filename);
return FALSE;
}
- ret = fprintf (sysfsfp, "%s", value);
- if (ret < 0) {
- g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
- "Error writing '%s'", filename);
- g_debug ("Error writing '%s'", filename);
- return FALSE;
+
+ size = strlen (value);
+ while (size) {
+ ssize_t written = write (fd, value, size);
+
+ if (written == -1) {
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+ "Error writing '%s': %s", filename, g_strerror (errno));
+ g_debug ("Error writing '%s': %s", filename, g_strerror (errno));
+#if !GLIB_CHECK_VERSION (2, 76, 0)
+ g_close (fd, NULL);
+#endif
+ return FALSE;
+ }
+
+ g_return_val_if_fail (written <= size, FALSE);
+ size -= written;
}
- fclose (sysfsfp);
+
+ return TRUE;
+}
+
+gboolean
+ppd_utils_write_files (GPtrArray *filenames,
+ const char *value,
+ GError **error)
+{
+ g_return_val_if_fail (filenames != NULL, FALSE);
+
+ for (guint i = 0; i < filenames->len; i++) {
+ const char *file = g_ptr_array_index (filenames, i);
+
+ if (!ppd_utils_write (file, value, error))
+ return FALSE;
+ }
+
return TRUE;
}
@@ -67,6 +105,17 @@
return ppd_utils_write (filename, value, error);
}
+gboolean ppd_utils_write_sysfs_int (GUdevDevice *device,
+ const char *attribute,
+ gint64 value,
+ GError **error)
+{
+ g_autofree char *str_value = NULL;
+
+ str_value = g_strdup_printf ("%" G_GINT64_FORMAT, value);
+ return ppd_utils_write_sysfs (device, attribute, str_value, error);
+}
+
GFileMonitor *
ppd_utils_monitor_sysfs_attr (GUdevDevice *device,
const char *attribute,
@@ -77,6 +126,7 @@
path = g_build_filename (g_udev_device_get_sysfs_path (device), attribute, NULL);
file = g_file_new_for_path (path);
+ g_debug ("Monitoring file %s for changes", path);
return g_file_monitor_file (file,
G_FILE_MONITOR_NONE,
NULL,
diff -Nru power-profiles-daemon-0.10.1/src/ppd-utils.h power-profiles-daemon-0.21/src/ppd-utils.h
--- power-profiles-daemon-0.10.1/src/ppd-utils.h 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/src/ppd-utils.h 2024-04-03 23:55:02.000000000 +0000
@@ -16,10 +16,17 @@
gboolean ppd_utils_write (const char *filename,
const char *value,
GError **error);
+gboolean ppd_utils_write_files (GPtrArray *filenames,
+ const char *value,
+ GError **error);
gboolean ppd_utils_write_sysfs (GUdevDevice *device,
const char *attribute,
const char *value,
GError **error);
+gboolean ppd_utils_write_sysfs_int (GUdevDevice *device,
+ const char *attribute,
+ gint64 value,
+ GError **error);
GFileMonitor *ppd_utils_monitor_sysfs_attr (GUdevDevice *device,
const char *attribute,
GError **error);
diff -Nru power-profiles-daemon-0.10.1/tests/integration-test.py power-profiles-daemon-0.21/tests/integration-test.py
--- power-profiles-daemon-0.10.1/tests/integration-test.py 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/tests/integration-test.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,816 +0,0 @@
-#!/usr/bin/python3
-
-# power-profiles-daemon integration test suite
-#
-# Run in built tree to test local built binaries, or from anywhere else to test
-# system installed binaries.
-#
-# Copyright: (C) 2011 Martin Pitt
-# (C) 2020 Bastien Nocera
-# (C) 2021 David Redondo
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-import os
-import sys
-import dbus
-import tempfile
-import subprocess
-import unittest
-import time
-
-try:
- import gi
- from gi.repository import GLib
- from gi.repository import Gio
-except ImportError as e:
- sys.stderr.write('Skipping tests, PyGobject not available for Python 3, or missing GI typelibs: %s\n' % str(e))
- sys.exit(0)
-
-try:
- gi.require_version('UMockdev', '1.0')
- from gi.repository import UMockdev
-except ImportError:
- sys.stderr.write('Skipping tests, umockdev not available (https://github.com/martinpitt/umockdev)\n')
- sys.exit(0)
-
-try:
- import dbusmock
-except ImportError:
- sys.stderr.write('Skipping tests, python-dbusmock not available (http://pypi.python.org/pypi/python-dbusmock).\n')
- sys.exit(0)
-
-
-PP = 'net.hadess.PowerProfiles'
-PP_PATH = '/net/hadess/PowerProfiles'
-PP_INTERFACE = 'net.hadess.PowerProfiles'
-
-class Tests(dbusmock.DBusTestCase):
- @classmethod
- def setUpClass(cls):
- # run from local build tree if we are in one, otherwise use system instance
- builddir = os.getenv('top_builddir', '.')
- if os.access(os.path.join(builddir, 'src', 'power-profiles-daemon'), os.X_OK):
- cls.daemon_path = os.path.join(builddir, 'src', 'power-profiles-daemon')
- print('Testing binaries from local build tree (%s)' % cls.daemon_path)
- elif os.environ.get('UNDER_JHBUILD', False):
- jhbuild_prefix = os.environ['JHBUILD_PREFIX']
- cls.daemon_path = os.path.join(jhbuild_prefix, 'libexec', 'power-profiles-daemon')
- print('Testing binaries from JHBuild (%s)' % cls.daemon_path)
- else:
- cls.daemon_path = None
- with open('/usr/lib/systemd/system/power-profiles-daemon.service') as f:
- for line in f:
- if line.startswith('ExecStart='):
- cls.daemon_path = line.split('=', 1)[1].strip()
- break
- assert cls.daemon_path, 'could not determine daemon path from systemd .service file'
- print('Testing installed system binary (%s)' % cls.daemon_path)
-
- # fail on CRITICALs on client and server side
- GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_WARNING |
- GLib.LogLevelFlags.LEVEL_ERROR |
- GLib.LogLevelFlags.LEVEL_CRITICAL)
- os.environ['G_DEBUG'] = 'fatal_warnings'
-
- # set up a fake system D-BUS
- cls.test_bus = Gio.TestDBus.new(Gio.TestDBusFlags.NONE)
- cls.test_bus.up()
- try:
- del os.environ['DBUS_SESSION_BUS_ADDRESS']
- except KeyError:
- pass
- os.environ['DBUS_SYSTEM_BUS_ADDRESS'] = cls.test_bus.get_bus_address()
-
- cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
- cls.dbus_con = cls.get_dbus(True)
-
- @classmethod
- def tearDownClass(cls):
- cls.test_bus.down()
- dbusmock.DBusTestCase.tearDownClass()
-
- def setUp(self):
- '''Set up a local umockdev testbed.
-
- The testbed is initially empty.
- '''
- self.testbed = UMockdev.Testbed.new()
- self.polkitd, self.obj_polkit = self.spawn_server_template(
- 'polkitd', {}, stdout=subprocess.PIPE)
- self.obj_polkit.SetAllowed(['net.hadess.PowerProfiles.switch-profile',
- 'net.hadess.PowerProfiles.hold-profile'])
-
- self.proxy = None
- self.log = None
- self.daemon = None
-
- # Used for dytc devices
- self.tp_acpi = None
-
- def tearDown(self):
- del self.testbed
- self.stop_daemon()
-
- if self.polkitd:
- try:
- self.polkitd.kill()
- except OSError:
- pass
- self.polkitd.wait()
- self.polkitd = None
- self.obj_polkit = None
-
- del self.tp_acpi
- try:
- os.remove(self.testbed.get_root_dir() + '/' + 'ppd_test_conf.ini')
- except Exception:
- pass
-
- # on failures, print daemon log
- errors = [x[1] for x in self._outcome.errors if x[1]]
- if errors and self.log:
- with open(self.log.name) as f:
- sys.stderr.write('\n-------------- daemon log: ----------------\n')
- sys.stderr.write(f.read())
- sys.stderr.write('------------------------------\n')
-
- #
- # Daemon control and D-BUS I/O
- #
-
- def start_daemon(self):
- '''Start daemon and create DBus proxy.
-
- When done, this sets self.proxy as the Gio.DBusProxy for power-profiles-daemon.
- '''
- env = os.environ.copy()
- env['G_DEBUG'] = 'fatal-criticals'
- env['G_MESSAGES_DEBUG'] = 'all'
- # note: Python doesn't propagate the setenv from Testbed.new(), so we
- # have to do that ourselves
- env['UMOCKDEV_DIR'] = self.testbed.get_root_dir()
- self.log = tempfile.NamedTemporaryFile()
- if os.getenv('VALGRIND') != None:
- daemon_path = ['valgrind', self.daemon_path, '-v']
- else:
- daemon_path = [self.daemon_path, '-v']
-
- self.daemon = subprocess.Popen(daemon_path,
- env=env, stdout=self.log,
- stderr=subprocess.STDOUT)
-
- # wait until the daemon gets online
- timeout = 100
- while timeout > 0:
- time.sleep(0.1)
- timeout -= 1
- try:
- self.get_dbus_property('ActiveProfile')
- break
- except GLib.GError:
- pass
- else:
- self.fail('daemon did not start in 10 seconds')
-
- self.proxy = Gio.DBusProxy.new_sync(
- self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, PP,
- PP_PATH, PP, None)
-
- self.assertEqual(self.daemon.poll(), None, 'daemon crashed')
-
- def stop_daemon(self):
- '''Stop the daemon if it is running.'''
-
- if self.daemon:
- try:
- self.daemon.kill()
- except OSError:
- pass
- self.daemon.wait()
- self.daemon = None
- self.proxy = None
-
- def get_dbus_property(self, name):
- '''Get property value from daemon D-Bus interface.'''
-
- proxy = Gio.DBusProxy.new_sync(
- self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, PP,
- PP_PATH, 'org.freedesktop.DBus.Properties', None)
- return proxy.Get('(ss)', PP, name)
-
- def set_dbus_property(self, name, value):
- '''Set property value on daemon D-Bus interface.'''
-
- proxy = Gio.DBusProxy.new_sync(
- self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, PP,
- PP_PATH, 'org.freedesktop.DBus.Properties', None)
- return proxy.Set('(ssv)', PP, name, value)
-
- def call_dbus_method(self, name, parameters):
- '''Call a method of the daemon D-Bus interface.'''
-
- proxy = Gio.DBusProxy.new_sync(
- self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, PP,
- PP_PATH, PP_INTERFACE, None)
- return proxy.call_sync(name, parameters, Gio.DBusCallFlags.NO_AUTO_START, -1, None)
-
-
- def have_text_in_log(self, text):
- return self.count_text_in_log(text) > 0
-
- def count_text_in_log(self, text):
- with open(self.log.name) as f:
- return f.read().count(text)
-
- def read_sysfs_file(self, path):
- with open(self.testbed.get_root_dir() + '/' + path, 'rb') as f:
- return f.read().rstrip()
- return None
-
- def read_sysfs_attr(self, device, attribute):
- return self.read_sysfs_file(device + '/' + attribute)
-
- def read_file(self, path):
- with open(path, 'rb') as f:
- return f.read()
- return None
-
- def create_dytc_device(self):
- self.tp_acpi = self.testbed.add_device('platform', 'thinkpad_acpi', None,
- ['dytc_lapmode', '0\n'],
- [ 'DEVPATH', '/devices/platform/thinkpad_acpi' ]
- )
-
- def create_empty_platform_profile(self):
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- os.makedirs(acpi_dir)
- with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
- profile.write('\n')
- with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
- choices.write('\n')
-
- def create_platform_profile(self):
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- os.makedirs(acpi_dir)
- with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
- profile.write("performance\n")
- with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
- choices.write("low-power balanced performance\n")
-
- def remove_platform_profile(self):
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- os.remove(os.path.join(acpi_dir, "platform_profile_choices"))
- os.remove(os.path.join(acpi_dir, "platform_profile"))
- os.removedirs(acpi_dir)
-
- def assertEventually(self, condition, message=None, timeout=50):
- '''Assert that condition function eventually returns True.
-
- Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
- printed on failure.
- '''
- while timeout >= 0:
- context = GLib.MainContext.default()
- while context.iteration(False):
- pass
- if condition():
- break
- timeout -= 1
- time.sleep(0.1)
- else:
- self.fail(message or 'timed out waiting for ' + str(condition))
-
- #
- # Actual test cases
- #
- def test_dbus_startup_error(self):
- '''D-Bus startup error'''
-
- self.start_daemon()
- out = subprocess.run([self.daemon_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- self.assertEqual(out.returncode, 1, "power-profile-daemon started but should have failed")
- self.stop_daemon()
-
- def test_no_performance_driver(self):
- '''no performance driver'''
-
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(self.get_dbus_property('PerformanceDegraded'), '')
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 2)
- self.assertEqual(profiles[1]['Driver'], 'placeholder')
- self.assertEqual(profiles[0]['Driver'], 'placeholder')
- self.assertEqual(profiles[1]['Profile'], 'balanced')
- self.assertEqual(profiles[0]['Profile'], 'power-saver')
-
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
-
- # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.PowerProfiles', '--object-path', '/net/hadess/PowerProfiles'])
- # print (self.get_dbus_property('GPUs'))
-
- self.stop_daemon()
-
- def test_inhibited_property(self):
- '''Test that the inhibited property exists'''
-
- self.create_dytc_device()
- self.create_platform_profile()
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(self.get_dbus_property('PerformanceInhibited'), '')
-
- def test_degraded_transition(self):
- '''Test that transitions work as expected when degraded'''
-
- self.create_dytc_device()
- self.create_platform_profile()
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
-
- # Degraded
- self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '1\n')
- self.assertEventually(lambda: self.have_text_in_log('dytc_lapmode is now on'))
- self.assertEqual(self.get_dbus_property('PerformanceDegraded'), 'lap-detected')
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
-
- # Switch to non-performance
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
-
- def test_intel_pstate(self):
- '''Intel P-State driver (no UPower)'''
-
- # Create 2 CPUs with preferences
- dir1 = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/")
- os.makedirs(dir1)
- with open(os.path.join(dir1, "energy_performance_preference"),'w') as prefs:
- prefs.write("performance\n")
- dir2 = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/")
- os.makedirs(dir2)
- with open(os.path.join(dir2, "energy_performance_preference"),'w') as prefs:
- prefs.write("performance\n")
-
- # Create no_turbo pref
- pstate_dir = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate")
- os.makedirs(pstate_dir)
- with open(os.path.join(pstate_dir, "no_turbo"),'w') as no_turbo:
- no_turbo.write("0\n")
-
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(profiles[0]['Driver'], 'intel_pstate')
- self.assertEqual(profiles[0]['Profile'], 'power-saver')
-
- contents = None
- with open(os.path.join(dir2, "energy_performance_preference"), 'rb') as f:
- contents = f.read()
- self.assertEqual(contents, b'balance_performance')
-
- # Set performance mode
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
-
- contents = None
- with open(os.path.join(dir2, "energy_performance_preference"), 'rb') as f:
- contents = f.read()
- self.assertEqual(contents, b'performance')
-
- # Disable turbo
- with open(os.path.join(pstate_dir, "no_turbo"),'w') as no_turbo:
- no_turbo.write("1\n")
-
- self.assertEventually(lambda: self.have_text_in_log('File monitor change happened for '))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- self.assertEqual(self.get_dbus_property('PerformanceDegraded'), 'high-operating-temperature')
-
- self.stop_daemon()
-
- # Verify that the Lenovo DYTC driver still gets preferred
- self.create_platform_profile()
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(profiles[0]['Driver'], 'platform_profile')
-
- def test_intel_pstate_balance(self):
- '''Intel P-State driver (balance)'''
-
- # Create CPU with preference
- dir1 = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/")
- os.makedirs(dir1)
- with open(os.path.join(dir1, "energy_performance_preference"),'w') as prefs:
- prefs.write("performance\n")
-
- upowerd, obj_upower = self.spawn_server_template(
- 'upower', {'DaemonVersion': '0.99', 'OnBattery': False}, stdout=subprocess.PIPE)
-
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(profiles[0]['Driver'], 'intel_pstate')
- self.assertEqual(profiles[0]['Profile'], 'power-saver')
-
- contents = None
- with open(os.path.join(dir1, "energy_performance_preference"), 'rb') as f:
- contents = f.read()
- # This matches what's written by ppd-driver-intel-pstate.c
- self.assertEqual(contents, b'balance_performance')
-
- self.stop_daemon()
-
- upowerd.terminate()
- upowerd.wait()
- upowerd.stdout.close()
-
- def test_dytc_performance_driver(self):
- '''Lenovo DYTC performance driver'''
-
- self.create_dytc_device()
- self.create_platform_profile()
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(profiles[0]['Driver'], 'platform_profile')
- self.assertEqual(profiles[0]['Profile'], 'power-saver')
- self.assertEqual(profiles[2]['Driver'], 'platform_profile')
- self.assertEqual(profiles[2]['Profile'], 'performance')
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
-
- # lapmode detected
- self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '1\n')
- self.assertEventually(lambda: self.get_dbus_property('PerformanceDegraded') == 'lap-detected')
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
-
- # Reset lapmode
- self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '0\n')
- self.assertEventually(lambda: self.get_dbus_property('PerformanceDegraded') == '')
-
- # Performance mode didn't change
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
-
- # Switch to power-saver mode
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertEventually(lambda: self.read_sysfs_file("sys/firmware/acpi/platform_profile") == b'low-power')
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
-
- # And mimick a user pressing a Fn+H
- with open(os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/platform_profile"), 'w') as platform_profile:
- platform_profile.write('performance\n')
- self.assertEventually(lambda: self.get_dbus_property('ActiveProfile') == 'performance')
-
- def test_fake_driver(self):
- '''Test that the fake driver works'''
-
- os.environ['POWER_PROFILE_DAEMON_FAKE_DRIVER'] = '1'
- self.start_daemon()
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.stop_daemon()
-
- del os.environ['POWER_PROFILE_DAEMON_FAKE_DRIVER']
- self.start_daemon()
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 2)
-
- def test_trickle_charge_mode(self):
- '''Trickle power_supply charge type'''
-
- idevice = self.testbed.add_device('usb', 'iDevice', None,
- [],
- [ 'ID_MODEL', 'iDevice', 'DRIVER', 'apple-mfi-fastcharge' ]
- )
- fastcharge = self.testbed.add_device('power_supply', 'MFi Fastcharge', idevice,
- [ 'charge_type', 'Trickle', 'scope', 'Device' ],
- []
- )
-
- self.start_daemon()
-
- self.assertIn('trickle_charge', self.get_dbus_property('Actions'))
-
- # Verify that charge-type got changed to Fast on startup
- self.assertEqual(self.read_sysfs_attr(fastcharge, 'charge_type'), b'Fast')
-
- # Verify that charge-type got changed to Trickle when power saving
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertEqual(self.read_sysfs_attr(fastcharge, 'charge_type'), b'Trickle')
-
- # FIXME no performance mode
- # Verify that charge-type got changed to Fast in a non-default, non-power save mode
- # self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
- # self.assertEqual(self.read_sysfs_attr(fastcharge, 'charge_type'), 'Fast')
-
- def test_platform_driver_late_load(self):
- '''Test that we can handle the platform_profile driver getting loaded late'''
- self.create_empty_platform_profile()
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 2)
-
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
- choices.write("low-power\nbalanced\nperformance\n")
- with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
- profile.write("performance\n")
-
- # Wait for profiles to get reloaded
- self.assertEventually(lambda: len(self.get_dbus_property('Profiles')) == 3)
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- # Was set in platform_profile before we loaded the drivers
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(self.get_dbus_property('PerformanceDegraded'), '')
-
- self.stop_daemon()
-
- def test_hp_wmi(self):
-
- # Uses cool instead of low-power
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- os.makedirs(acpi_dir)
- with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
- profile.write("cool\n")
- with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
- choices.write("cool balanced performance\n")
-
- self.start_daemon()
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(profiles[0]['Driver'], 'platform_profile')
- self.assertEqual(profiles[0]['Profile'], 'power-saver')
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'balanced')
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'cool')
-
- self.stop_daemon()
-
- def test_quiet(self):
- # Uses cool instead of low-power
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- os.makedirs(acpi_dir)
- with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
- profile.write("cool\n")
- with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
- choices.write("quiet balanced balanced-performance performance\n")
-
- self.start_daemon()
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(profiles[0]['Driver'], 'platform_profile')
- self.assertEqual(profiles[0]['Profile'], 'power-saver')
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'balanced')
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'quiet')
-
- self.stop_daemon()
-
- def test_hold_release_profile(self):
- self.create_platform_profile()
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
-
- cookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', 'testReason', 'testApplication')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- profileHolds = self.get_dbus_property('ActiveProfileHolds')
- self.assertEqual(len(profileHolds), 1)
- self.assertEqual(profileHolds[0]["Profile"], "performance")
- self.assertEqual(profileHolds[0]["Reason"], "testReason")
- self.assertEqual(profileHolds[0]["ApplicationId"], "testApplication")
-
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", cookie))
- profileHolds = self.get_dbus_property('ActiveProfileHolds')
- self.assertEqual(len(profileHolds), 0)
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- # When the profile is changed manually, holds should be released a
- self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertEqual(len(self.get_dbus_property('ActiveProfileHolds')), 1)
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
-
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('balanced'))
- self.assertEqual(len(self.get_dbus_property('ActiveProfileHolds')), 0)
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- # When all holds are released, the last manually selected profile should be activated
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- cookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", cookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
-
- self.stop_daemon()
-
- def test_vanishing_hold(self):
- self.create_platform_profile()
- self.start_daemon()
-
- builddir = os.getenv('top_builddir', '.')
- tool_path = os.path.join(builddir, 'src', 'powerprofilesctl')
-
- launch_process = subprocess.Popen([tool_path, 'launch', '-p', 'power-saver', 'sleep', '3600'],
- stdout=sys.stdout, stderr=sys.stderr)
- assert launch_process
- time.sleep(1)
- holds = self.get_dbus_property('ActiveProfileHolds')
- self.assertEqual(len(holds), 1)
- hold = holds[0]
- self.assertEqual(hold['Profile'], 'power-saver')
-
- # Make sure to handle vanishing clients
- launch_process.terminate()
- launch_process.wait()
-
- holds = self.get_dbus_property('ActiveProfileHolds')
- self.assertEqual(len(holds), 0)
-
- self.stop_daemon()
-
- def test_hold_priority(self):
- '''power-saver should take priority over performance'''
- self.create_platform_profile()
- self.start_daemon()
-
- profiles = self.get_dbus_property('Profiles')
- self.assertEqual(len(profiles), 3)
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- # Test every order of holding and releasing power-saver and performance
- # hold performance and then power-saver, release in the same order
- performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", performanceCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", powerSaverCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- # hold performance and then power-saver, but release power-saver first
- performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)",powerSaverCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", performanceCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- # hold power-saver and then performance, release in the same order
- powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)",powerSaverCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", performanceCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- # hold power-saver and then performance, but release performance first
- powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)",performanceCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", powerSaverCookie))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- self.stop_daemon()
-
- def test_save_profile(self):
- '''save profile across runs'''
-
- self.create_platform_profile()
-
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.stop_daemon()
-
- # sys.stderr.write('\n-------------- config file: ----------------\n')
- # with open(self.testbed.get_root_dir() + '/' + 'ppd_test_conf.ini') as f:
- # sys.stderr.write(f.read())
- # sys.stderr.write('------------------------------\n')
-
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- # Programmatically set profile aren't saved
- performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- self.stop_daemon()
-
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.stop_daemon()
-
- def test_save_deferred_load(self):
- '''save profile across runs, but kernel driver loaded after start'''
-
- self.create_platform_profile()
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.stop_daemon()
- self.remove_platform_profile()
-
- # We could verify the contents of the configuration file here
-
- self.create_empty_platform_profile()
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
- choices.write("low-power\nbalanced\nperformance\n")
- with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
- profile.write("performance\n")
-
- self.assertEventually(lambda: self.get_dbus_property('ActiveProfile') == 'power-saver')
- self.stop_daemon()
-
- def test_not_allowed_profile(self):
- '''Check that we get errors when trying to change a profile and not allowed'''
-
- self.obj_polkit.SetAllowed(dbus.Array([], signature='s'))
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- proxy = Gio.DBusProxy.new_sync(
- self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, PP,
- PP_PATH, 'org.freedesktop.DBus.Properties', None)
- with self.assertRaises(gi.repository.GLib.GError) as cm:
- proxy.Set('(ssv)', PP, 'ActiveProfile', GLib.Variant.new_string('power-saver'))
- self.assertIn('AccessDenied', str(cm.exception))
-
- self.stop_daemon()
-
- def test_not_allowed_hold(self):
- '''Check that we get an error when trying to hold a profile and not allowed'''
-
- self.obj_polkit.SetAllowed(dbus.Array([], signature='s'))
- self.start_daemon()
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
-
- with self.assertRaises(gi.repository.GLib.GError) as cm:
- self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
- self.assertIn('AccessDenied', str(cm.exception))
-
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(len(self.get_dbus_property('ActiveProfileHolds')), 0)
-
- self.stop_daemon()
-
- #
- # Helper methods
- #
-
- @classmethod
- def _props_to_str(cls, properties):
- '''Convert a properties dictionary to uevent text representation.'''
-
- prop_str = ''
- if properties:
- for k, v in properties.items():
- prop_str += '%s=%s\n' % (k, v)
- return prop_str
-
-if __name__ == '__main__':
- # run ourselves under umockdev
- if 'umockdev' not in os.environ.get('LD_PRELOAD', ''):
- os.execvp('umockdev-wrapper', ['umockdev-wrapper'] + sys.argv)
-
- unittest.main()
diff -Nru power-profiles-daemon-0.10.1/tests/integration_test.py power-profiles-daemon-0.21/tests/integration_test.py
--- power-profiles-daemon-0.10.1/tests/integration_test.py 1970-01-01 00:00:00.000000000 +0000
+++ power-profiles-daemon-0.21/tests/integration_test.py 2024-04-03 23:55:02.000000000 +0000
@@ -0,0 +1,2305 @@
+#!/usr/bin/python3
+
+# power-profiles-daemon integration test suite
+#
+# Run in built tree to test local built binaries, or from anywhere else to test
+# system installed binaries.
+#
+# Copyright: (C) 2011 Martin Pitt
+# (C) 2020 Bastien Nocera
+# (C) 2021 David Redondo
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+import os
+import subprocess
+import signal
+import sys
+import tempfile
+import time
+import unittest
+
+import dbus
+
+try:
+ import gi
+ from gi.repository import GLib
+ from gi.repository import Gio
+except ImportError as e:
+ sys.stderr.write(
+ f"Skipping tests, PyGobject not available for Python 3, or missing GI typelibs: {str(e)}\n"
+ )
+ sys.exit(77)
+
+try:
+ gi.require_version("UMockdev", "1.0")
+ from gi.repository import UMockdev
+except ImportError:
+ sys.stderr.write("Skipping tests, umockdev not available.\n")
+ sys.stderr.write("(https://github.com/martinpitt/umockdev)\n")
+ sys.exit(77)
+
+try:
+ import dbusmock
+except ImportError:
+ sys.stderr.write("Skipping tests, python-dbusmock not available.\n")
+ sys.stderr.write("(http://pypi.python.org/pypi/python-dbusmock)")
+ sys.exit(77)
+
+
+# pylint: disable=too-many-public-methods,too-many-instance-attributes
+class Tests(dbusmock.DBusTestCase):
+ """Dbus based integration unit tests"""
+
+ PP = "org.freedesktop.UPower.PowerProfiles"
+ PP_PATH = "/org/freedesktop/UPower/PowerProfiles"
+ PP_INTERFACE = "org.freedesktop.UPower.PowerProfiles"
+
+ @classmethod
+ def setUpClass(cls):
+ # run from local build tree if we are in one, otherwise use system instance
+ builddir = os.getenv("top_builddir", ".")
+ if os.access(os.path.join(builddir, "src", "power-profiles-daemon"), os.X_OK):
+ cls.daemon_path = os.path.join(builddir, "src", "power-profiles-daemon")
+ print(f"Testing binaries from local build tree {cls.daemon_path}")
+ elif os.environ.get("UNDER_JHBUILD", False):
+ jhbuild_prefix = os.environ["JHBUILD_PREFIX"]
+ cls.daemon_path = os.path.join(
+ jhbuild_prefix, "libexec", "power-profiles-daemon"
+ )
+ print(f"Testing binaries from JHBuild {cls.daemon_path}")
+ else:
+ cls.daemon_path = None
+ with open(
+ "/usr/lib/systemd/system/power-profiles-daemon.service",
+ encoding="utf-8",
+ ) as tmpf:
+ for line in tmpf:
+ if line.startswith("ExecStart="):
+ cls.daemon_path = line.split("=", 1)[1].strip()
+ break
+ assert (
+ cls.daemon_path
+ ), "could not determine daemon path from systemd .service file"
+ print(f"Testing installed system binary {cls.daemon_path}")
+
+ # fail on CRITICALs on client and server side
+ GLib.log_set_always_fatal(
+ GLib.LogLevelFlags.LEVEL_WARNING
+ | GLib.LogLevelFlags.LEVEL_ERROR
+ | GLib.LogLevelFlags.LEVEL_CRITICAL
+ )
+ os.environ["G_DEBUG"] = "fatal_warnings"
+
+ # set up a fake system D-BUS
+ cls.start_system_bus()
+ cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+
+ def start_dbus_template(self, template, parameters):
+ process, dbus_object = self.spawn_server_template(
+ template, parameters, stdout=subprocess.PIPE
+ )
+
+ def stop_template():
+ process.stdout.close()
+ try:
+ process.kill()
+ except OSError:
+ pass
+ process.wait()
+
+ self.addCleanup(stop_template)
+ self.assertTrue(process)
+ self.assertTrue(dbus_object)
+
+ return process, dbus_object, stop_template
+
+ def setUp(self):
+ """Set up a local umockdev testbed.
+
+ The testbed is initially empty.
+ """
+ self.testbed = UMockdev.Testbed.new()
+
+ def del_testbed():
+ del self.testbed
+
+ self.addCleanup(del_testbed)
+ self.proxy = None
+ self.props_proxy = None
+ self.log = None
+ self.daemon = None
+ self.changed_properties = {}
+
+ # Used for dytc devices
+ self.tp_acpi = None
+
+ self.polkitd, self.obj_polkit, _ = self.start_dbus_template("polkitd", {})
+ self.obj_polkit.SetAllowed(
+ [
+ "org.freedesktop.UPower.PowerProfiles.switch-profile",
+ "org.freedesktop.UPower.PowerProfiles.hold-profile",
+ ]
+ )
+
+ def run(self, result=None):
+ super().run(result)
+ if not result or not self.log:
+ return
+ if len(result.errors) + len(result.failures) or os.getenv("PPD_TEST_VERBOSE"):
+ with open(self.log.name, encoding="utf-8") as tmpf:
+ sys.stderr.write("\n-------------- daemon log: ----------------\n")
+ sys.stderr.write(tmpf.read())
+ sys.stderr.write("------------------------------\n")
+
+ #
+ # Daemon control and D-BUS I/O
+ #
+
+ def start_daemon(self, args=None):
+ """Start daemon and create DBus proxy.
+
+ When done, this sets self.proxy as the Gio.DBusProxy for power-profiles-daemon.
+ """
+ env = os.environ.copy()
+ env["G_DEBUG"] = "fatal-criticals"
+ env["G_MESSAGES_DEBUG"] = "all"
+ # note: Python doesn't propagate the setenv from Testbed.new(), so we
+ # have to do that ourselves
+ env["UMOCKDEV_DIR"] = self.testbed.get_root_dir()
+ env["LD_PRELOAD"] = os.getenv("PPD_LD_PRELOAD") + " " + os.getenv("LD_PRELOAD")
+ self.log = tempfile.NamedTemporaryFile() # pylint: disable=consider-using-with
+ daemon_path = [self.daemon_path, "-vv"]
+ if args:
+ daemon_path += args
+ if os.getenv("PPD_TEST_WRAPPER"):
+ daemon_path = os.getenv("PPD_TEST_WRAPPER").split(" ") + daemon_path
+ elif os.getenv("VALGRIND"):
+ daemon_path = ["valgrind"] + daemon_path
+
+ # pylint: disable=consider-using-with
+ self.daemon = subprocess.Popen(
+ daemon_path, env=env, stdout=self.log, stderr=sys.stderr
+ )
+ self.addCleanup(self.stop_daemon, delete_profile=True)
+
+ def on_proxy_connected(_, res):
+ try:
+ self.proxy = Gio.DBusProxy.new_finish(res)
+ print(f"Proxy to {self.proxy.get_name()} connected")
+ except GLib.Error as exc:
+ self.fail(exc)
+
+ cancellable = Gio.Cancellable()
+ self.addCleanup(cancellable.cancel)
+ Gio.DBusProxy.new(
+ self.dbus,
+ Gio.DBusProxyFlags.DO_NOT_AUTO_START,
+ None,
+ self.PP,
+ self.PP_PATH,
+ self.PP_INTERFACE,
+ cancellable,
+ on_proxy_connected,
+ )
+
+ # wait until the daemon gets online
+ wait_time = 20 if "valgrind" in daemon_path[0] else 5
+ self.assert_eventually(
+ lambda: self.proxy and self.proxy.get_name_owner(),
+ timeout=wait_time * 1000,
+ message=lambda: f"daemon did not start in {wait_time} seconds: "
+ + f"proxy is {self.proxy} and owner "
+ + f"{self.proxy.get_name_owner() if self.proxy else 'None'}",
+ )
+
+ def properties_changed_cb(_, changed_properties, invalidated):
+ self.changed_properties.update(changed_properties.unpack())
+
+ self.addCleanup(
+ self.proxy.disconnect,
+ self.proxy.connect("g-properties-changed", properties_changed_cb),
+ )
+
+ self.assertEqual(self.daemon.poll(), None, "daemon crashed")
+
+ def ensure_dbus_properties_proxies(self):
+ self.props_proxy = Gio.DBusProxy.new_sync(
+ self.dbus,
+ Gio.DBusProxyFlags.DO_NOT_AUTO_START
+ | Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION
+ | Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES
+ | Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
+ None,
+ self.PP,
+ self.PP_PATH,
+ "org.freedesktop.DBus.Properties",
+ None,
+ )
+
+ def stop_daemon(self, delete_profile=False):
+ """Stop the daemon if it is running."""
+
+ if self.daemon:
+ try:
+ self.daemon.terminate()
+ except OSError:
+ pass
+ self.assertEqual(self.daemon.wait(timeout=3000), 0)
+
+ if delete_profile:
+ try:
+ os.remove(self.testbed.get_root_dir() + "/" + "ppd_test_conf.ini")
+ except (AttributeError, FileNotFoundError):
+ pass
+
+ self.daemon = None
+ self.proxy = None
+
+ def get_dbus_property(self, name):
+ """Get property value from daemon D-Bus interface."""
+ self.ensure_dbus_properties_proxies()
+ return self.props_proxy.Get("(ss)", self.PP, name)
+
+ def set_dbus_property(self, name, value):
+ """Set property value on daemon D-Bus interface."""
+ self.ensure_dbus_properties_proxies()
+ return self.props_proxy.Set("(ssv)", self.PP, name, value)
+
+ def call_dbus_method(self, name, parameters):
+ """Call a method of the daemon D-Bus interface."""
+ return self.proxy.call_sync(
+ name, parameters, Gio.DBusCallFlags.NO_AUTO_START, -1, None
+ )
+
+ def have_text_in_log(self, text):
+ return self.count_text_in_log(text) > 0
+
+ def count_text_in_log(self, text):
+ with open(self.log.name, encoding="utf-8") as tmpf:
+ return tmpf.read().count(text)
+
+ def read_file_contents(self, path):
+ """Get the contents of a file"""
+ with open(path, "rb") as tmpf:
+ return tmpf.read()
+
+ def read_sysfs_file(self, path):
+ return self.read_file_contents(
+ self.testbed.get_root_dir() + "/" + path
+ ).rstrip()
+
+ def read_sysfs_attr(self, device, attribute):
+ return self.read_sysfs_file(device + "/" + attribute)
+
+ def get_mtime(self, device, attribute):
+ return os.path.getmtime(
+ self.testbed.get_root_dir() + "/" + device + "/" + attribute
+ )
+
+ def write_file_contents(self, path, contents):
+ """Set the contents of a file"""
+ with open(path, "wb") as tmpf:
+ return tmpf.write(
+ contents if isinstance(contents, bytes) else contents.encode("utf-8")
+ )
+
+ def change_immutable(self, fname, enable):
+ attr = "-"
+ if enable:
+ os.chmod(fname, 0o444)
+ self.addCleanup(self.change_immutable, fname, False)
+ attr = "+"
+ if os.geteuid() == 0:
+ if not GLib.find_program_in_path("chattr"):
+ self.skipTest("chattr is not found")
+
+ subprocess.check_output(["chattr", f"{attr}i", fname])
+ if not enable:
+ os.chmod(fname, 0o666)
+
+ def create_dytc_device(self):
+ self.tp_acpi = self.testbed.add_device(
+ "platform",
+ "thinkpad_acpi",
+ None,
+ ["dytc_lapmode", "0\n"],
+ ["DEVPATH", "/devices/platform/thinkpad_acpi"],
+ )
+ self.addCleanup(self.testbed.remove_device, self.tp_acpi)
+
+ def create_amd_apu(self):
+ proc_dir = os.path.join(self.testbed.get_root_dir(), "proc/")
+ os.makedirs(proc_dir)
+ self.write_file_contents(
+ os.path.join(proc_dir, "cpuinfo"), "vendor_id : AuthenticAMD\n"
+ )
+
+ def create_empty_platform_profile(self):
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(acpi_dir)
+ self.write_file_contents(os.path.join(acpi_dir, "platform_profile"), "\n")
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile_choices"), "\n"
+ )
+
+ def create_platform_profile(self):
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(acpi_dir, exist_ok=True)
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile"), "performance\n"
+ )
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile_choices"),
+ "low-power balanced performance\n",
+ )
+
+ def remove_platform_profile(self):
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.remove(os.path.join(acpi_dir, "platform_profile_choices"))
+ os.remove(os.path.join(acpi_dir, "platform_profile"))
+ os.removedirs(acpi_dir)
+
+ def powerprofilesctl_path(self):
+ builddir = os.getenv("top_builddir", ".")
+ return os.path.join(builddir, "src", "powerprofilesctl")
+
+ def python_coverage_commands(self):
+ coverage = os.getenv("PPD_PYTHON_COVERAGE")
+ if not coverage:
+ return []
+
+ builddir = os.getenv("top_builddir", ".")
+ data_file = os.path.join(builddir, "python-coverage", self.id() + ".coverage")
+ # We also may need to use "--parallel-mode" if running with
+ # meson test --repeat, but this is not a priority for now.
+ return [
+ coverage,
+ "run",
+ f"--data-file={data_file}",
+ f"--include={builddir}/*",
+ ]
+
+ def powerprofilesctl_command(self):
+ return self.python_coverage_commands() + [self.powerprofilesctl_path()]
+
+ def assert_eventually(self, condition, message=None, timeout=5000, keep_checking=0):
+ """Assert that condition function eventually returns True.
+
+ Timeout is in milliseconds, defaulting to 5000 (5 seconds). message is
+ printed on failure.
+ """
+ if not keep_checking:
+ if condition():
+ return
+
+ done = False
+
+ def on_timeout_reached():
+ nonlocal done
+ done = True
+
+ source = GLib.timeout_add(timeout, on_timeout_reached)
+ while not done:
+ if condition():
+ GLib.source_remove(source)
+ if keep_checking > 0:
+ self.assert_condition_persists(
+ condition, message, timeout=keep_checking
+ )
+ return
+ GLib.MainContext.default().iteration(False)
+
+ self.fail(message() if message else f"timed out waiting for {condition}")
+
+ def assert_condition_persists(self, condition, message=None, timeout=1000):
+ done = False
+
+ def on_timeout_reached():
+ nonlocal done
+ done = True
+
+ source = GLib.timeout_add(timeout, on_timeout_reached)
+ while not done:
+ if not condition():
+ GLib.source_remove(source)
+ self.fail(
+ message() if message else f"Condition is not persisting {condition}"
+ )
+ GLib.MainContext.default().iteration(False)
+
+ def assert_file_eventually_contains(
+ self, path, contents, timeout=800, keep_checking=0
+ ):
+ """Asserts that file contents eventually matches expectations"""
+ encoded = contents.encode("utf-8")
+ return self.assert_eventually(
+ lambda: self.read_file_contents(path) == encoded,
+ timeout=timeout,
+ keep_checking=keep_checking,
+ message=lambda: f"file '{path}' does not contain '{contents}', "
+ + f"but '{self.read_file_contents(path)}'",
+ )
+
+ # pylint: disable=too-many-arguments
+ def assert_sysfs_attr_eventually_is(
+ self, device, attribute, contents, timeout=800, keep_checking=0
+ ):
+ """Asserts that file contents eventually matches expectations"""
+ encoded = contents.encode("utf-8")
+ return self.assert_eventually(
+ lambda: self.read_sysfs_attr(device, attribute) == encoded,
+ timeout=timeout,
+ keep_checking=keep_checking,
+ message=lambda: f"file {device} '{attribute}' does not contain '{contents}', "
+ + f"but '{self.read_sysfs_attr(device, attribute)}'",
+ )
+
+ def assert_dbus_property_eventually_is(
+ self, prop, value, timeout=1200, keep_checking=0
+ ):
+ """Asserts that a dbus property eventually is what expected"""
+ return self.assert_eventually(
+ lambda: self.get_dbus_property(prop) == value,
+ timeout=timeout,
+ keep_checking=keep_checking,
+ message=lambda: f"property '{prop}' is not '{value}', but "
+ + f"'{self.get_dbus_property(prop)}'",
+ )
+
+ #
+ # Actual test cases
+ #
+ def test_dbus_startup_error(self):
+ """D-Bus startup error"""
+
+ self.start_daemon()
+ daemon_path = [self.daemon_path]
+ if os.getenv("PPD_TEST_WRAPPER"):
+ daemon_path = os.getenv("PPD_TEST_WRAPPER").split(" ") + daemon_path
+ out = subprocess.run(
+ daemon_path,
+ env={
+ "LD_PRELOAD": os.getenv("PPD_LD_PRELOAD")
+ + " "
+ + os.getenv("LD_PRELOAD")
+ },
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ check=False,
+ )
+ self.assertEqual(
+ out.returncode, 1, "power-profile-daemon started but should have failed"
+ )
+ self.stop_daemon()
+
+ def test_no_performance_driver(self):
+ """no performance driver"""
+
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "")
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 2)
+ self.assertEqual(profiles[1]["Driver"], "placeholder")
+ self.assertEqual(profiles[1]["PlatformDriver"], "placeholder")
+ self.assertEqual(profiles[0]["PlatformDriver"], "placeholder")
+ self.assertEqual(profiles[1]["Profile"], "balanced")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ with self.assertRaises(gi.repository.GLib.GError):
+ self.set_dbus_property(
+ "ActiveProfile", GLib.Variant.new_string("performance")
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ with self.assertRaises(gi.repository.GLib.GError):
+ cookie = self.call_dbus_method(
+ "HoldProfile",
+ GLib.Variant("(sss)", ("performance", "testReason", "testApplication")),
+ )
+ assert cookie
+
+ self.stop_daemon()
+
+ def test_inhibited_property(self):
+ """Test that the inhibited property exists"""
+
+ self.create_dytc_device()
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(self.get_dbus_property("PerformanceInhibited"), "")
+
+ def test_multi_degredation(self):
+ """Test handling of degradation from multiple drivers"""
+ self.create_dytc_device()
+ self.create_platform_profile()
+
+ # Create CPU with preference
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create Intel P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "0\n")
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ self.start_daemon()
+
+ # Set performance mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ # Degraded CPU
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "1\n")
+ self.assert_eventually(
+ lambda: self.have_text_in_log("File monitor change happened for ")
+ )
+
+ self.assertEqual(
+ self.get_dbus_property("PerformanceDegraded"), "high-operating-temperature"
+ )
+
+ # Degraded DYTC
+ lapmode = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/thinkpad_acpi/dytc_lapmode"
+ )
+ self.write_file_contents(lapmode, "1\n")
+ self.assert_eventually(lambda: self.have_text_in_log("dytc_lapmode is now on"))
+ self.assertEqual(
+ self.get_dbus_property("PerformanceDegraded"),
+ "high-operating-temperature,lap-detected",
+ )
+
+ def test_degraded_transition(self):
+ """Test that transitions work as expected when degraded"""
+
+ self.create_dytc_device()
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ # Degraded
+ lapmode = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/thinkpad_acpi/dytc_lapmode"
+ )
+ self.write_file_contents(lapmode, "1\n")
+ self.assert_eventually(lambda: self.have_text_in_log("dytc_lapmode is now on"))
+ self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "lap-detected")
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ # Switch to non-performance
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ def test_intel_pstate(self):
+ """Intel P-State driver (no UPower)"""
+
+ # Create 2 CPUs with preferences
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+ dir2 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/"
+ )
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir2, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create Intel P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "0\n")
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "intel_pstate")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+
+ energy_prefs = os.path.join(dir2, "energy_performance_preference")
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ # Set performance mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ self.assert_file_eventually_contains(energy_prefs, "performance")
+
+ # Disable turbo
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "1\n")
+
+ self.assert_eventually(
+ lambda: self.have_text_in_log("File monitor change happened for ")
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ self.assertEqual(
+ self.get_dbus_property("PerformanceDegraded"), "high-operating-temperature"
+ )
+
+ self.stop_daemon()
+
+ # Verify that Lenovo DYTC and Intel P-State drivers are loaded
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "intel_pstate")
+ self.assertEqual(profiles[0]["PlatformDriver"], "platform_profile")
+
+ def test_intel_pstate_balance(self):
+ """Intel P-State driver (balance)"""
+
+ # Create CPU with preference
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ gov_path = os.path.join(dir1, "scaling_governor")
+ self.write_file_contents(gov_path, "performance\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+
+ self.start_daemon()
+
+ self.assert_file_eventually_contains(gov_path, "powersave")
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "intel_pstate")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+
+ self.assert_file_eventually_contains(
+ os.path.join(dir1, "energy_performance_preference"), "balance_performance"
+ )
+
+ def test_intel_pstate_reapply_on_resume_from_sleep(self):
+ # Create CPU with preference
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ gov_path = os.path.join(dir1, "scaling_governor")
+ self.write_file_contents(gov_path, "performance\n")
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ self.write_file_contents(energy_prefs, "performance\n")
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ _, obj_logind, _ = self.start_dbus_template("logind", {})
+
+ self.start_daemon()
+ self.assert_dbus_property_eventually_is(
+ "ActiveProfile", "balanced", keep_checking=100
+ )
+
+ # Simulate system changing to performance mode just before going to suspend
+ self.write_file_contents(energy_prefs, "performance\n")
+ self.assert_file_eventually_contains(
+ energy_prefs, "performance\n", keep_checking=500
+ )
+
+ obj_logind.EmitSignal(
+ "org.freedesktop.login1.Manager", "PrepareForSleep", "b", [True]
+ )
+ self.assert_file_eventually_contains(
+ energy_prefs, "performance\n", keep_checking=500
+ )
+
+ # Check that on resume the value is reset to the expected one.
+ obj_logind.EmitSignal(
+ "org.freedesktop.login1.Manager", "PrepareForSleep", "b", [False]
+ )
+
+ self.assert_file_eventually_contains(
+ energy_prefs, "balance_performance", timeout=3000, keep_checking=100
+ )
+
+ def test_intel_pstate_error(self):
+ """Intel P-State driver in error state"""
+
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ pref_path = os.path.join(dir1, "energy_performance_preference")
+ old_umask = os.umask(0o333)
+ self.write_file_contents(pref_path, "balance_performance\n")
+ os.umask(old_umask)
+ # Make file non-writable to root
+ self.change_immutable(pref_path, True)
+
+ self.start_daemon()
+
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # Error when setting performance mode
+ with self.assertRaises(gi.repository.GLib.GError):
+ self.set_dbus_property(
+ "ActiveProfile", GLib.Variant.new_string("performance")
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance\n")
+
+ def test_intel_pstate_passive(self):
+ """Intel P-State in passive mode -> placeholder"""
+
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create Intel P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "0\n")
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "passive\n")
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 2)
+ self.assertEqual(profiles[0]["Driver"], "placeholder")
+ self.assertEqual(profiles[0]["PlatformDriver"], "placeholder")
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ self.assert_file_eventually_contains(energy_prefs, "performance\n")
+
+ # Set performance mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ self.assert_file_eventually_contains(energy_prefs, "performance\n")
+
+ def test_intel_pstate_passive_with_epb(self):
+ """Intel P-State in passive mode (no HWP) with energy_perf_bias"""
+
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+ dir2 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpu0/power/"
+ )
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "energy_perf_bias"), "6")
+
+ # Create Intel P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "0\n")
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "passive\n")
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "intel_pstate")
+ self.assertEqual(profiles[0]["PlatformDriver"], "placeholder")
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # Set power-saver mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ energy_perf_bias = os.path.join(dir2, "energy_perf_bias")
+ self.assert_file_eventually_contains(energy_perf_bias, "15")
+
+ # Set performance mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ self.assert_file_eventually_contains(energy_perf_bias, "0")
+
+ def test_action_blocklist(self):
+ """Test action blocklist works"""
+ self.testbed.add_device(
+ "drm",
+ "card1-eDP",
+ None,
+ ["amdgpu/panel_power_savings", "0"],
+ ["DEVTYPE", "drm_connector"],
+ )
+
+ self.create_amd_apu()
+
+ self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+
+ # Block panel_power action
+ self.start_daemon(["--block-action", "amdgpu_panel_power"])
+ self.assertNotIn("amdgpu_panel_power", self.get_dbus_property("Actions"))
+
+ def test_driver_blocklist(self):
+ """Test driver blocklist works"""
+ # Create 2 CPUs with preferences
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ scaling_governor = os.path.join(dir1, "scaling_governor")
+ self.write_file_contents(scaling_governor, "powersave\n")
+
+ prefs1 = os.path.join(dir1, "energy_performance_preference")
+ self.write_file_contents(prefs1, "performance\n")
+
+ dir2 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/"
+ )
+ os.makedirs(dir2)
+ scaling_governor = os.path.join(dir2, "scaling_governor")
+ self.write_file_contents(scaling_governor, "powersave\n")
+ prefs2 = os.path.join(
+ dir2,
+ "energy_performance_preference",
+ )
+ self.write_file_contents(prefs2, "prformance\n")
+
+ # Create AMD P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ # create ACPI platform profile
+ self.create_platform_profile()
+ profile = os.path.join(
+ self.testbed.get_root_dir(), "sys/firmware/acpi/platform_profile"
+ )
+ self.assertNotEqual(profile, None)
+
+ # desktop PM profile
+ dir3 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir3, exist_ok=True)
+ self.write_file_contents(os.path.join(dir3, "pm_profile"), "1\n")
+
+ # block platform profile
+ self.start_daemon(["--block-driver", "platform_profile"])
+ # Verify that only amd-pstate is loaded
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "amd_pstate")
+ self.assertEqual(profiles[0]["PlatformDriver"], "placeholder")
+
+ self.stop_daemon()
+
+ # block both drivers
+ self.start_daemon(
+ ["--block-driver", "amd_pstate", "--block-driver", "platform_profile"]
+ )
+ # Verify that only placeholder is loaded
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 2)
+ self.assertEqual(profiles[0]["PlatformDriver"], "placeholder")
+
+ # pylint: disable=too-many-statements
+ def test_multi_driver_flows(self):
+ """Test corner cases associated with multiple drivers"""
+
+ # Create 2 CPUs with preferences
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ prefs1 = os.path.join(dir1, "energy_performance_preference")
+ self.write_file_contents(prefs1, "performance\n")
+
+ dir2 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/"
+ )
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "scaling_governor"), "powersave\n")
+ prefs2 = os.path.join(dir2, "energy_performance_preference")
+ self.write_file_contents(prefs2, "performance\n")
+
+ # Create AMD P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ # create ACPI platform profile
+ self.create_platform_profile()
+ profile = os.path.join(
+ self.testbed.get_root_dir(), "sys/firmware/acpi/platform_profile"
+ )
+
+ # desktop PM profile
+ dir3 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir3, exist_ok=True)
+ self.write_file_contents(os.path.join(dir3, "pm_profile"), "1\n")
+
+ self.start_daemon()
+
+ # Verify that both drivers are loaded
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "amd_pstate")
+ self.assertEqual(profiles[0]["PlatformDriver"], "platform_profile")
+
+ # test both drivers can switch to power-saver
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ # test both drivers can switch to performance
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ # test both drivers can switch to balanced
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("balanced"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # test when CPU driver fails to write
+ self.change_immutable(prefs1, True)
+ with self.assertRaises(gi.repository.GLib.GError):
+ self.set_dbus_property(
+ "ActiveProfile", GLib.Variant.new_string("power-saver")
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.assertEqual(
+ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"balanced"
+ )
+ self.change_immutable(prefs1, False)
+
+ # test when platform driver fails to write
+ self.change_immutable(profile, True)
+ with self.assertRaises(gi.repository.GLib.GError):
+ self.set_dbus_property(
+ "ActiveProfile", GLib.Variant.new_string("power-saver")
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # make sure CPU was undone since platform failed
+ self.assertEqual(
+ self.read_sysfs_file(
+ "sys/devices/system/cpu/cpufreq/policy0/energy_performance_preference"
+ ),
+ b"balance_performance",
+ )
+ self.assertEqual(
+ self.read_sysfs_file(
+ "sys/devices/system/cpu/cpufreq/policy1/energy_performance_preference"
+ ),
+ b"balance_performance",
+ )
+
+ # pylint: disable=too-many-statements
+ def test_amd_pstate(self):
+ """AMD P-State driver (no UPower)"""
+
+ # Create 2 CPUs with preferences
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+ dir2 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/"
+ )
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir2, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create AMD P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ # desktop PM profile
+ dir3 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir3)
+ self.write_file_contents(os.path.join(dir3, "pm_profile"), "1\n")
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "amd_pstate")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+
+ energy_prefs = os.path.join(dir2, "energy_performance_preference")
+ scaling_governor = os.path.join(dir2, "scaling_governor")
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+ self.assert_file_eventually_contains(scaling_governor, "powersave")
+
+ # Set performance mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ self.assert_file_eventually_contains(energy_prefs, "performance")
+ self.assert_file_eventually_contains(scaling_governor, "performance")
+
+ # Set powersave mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ self.assert_file_eventually_contains(energy_prefs, "power")
+ self.assert_file_eventually_contains(scaling_governor, "powersave")
+
+ def test_amd_pstate_balance(self):
+ """AMD P-State driver (balance)"""
+
+ # Create CPU with preference
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ gov_path = os.path.join(dir1, "scaling_governor")
+ self.write_file_contents(gov_path, "performance\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ # desktop PM profile
+ dir2 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "pm_profile"), "1\n")
+
+ self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "amd_pstate")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+
+ # This matches what's written by ppd-driver-amd-pstate.c
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ scaling_governor = os.path.join(dir1, "scaling_governor")
+ self.assert_file_eventually_contains(scaling_governor, "powersave")
+
+ def test_amd_pstate_error(self):
+ """AMD P-State driver in error state"""
+
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ pref_path = os.path.join(dir1, "energy_performance_preference")
+ old_umask = os.umask(0o333)
+ self.write_file_contents(pref_path, "balance_performance\n")
+ os.umask(old_umask)
+ # Make file non-writable to root
+ self.change_immutable(pref_path, True)
+
+ # desktop PM profile
+ dir2 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "pm_profile"), "1\n")
+
+ self.start_daemon()
+
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # Error when setting performance mode
+ with self.assertRaises(gi.repository.GLib.GError):
+ self.set_dbus_property(
+ "ActiveProfile", GLib.Variant.new_string("performance")
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance\n")
+
+ def test_amd_pstate_passive(self):
+ """AMD P-State in passive mode -> placeholder"""
+
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create AMD P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "passive\n")
+
+ # desktop PM profile
+ dir2 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "pm_profile"), "1\n")
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 2)
+ self.assertEqual(profiles[0]["Driver"], "placeholder")
+ self.assertEqual(profiles[0]["PlatformDriver"], "placeholder")
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ self.assert_file_eventually_contains(energy_prefs, "performance\n")
+
+ # Set performance mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ self.assert_file_eventually_contains(energy_prefs, "performance\n")
+
+ def test_amd_pstate_server(self):
+ # Create 2 CPUs with preferences
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+ dir2 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/"
+ )
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir2, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create AMD P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ # server PM profile
+ dir3 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir3)
+ self.write_file_contents(os.path.join(dir3, "pm_profile"), "4\n")
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 2)
+ with self.assertRaises(KeyError):
+ print(profiles[0]["CpuDriver"])
+
+ def test_dytc_performance_driver(self):
+ """Lenovo DYTC performance driver"""
+
+ self.create_dytc_device()
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "platform_profile")
+ self.assertEqual(profiles[0]["PlatformDriver"], "platform_profile")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+ self.assertEqual(profiles[2]["PlatformDriver"], "platform_profile")
+ self.assertEqual(profiles[2]["Profile"], "performance")
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ # lapmode detected
+ lapmode = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/thinkpad_acpi/dytc_lapmode"
+ )
+ self.write_file_contents(lapmode, "1\n")
+ self.assert_dbus_property_eventually_is("PerformanceDegraded", "lap-detected")
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ # Reset lapmode
+ self.write_file_contents(lapmode, "0\n")
+ self.assert_dbus_property_eventually_is("PerformanceDegraded", "")
+
+ # Performance mode didn't change
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ # Switch to power-saver mode
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assert_eventually(
+ lambda: self.read_sysfs_file("sys/firmware/acpi/platform_profile")
+ == b"low-power"
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ # And mimic a user pressing a Fn+H
+ platform_profile = os.path.join(
+ self.testbed.get_root_dir(), "sys/firmware/acpi/platform_profile"
+ )
+ self.write_file_contents(platform_profile, "performance\n")
+ self.assert_dbus_property_eventually_is("ActiveProfile", "performance")
+
+ def test_fake_driver(self):
+ """Test that the fake driver works"""
+
+ os.environ["POWER_PROFILE_DAEMON_FAKE_DRIVER"] = "1"
+ self.start_daemon()
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.stop_daemon()
+
+ del os.environ["POWER_PROFILE_DAEMON_FAKE_DRIVER"]
+ self.start_daemon()
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 2)
+
+ def test_amd_pstate_upower(self):
+ """Switching between balance_power and balance_performance based on battery"""
+ # Create 2 CPUs with preferences
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+ dir2 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/"
+ )
+ os.makedirs(dir2)
+ self.write_file_contents(os.path.join(dir2, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir2, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create AMD P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ # desktop PM profile
+ dir3 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(dir3)
+ self.write_file_contents(os.path.join(dir3, "pm_profile"), "1\n")
+
+ _, _, stop_upowerd = self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": True},
+ )
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+
+ self.assertEqual(profiles[0]["Driver"], "multiple")
+ self.assertEqual(profiles[0]["CpuDriver"], "amd_pstate")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+
+ energy_prefs = os.path.join(dir2, "energy_performance_preference")
+ scaling_governor = os.path.join(dir2, "scaling_governor")
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_power")
+ self.assert_file_eventually_contains(scaling_governor, "powersave")
+
+ stop_upowerd()
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ _, upowerd_obj, stop_upowerd = self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True)
+ self.assert_file_eventually_contains(energy_prefs, "balance_power")
+
+ # Ensure that changing some other property doesn't change the state.
+ upowerd_obj.Set("org.freedesktop.UPower", "LidIsClosed", True)
+ self.assert_file_eventually_contains(
+ energy_prefs, "balance_power", keep_checking=800
+ )
+
+ upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", False)
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ self.stop_daemon()
+
+ # start upower after the daemon
+ stop_upowerd()
+
+ self.start_daemon()
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ _, upowerd_obj, _ = self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True)
+ self.assert_file_eventually_contains(energy_prefs, "balance_power")
+
+ def test_amdgpu_panel_power(self):
+ """Verify AMDGPU Panel power actions"""
+ amdgpu_panel_power_savings = "amdgpu/panel_power_savings"
+ edp = self.testbed.add_device(
+ "drm",
+ "card1-eDP",
+ None,
+ ["status", "connected\n", amdgpu_panel_power_savings, "0"],
+ ["DEVTYPE", "drm_connector"],
+ )
+
+ self.create_amd_apu()
+
+ self.start_daemon()
+
+ self.assertIn("amdgpu_panel_power", self.get_dbus_property("Actions"))
+
+ # verify it hasn't been updated yet due to missing upower
+ self.assert_sysfs_attr_eventually_is(edp, amdgpu_panel_power_savings, "0")
+
+ # start upower and try again
+ self.stop_daemon()
+ self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": True},
+ )
+ self.start_daemon()
+
+ # verify balanced updated it
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("balanced"))
+ self.assert_sysfs_attr_eventually_is(edp, amdgpu_panel_power_savings, "1")
+
+ # verify power saver updated it
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assert_sysfs_attr_eventually_is(edp, amdgpu_panel_power_savings, "3")
+
+ # add another device that supports the feature
+ edp2 = self.testbed.add_device(
+ "drm",
+ "card2-eDP",
+ None,
+ ["status", "connected\n", amdgpu_panel_power_savings, "0"],
+ ["DEVTYPE", "drm_connector"],
+ )
+
+ # verify power saver got updated for it
+ self.assert_sysfs_attr_eventually_is(edp2, amdgpu_panel_power_savings, "3")
+
+ # add another device that supports the feature, but panel is disconnected
+ edp3 = self.testbed.add_device(
+ "drm",
+ "card3-eDP",
+ None,
+ ["status", "disconnected\n", amdgpu_panel_power_savings, "0"],
+ ["DEVTYPE", "drm_connector"],
+ )
+
+ # verify power saver didn't get updated for it
+ self.assert_sysfs_attr_eventually_is(edp3, amdgpu_panel_power_savings, "0")
+
+ def test_trickle_charge_system(self):
+ """Trickle power_supply charge type"""
+
+ fastcharge = self.testbed.add_device(
+ "power_supply",
+ "bq24190-charger",
+ None,
+ ["charge_type", "Trickle", "scope", "System"],
+ [],
+ )
+
+ self.start_daemon()
+
+ self.assertIn("trickle_charge", self.get_dbus_property("Actions"))
+
+ # Verify that charge-type stays untouched
+ self.assertEqual(self.read_sysfs_attr(fastcharge, "charge_type"), b"Trickle")
+
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.read_sysfs_attr(fastcharge, "charge_type"), b"Trickle")
+
+ def test_trickle_charge_mode_no_change(self):
+ """Trickle power_supply charge type"""
+
+ fastcharge = self.testbed.add_device(
+ "power_supply",
+ "MFi Fastcharge",
+ None,
+ ["charge_type", "Fast", "scope", "Device"],
+ [],
+ )
+
+ mtime = self.get_mtime(fastcharge, "charge_type")
+ self.start_daemon()
+
+ self.assertIn("trickle_charge", self.get_dbus_property("Actions"))
+
+ # Verify that charge-type didn't get touched
+ self.assert_sysfs_attr_eventually_is(fastcharge, "charge_type", "Fast")
+ self.assertEqual(self.get_mtime(fastcharge, "charge_type"), mtime)
+
+ def test_trickle_charge_mode(self):
+ """Trickle power_supply charge type"""
+
+ idevice = self.testbed.add_device(
+ "usb",
+ "iDevice",
+ None,
+ [],
+ ["ID_MODEL", "iDevice", "DRIVER", "apple-mfi-fastcharge"],
+ )
+ fastcharge = self.testbed.add_device(
+ "power_supply",
+ "MFi Fastcharge",
+ idevice,
+ ["charge_type", "Trickle", "scope", "Device"],
+ [],
+ )
+
+ self.start_daemon()
+
+ self.assertIn("trickle_charge", self.get_dbus_property("Actions"))
+
+ # Verify that charge-type got changed to Fast on startup
+ self.assert_sysfs_attr_eventually_is(fastcharge, "charge_type", "Fast")
+
+ # Verify that charge-type got changed to Trickle when power saving
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assert_sysfs_attr_eventually_is(fastcharge, "charge_type", "Trickle")
+
+ # FIXME no performance mode
+ # Verify that charge-type got changed to Fast in a non-default, non-power save mode
+ # self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
+ # self.assert_sysfs_attr_eventually_is(fastcharge, "charge_type", "Fast")
+
+ def test_platform_driver_late_load(self):
+ """Test that we can handle the platform_profile driver getting loaded late"""
+ self.create_empty_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 2)
+
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile_choices"),
+ "low-power\nbalanced\nperformance\n",
+ )
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile"), "performance\n"
+ )
+
+ # Wait for profiles to get reloaded
+ self.assert_eventually(lambda: len(self.get_dbus_property("Profiles")) == 3)
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ # Was set in platform_profile before we loaded the drivers
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "")
+
+ def test_hp_wmi(self):
+ # Uses cool instead of low-power
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(acpi_dir)
+ self.write_file_contents(os.path.join(acpi_dir, "platform_profile"), "cool\n")
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile_choices"),
+ "cool balanced performance\n",
+ )
+
+ self.start_daemon()
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "platform_profile")
+ self.assertEqual(profiles[0]["PlatformDriver"], "platform_profile")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.assertEqual(
+ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"cool"
+ )
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.assertEqual(
+ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"cool"
+ )
+
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("performance"))
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("balanced"))
+ self.assertEqual(
+ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"balanced"
+ )
+
+ def test_quiet(self):
+ # Uses quiet instead of low-power
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(acpi_dir)
+ self.write_file_contents(os.path.join(acpi_dir, "platform_profile"), "quiet\n")
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile_choices"),
+ "quiet balanced balanced-performance performance\n",
+ )
+
+ self.start_daemon()
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]["Driver"], "platform_profile")
+ self.assertEqual(profiles[0]["PlatformDriver"], "platform_profile")
+ self.assertEqual(profiles[0]["Profile"], "power-saver")
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.assertEqual(
+ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"balanced"
+ )
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.assertEqual(
+ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"quiet"
+ )
+
+ def test_hold_release_profile(self):
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+
+ cookie = self.call_dbus_method(
+ "HoldProfile",
+ GLib.Variant("(sss)", ("performance", "testReason", "testApplication")),
+ )
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfile") == "performance"
+ )
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfileHolds")
+ == [
+ {
+ "ApplicationId": "testApplication",
+ "Profile": "performance",
+ "Reason": "testReason",
+ }
+ ]
+ )
+
+ released_cookie = None
+
+ def signal_cb(_, sender, signal_name, params):
+ nonlocal released_cookie
+ if signal_name == "ProfileReleased":
+ released_cookie = params
+
+ self.addCleanup(
+ self.proxy.disconnect, self.proxy.connect("g-signal", signal_cb)
+ )
+
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ profile_holds = self.get_dbus_property("ActiveProfileHolds")
+ self.assertEqual(len(profile_holds), 1)
+ self.assertEqual(profile_holds[0]["Profile"], "performance")
+ self.assertEqual(profile_holds[0]["Reason"], "testReason")
+ self.assertEqual(profile_holds[0]["ApplicationId"], "testApplication")
+
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", cookie))
+ self.assert_eventually(lambda: released_cookie == cookie)
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfile") == "balanced"
+ )
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfileHolds") == []
+ )
+ profile_holds = self.get_dbus_property("ActiveProfileHolds")
+ self.assertEqual(len(profile_holds), 0)
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # When the profile is changed manually, holds should be released a
+ self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assertEqual(len(self.get_dbus_property("ActiveProfileHolds")), 1)
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfile") == "performance"
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("balanced"))
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfile") == "balanced"
+ )
+ self.assertEqual(len(self.get_dbus_property("ActiveProfileHolds")), 0)
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # When all holds are released, the last manually selected profile should be activated
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfile") == "power-saver"
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfileHolds")
+ == [
+ {
+ "ApplicationId": "",
+ "Profile": "performance",
+ "Reason": "",
+ }
+ ]
+ )
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfile") == "performance"
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", cookie))
+ self.assert_eventually(lambda: released_cookie == cookie)
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfileHolds") == []
+ )
+ self.assert_eventually(
+ lambda: self.changed_properties.get("ActiveProfile") == "power-saver"
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ def test_launch_arguments_redirection(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ with tempfile.NamedTemporaryFile(mode="+rt") as tmpf:
+ subprocess.check_call(
+ self.powerprofilesctl_command()
+ + [
+ "launch",
+ "sh",
+ "-c",
+ f'echo "$@" > {tmpf.name}',
+ "--",
+ "--foo",
+ "--bar",
+ "-v",
+ "arg",
+ ]
+ )
+ self.assertEqual(tmpf.readlines(), ["--foo --bar -v arg\n"])
+
+ def test_unknown_action(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ with self.assertRaises(subprocess.CalledProcessError):
+ tool_cmd = self.powerprofilesctl_command()
+ subprocess.check_output(
+ tool_cmd + ["hopefully-invalid-action"], stderr=subprocess.PIPE
+ )
+
+ def test_unknown_list_argument(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ with self.assertRaises(subprocess.CalledProcessError):
+ subprocess.check_output(
+ self.powerprofilesctl_command() + ["list", "--invalid-argument"],
+ stderr=subprocess.PIPE,
+ )
+
+ def test_launch_arguments_invalid(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ with self.assertRaises(subprocess.CalledProcessError):
+ tool_cmd = self.powerprofilesctl_command()
+ subprocess.check_output(
+ tool_cmd + ["--foo-arg", "launch", "true"], stderr=subprocess.PIPE
+ )
+
+ def test_launch_with_command_failure(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ tool_cmd = self.powerprofilesctl_command()
+ cmd = subprocess.run(tool_cmd + ["launch", "false"], check=False)
+ self.assertEqual(cmd.returncode, 1)
+
+ cmd = subprocess.run(tool_cmd + ["launch", "sh", "-c", "exit 55"], check=False)
+ self.assertEqual(cmd.returncode, 55)
+
+ def test_launch_with_command_signaled(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ tool_cmd = self.powerprofilesctl_command()
+ cmd = subprocess.run(
+ tool_cmd + ["launch", "sh", "-c", f"kill -{signal.SIGKILL} $$"], check=False
+ )
+ self.assertEqual(cmd.returncode, -signal.SIGKILL)
+
+ cmd = subprocess.run(
+ tool_cmd + ["launch", "sh", "-c", f"kill -{signal.SIGINT} $$"], check=False
+ )
+ self.assertEqual(cmd.returncode, -signal.SIGINT)
+
+ def test_vanishing_hold(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ tool_cmd = self.powerprofilesctl_command()
+ with subprocess.Popen(
+ tool_cmd + ["launch", "-p", "power-saver", "sleep", "3600"],
+ stdout=sys.stdout,
+ stderr=sys.stderr,
+ ) as launch_process:
+ self.assertTrue(launch_process)
+ time.sleep(1)
+ holds = self.get_dbus_property("ActiveProfileHolds")
+ self.assertEqual(len(holds), 1)
+ hold = holds[0]
+ self.assertEqual(hold["Profile"], "power-saver")
+
+ # Make sure to handle vanishing clients
+ launch_process.terminate()
+ retcode = launch_process.wait()
+ self.assertEqual(retcode, -signal.SIGTERM)
+
+ holds = self.get_dbus_property("ActiveProfileHolds")
+ self.assertEqual(len(holds), 0)
+
+ def test_launch_sigint_wrapper(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ with subprocess.Popen(
+ self.powerprofilesctl_command() + ["launch", "sleep", "3600"],
+ ) as launch_process:
+ time.sleep(1)
+ launch_process.send_signal(signal.SIGINT)
+ retcode = launch_process.wait()
+ self.assertEqual(retcode, -signal.SIGINT)
+
+ def test_launch_sigabrt_wrapper(self):
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile"))
+
+ with subprocess.Popen(
+ self.powerprofilesctl_command() + ["launch", "sleep", "3600"],
+ ) as launch_process:
+ time.sleep(1)
+ launch_process.send_signal(signal.SIGABRT)
+ retcode = launch_process.wait()
+ self.assertEqual(retcode, -signal.SIGABRT)
+
+ def test_hold_priority(self):
+ """power-saver should take priority over performance"""
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # Test every order of holding and releasing power-saver and performance
+ # hold performance and then power-saver, release in the same order
+ performance_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ powersaver_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("power-saver", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", performance_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", powersaver_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # hold performance and then power-saver, but release power-saver first
+ performance_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ powersaver_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("power-saver", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", powersaver_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", performance_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # hold power-saver and then performance, release in the same order
+ powersaver_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("power-saver", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ performance_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", powersaver_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", performance_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ # hold power-saver and then performance, but release performance first
+ powersaver_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("power-saver", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ performance_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", performance_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", powersaver_cookie))
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ def test_save_profile(self):
+ """save profile across runs"""
+
+ self.create_platform_profile()
+
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.stop_daemon()
+
+ # sys.stderr.write('\n-------------- config file: ----------------\n')
+ # with open(self.testbed.get_root_dir() + '/' + 'ppd_test_conf.ini') as tmpf:
+ # sys.stderr.write(tmpf.read())
+ # sys.stderr.write('------------------------------\n')
+
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+ # Programmatically set profile aren't saved
+ performance_cookie = self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assertTrue(performance_cookie)
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance")
+ self.stop_daemon()
+
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ def test_save_deferred_load(self):
+ """save profile across runs, but kernel driver loaded after start"""
+
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.set_dbus_property("ActiveProfile", GLib.Variant.new_string("power-saver"))
+ self.stop_daemon()
+ self.remove_platform_profile()
+
+ # We could verify the contents of the configuration file here
+
+ self.create_empty_platform_profile()
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile_choices"),
+ "low-power\nbalanced\nperformance\n",
+ )
+ self.write_file_contents(
+ os.path.join(acpi_dir, "platform_profile"), "performance\n"
+ )
+
+ self.assert_dbus_property_eventually_is("ActiveProfile", "power-saver")
+
+ def test_not_allowed_profile(self):
+ """Check that we get errors when trying to change a profile and not allowed"""
+
+ self.obj_polkit.SetAllowed(dbus.Array([], signature="s"))
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ proxy = Gio.DBusProxy.new_sync(
+ self.dbus,
+ Gio.DBusProxyFlags.DO_NOT_AUTO_START,
+ None,
+ self.PP,
+ self.PP_PATH,
+ "org.freedesktop.DBus.Properties",
+ None,
+ )
+ with self.assertRaises(gi.repository.GLib.GError) as error:
+ proxy.Set(
+ "(ssv)",
+ self.PP,
+ "ActiveProfile",
+ GLib.Variant.new_string("power-saver"),
+ )
+ self.assertIn("AccessDenied", str(error.exception))
+
+ def test_not_allowed_hold(self):
+ """Check that we get an error when trying to hold a profile and not allowed"""
+
+ self.obj_polkit.SetAllowed(dbus.Array([], signature="s"))
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ with self.assertRaises(gi.repository.GLib.GError) as error:
+ self.call_dbus_method(
+ "HoldProfile", GLib.Variant("(sss)", ("performance", "", ""))
+ )
+ self.assertIn("AccessDenied", str(error.exception))
+
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+ self.assertEqual(len(self.get_dbus_property("ActiveProfileHolds")), 0)
+
+ def test_get_version_prop(self):
+ """Checks that the version property is advertised"""
+ self.start_daemon()
+ self.assertTrue(self.get_dbus_property("Version"))
+
+ def test_intel_pstate_upower(self):
+ # Create CPU with preference
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create Intel P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "1\n")
+ self.write_file_contents(os.path.join(pstate_dir, "turbo_pct"), "0\n")
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ _, _, stop_upowerd = self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": True},
+ )
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "")
+
+ energy_prefs = os.path.join(dir1, "energy_performance_preference")
+ scaling_governor = os.path.join(dir1, "scaling_governor")
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_power")
+ self.assert_file_eventually_contains(scaling_governor, "powersave")
+
+ stop_upowerd()
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ _, upowerd_obj, stop_upowerd = self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True)
+ self.assert_file_eventually_contains(energy_prefs, "balance_power")
+
+ upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", False)
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ self.stop_daemon()
+
+ # start upower after the daemon
+ stop_upowerd()
+
+ self.start_daemon()
+
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ _, upowerd_obj, _ = self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+ self.assert_file_eventually_contains(energy_prefs, "balance_performance")
+
+ upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True)
+ self.assert_file_eventually_contains(energy_prefs, "balance_power")
+
+ def test_intel_pstate_noturbo(self):
+ """Intel P-State driver (balance)"""
+
+ # Create CPU with preference
+ dir1 = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/"
+ )
+ os.makedirs(dir1)
+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n")
+ self.write_file_contents(
+ os.path.join(dir1, "energy_performance_preference"), "performance\n"
+ )
+
+ # Create Intel P-State configuration
+ pstate_dir = os.path.join(
+ self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate"
+ )
+ os.makedirs(pstate_dir)
+ self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "1\n")
+ self.write_file_contents(os.path.join(pstate_dir, "turbo_pct"), "0\n")
+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n")
+
+ self.start_daemon()
+
+ profiles = self.get_dbus_property("Profiles")
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "")
+
+ def test_powerprofilesctl_version_command(self):
+ """Check powerprofilesctl version command works"""
+
+ self.start_daemon()
+
+ cmd = subprocess.run(self.powerprofilesctl_command() + ["version"], check=True)
+ self.assertEqual(cmd.returncode, 0)
+
+ def test_powerprofilesctl_list_command(self):
+ """Check powerprofilesctl list command works"""
+
+ self.start_daemon()
+
+ tool_cmd = self.powerprofilesctl_command()
+ cmd = subprocess.run(tool_cmd + ["list"], capture_output=True, check=True)
+ self.assertEqual(cmd.returncode, 0)
+ self.assertIn("* balanced", cmd.stdout.decode("utf-8"))
+
+ def test_powerprofilesctl_set_get_commands(self):
+ """Check powerprofilesctl set/get command works"""
+
+ self.start_daemon()
+
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced")
+
+ tool_cmd = self.powerprofilesctl_command()
+ cmd = subprocess.run(tool_cmd + ["get"], capture_output=True, check=True)
+ self.assertEqual(cmd.returncode, 0)
+ self.assertEqual(cmd.stdout, b"balanced\n")
+
+ cmd = subprocess.run(
+ tool_cmd + ["set", "power-saver"], capture_output=True, check=True
+ )
+ self.assertEqual(cmd.returncode, 0)
+
+ self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver")
+
+ cmd = subprocess.run(tool_cmd + ["get"], capture_output=True, check=True)
+ self.assertEqual(cmd.returncode, 0)
+ self.assertEqual(cmd.stdout, b"power-saver\n")
+
+ def test_powerprofilesctl_error(self):
+ """Check that powerprofilesctl returns 1 rather than an exception on error"""
+
+ tool_cmd = self.powerprofilesctl_command()
+ with self.assertRaises(subprocess.CalledProcessError) as error:
+ subprocess.check_output(
+ tool_cmd + ["list"], stderr=subprocess.PIPE, universal_newlines=True
+ )
+ self.assertNotIn("Traceback", error.exception.stderr)
+
+ with self.assertRaises(subprocess.CalledProcessError) as error:
+ subprocess.check_output(
+ tool_cmd + ["get"], stderr=subprocess.PIPE, universal_newlines=True
+ )
+ self.assertNotIn("Traceback", error.exception.stderr)
+
+ with self.assertRaises(subprocess.CalledProcessError) as error:
+ subprocess.check_output(
+ tool_cmd + ["set", "not-a-profile"],
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ )
+ self.assertNotIn("Traceback", error.exception.stderr)
+
+ with self.assertRaises(subprocess.CalledProcessError) as error:
+ subprocess.check_output(
+ tool_cmd + ["list-holds"],
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ )
+ self.assertNotIn("Traceback", error.exception.stderr)
+
+ with self.assertRaises(subprocess.CalledProcessError) as error:
+ subprocess.check_output(
+ tool_cmd + ["launch", "-p", "power-saver", "sleep", "1"],
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ )
+ self.assertNotIn("Traceback", error.exception.stderr)
+
+ self.start_daemon()
+ with self.assertRaises(subprocess.CalledProcessError) as error:
+ subprocess.check_output(
+ tool_cmd + ["set", "not-a-profile"],
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ )
+ self.assertNotIn("Traceback", error.exception.stderr)
+
+ #
+ # Helper methods
+ #
+
+ @classmethod
+ def _props_to_str(cls, properties):
+ """Convert a properties dictionary to uevent text representation."""
+
+ prop_str = ""
+ if properties:
+ for key, val in properties.items():
+ prop_str += f"{key}={val}\n"
+ return prop_str
+
+
+class LegacyDBusNameTests(Tests):
+ """This will repeats all the tests in the Tests class using the legacy dbus name"""
+
+ PP = "net.hadess.PowerProfiles"
+ PP_PATH = "/net/hadess/PowerProfiles"
+ PP_INTERFACE = "net.hadess.PowerProfiles"
+
+
+if __name__ == "__main__":
+ # run ourselves under umockdev
+ if "umockdev" not in os.environ.get("LD_PRELOAD", ""):
+ os.execvp("umockdev-wrapper", ["umockdev-wrapper", sys.executable] + sys.argv)
+
+ prog = unittest.main(exit=False)
+ if prog.result.errors or prog.result.failures:
+ sys.exit(1)
+
+ # Translate to skip error
+ if prog.result.testsRun == len(prog.result.skipped):
+ sys.exit(77)
diff -Nru power-profiles-daemon-0.10.1/tests/meson.build power-profiles-daemon-0.21/tests/meson.build
--- power-profiles-daemon-0.10.1/tests/meson.build 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/tests/meson.build 2024-04-03 23:55:02.000000000 +0000
@@ -1,18 +1,95 @@
envs = environment()
-envs.set ('top_builddir', meson.build_root())
-envs.set ('top_srcdir', meson.source_root())
+envs.set('PPD_TEST_VERBOSE', 'true')
+envs.set ('top_builddir', meson.project_build_root())
-python3 = find_program('python3')
unittest_inspector = find_program('unittest_inspector.py')
-r = run_command(unittest_inspector, files('integration-test.py'))
+integration_tests = files('integration_test.py')
+r = run_command(python3, unittest_inspector, integration_tests, check: true)
unit_tests = r.stdout().strip().split('\n')
+valgrind = find_program('valgrind', required: false)
+if valgrind.found()
+ glib_share = glib_dep.get_variable('prefix') / 'share' / glib_dep.name()
+ glib_suppressions = glib_share + '/valgrind/glib.supp'
+ libfprint_wrapper = [
+ valgrind.full_path(),
+ '--tool=memcheck',
+ '--leak-check=full',
+ '--leak-resolution=high',
+ '--error-exitcode=1',
+ '--errors-for-leak-kinds=definite',
+ '--track-origins=yes',
+ '--show-leak-kinds=definite,possible',
+ '--show-error-list=yes',
+ '--gen-suppressions=all',
+ '--suppressions=' + glib_suppressions,
+ ]
+ add_test_setup('valgrind',
+ timeout_multiplier: 5,
+ env: [
+ 'G_SLICE=always-malloc',
+ 'UNDER_VALGRIND=1',
+ 'PPD_TEST_WRAPPER=' + ' '.join(libfprint_wrapper),
+ ])
+endif
+
+preloaded_libs = []
+ppd_tests_ld_preload = []
+
+if address_sanitizer
+ # ASAN has to be the first in list
+ preloaded_libs += 'asan'
+endif
+
+foreach libname: preloaded_libs
+ lib = run_command(meson.get_compiler('c'),
+ '-print-file-name=lib@0@.so'.format(libname),
+ check: true,
+ ).stdout().strip()
+
+ # Support linker script files
+ if run_command('grep', '-qI', '^INPUT', files(lib), check: false).returncode() == 0
+ out = run_command('cat', lib, check: true).stdout()
+ lib = out.split('(')[1].split(')')[0].strip()
+ endif
+
+ if lib != '' and lib[0] == '/'
+ message('Found library @0@ as @1@'.format(libname, lib))
+ ppd_tests_ld_preload += '@0@'.format(files(lib)[0])
+ else
+ tests = []
+ warning('No library found for ' + libname + ', skipping PAM tests')
+ endif
+endforeach
+
+envs.set('PPD_LD_PRELOAD', ' '.join(ppd_tests_ld_preload))
+
+coverage_args = []
+python3_coverage = find_program([
+ 'python3-coverage',
+ 'coverage3',
+ 'coverage',
+ ], required: false)
+if python3_coverage.found()
+ envs.set('PPD_PYTHON_COVERAGE', python3_coverage.full_path())
+endif
+
foreach ut: unit_tests
- ut_args = files('integration-test.py')
- ut_args += ut
test(ut,
python3,
- args: ut_args,
+ args: [
+ integration_tests,
+ ut,
+ ],
env: envs,
)
endforeach
+
+if pylint.found()
+ integration_pylint_flags = ['-d', 'W0511', '-d', 'C0302'] + pylint_flags
+ test('pylint-integration-tests',
+ pylint,
+ args: integration_pylint_flags + integration_tests,
+ env: nomalloc,
+ )
+endif
diff -Nru power-profiles-daemon-0.10.1/tests/unittest_inspector.py power-profiles-daemon-0.21/tests/unittest_inspector.py
--- power-profiles-daemon-0.10.1/tests/unittest_inspector.py 2021-10-28 09:40:21.808133000 +0000
+++ power-profiles-daemon-0.21/tests/unittest_inspector.py 2024-04-03 23:55:02.000000000 +0000
@@ -22,23 +22,25 @@
import os
import unittest
+
def list_tests(module):
tests = []
for name, obj in inspect.getmembers(module):
if inspect.isclass(obj) and issubclass(obj, unittest.TestCase):
cases = unittest.defaultTestLoader.getTestCaseNames(obj)
- tests += [ (obj, '{}.{}'.format(name, t)) for t in cases ]
+ tests += [(obj, "{}.{}".format(name, t)) for t in cases]
return tests
-if __name__ == '__main__':
+if __name__ == "__main__":
parser = argparse.ArgumentParser()
- parser.add_argument('unittest_source', type=argparse.FileType('r'))
+ parser.add_argument("unittest_source", type=argparse.FileType("r"))
args = parser.parse_args()
source_path = args.unittest_source.name
spec = importlib.util.spec_from_file_location(
- os.path.basename(source_path), source_path)
+ os.path.basename(source_path), source_path
+ )
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)