1
Fork 0
mirror of https://github.com/RGBCube/superfreq synced 2025-07-27 17:07:44 +00:00

Merge branch 'main' into better-battery-mgnmnt

This commit is contained in:
raf 2025-05-16 01:57:23 +03:00 committed by GitHub
commit f4fceed893
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 79 additions and 44 deletions

View file

@ -135,7 +135,7 @@ more than issue reports, as supporting hardware _needs_ hardware.
Superfreq uses TOML configuration files. Default locations: Superfreq uses TOML configuration files. Default locations:
- `/etc/superfreq/config.toml` - `/etc/xdg/superfreq/config.toml`
- `/etc/superfreq.toml` - `/etc/superfreq.toml`
You can also specify a custom path by setting the `SUPERFREQ_CONFIG` environment You can also specify a custom path by setting the `SUPERFREQ_CONFIG` environment

View file

@ -5,18 +5,21 @@ inputs: {
... ...
}: let }: let
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption mkPackageOption;
inherit (lib.types) submodule; inherit (lib.types) submodule;
inherit (lib.lists) optional;
inherit (lib.meta) getExe; inherit (lib.meta) getExe;
cfg = config.programs.superfreq; cfg = config.services.superfreq;
defaultPackage = inputs.self.packages.${pkgs.stdenv.system}.default;
format = pkgs.formats.toml {}; format = pkgs.formats.toml {};
cfgFile = format.generate "superfreq-config.toml" cfg.settings;
in { in {
options.programs.superfreq = { options.services.superfreq = {
enable = mkEnableOption "Automatic CPU speed & power optimizer for Linux"; enable = mkEnableOption "Automatic CPU speed & power optimizer for Linux";
package = mkPackageOption inputs.self.packages.${pkgs.stdenv.system} "superfreq" {
pkgsText = "self.packages.\${pkgs.stdenv.system}";
};
settings = mkOption { settings = mkOption {
default = {}; default = {};
@ -26,23 +29,18 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [defaultPackage]; environment.systemPackages = [cfg.package];
systemd = { systemd.services.superfreq = {
packages = [defaultPackage]; wantedBy = ["multi-user.target"];
services.superfreq = { serviceConfig = {
wantedBy = ["multi-user.target"]; Environment = optional (cfg.settings != {}) ["SUPERFREQ_CONFIG=${cfgFile}"];
serviceConfig = let WorkingDirectory = "";
cfgFile = format.generate "superfreq-config.toml" cfg.settings; ExecStart = "${getExe cfg.package} daemon --verbose";
in { Restart = "on-failure";
Environment = ["SUPERFREQ_CONFIG=${cfgFile}"];
WorkingDirectory = "";
ExecStart = "${getExe defaultPackage} daemon --verbose";
Restart = "on-failure";
RuntimeDirectory = "superfreq"; RuntimeDirectory = "superfreq";
RuntimeDirectoryMode = "0755"; RuntimeDirectoryMode = "0755";
};
}; };
}; };
@ -55,9 +53,9 @@ in {
''; '';
} }
{ {
assertion = !config.programs.auto-cpufreq.enable; assertion = !config.services.auto-cpufreq.enable;
message = '' message = ''
You have set programs.auto-cpufreq.enable = true; You have set services.auto-cpufreq.enable = true;
which conflicts with Superfreq. which conflicts with Superfreq.
''; '';
} }

View file

@ -46,7 +46,7 @@ pub fn load_config_from_path(specific_path: Option<&str>) -> Result<AppConfig, C
} }
// System-wide paths // System-wide paths
config_paths.push(PathBuf::from("/etc/superfreq/config.toml")); config_paths.push(PathBuf::from("/etc/xdg/superfreq/config.toml"));
config_paths.push(PathBuf::from("/etc/superfreq.toml")); config_paths.push(PathBuf::from("/etc/superfreq.toml"));
for path in config_paths { for path in config_paths {

View file

@ -1,6 +1,7 @@
use crate::core::{GovernorOverrideMode, TurboSetting}; use crate::core::{GovernorOverrideMode, TurboSetting};
use crate::util::error::ControlError; use crate::util::error::ControlError;
use core::str; use core::str;
use std::path::PathBuf;
use std::{fs, io, path::Path, string::ToString}; use std::{fs, io, path::Path, string::ToString};
pub type Result<T, E = ControlError> = std::result::Result<T, E>; pub type Result<T, E = ControlError> = std::result::Result<T, E>;
@ -18,12 +19,15 @@ const VALID_EPB_STRINGS: &[&str] = &[
// Write a value to a sysfs file // Write a value to a sysfs file
fn write_sysfs_value(path: impl AsRef<Path>, value: &str) -> Result<()> { fn write_sysfs_value(path: impl AsRef<Path>, value: &str) -> Result<()> {
let p = path.as_ref(); let p = path.as_ref();
fs::write(p, value).map_err(|e| { fs::write(p, value).map_err(|e| {
let error_msg = format!("Path: {:?}, Value: '{}', Error: {}", p.display(), value, e); let error_msg = format!("Path: {:?}, Value: '{}', Error: {}", p.display(), value, e);
if e.kind() == io::ErrorKind::PermissionDenied { match e.kind() {
ControlError::PermissionDenied(error_msg) io::ErrorKind::PermissionDenied => ControlError::PermissionDenied(error_msg),
} else { io::ErrorKind::NotFound => {
ControlError::WriteError(error_msg) ControlError::PathMissing(format!("Path '{}' does not exist", p.display()))
}
_ => ControlError::WriteError(error_msg),
} }
}) })
} }
@ -136,6 +140,7 @@ fn get_available_governors() -> Result<Vec<String>> {
let path = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors"; let path = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors";
if !Path::new(path).exists() { if !Path::new(path).exists() {
return Err(ControlError::NotSupported( return Err(ControlError::NotSupported(
"Could not determine available governors".to_string(), "Could not determine available governors".to_string(),
)); ));
@ -487,7 +492,7 @@ const GOVERNOR_OVERRIDE_PATH: &str = "/etc/superfreq/governor_override";
/// Force a specific CPU governor or reset to automatic mode /// Force a specific CPU governor or reset to automatic mode
pub fn force_governor(mode: GovernorOverrideMode) -> Result<()> { pub fn force_governor(mode: GovernorOverrideMode) -> Result<()> {
// Create directory if it doesn't exist // Create directory if it doesn't exist
let dir_path = Path::new("/etc/superfreq"); let dir_path = Path::new("/etc/xdg/superfreq");
if !dir_path.exists() { if !dir_path.exists() {
fs::create_dir_all(dir_path).map_err(|e| { fs::create_dir_all(dir_path).map_err(|e| {
if e.kind() == io::ErrorKind::PermissionDenied { if e.kind() == io::ErrorKind::PermissionDenied {

View file

@ -65,7 +65,7 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box<dyn st
Some(path) Some(path)
} else { } else {
// Check standard config paths // Check standard config paths
let default_paths = ["/etc/superfreq/config.toml", "/etc/superfreq.toml"]; let default_paths = ["/etc/xdg/superfreq/config.toml", "/etc/superfreq.toml"];
default_paths default_paths
.iter() .iter()

View file

@ -390,25 +390,41 @@ pub fn get_all_cpu_core_info() -> Result<Vec<CpuCoreInfo>> {
} }
pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo { pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
// FIXME: Assume global settings can be read from cpu0 or are consistent. // Find a valid CPU to read global settings from
// This might not work properly for heterogeneous systems (e.g. big.LITTLE) // Try cpu0 first, then fall back to any available CPU with cpufreq
let cpufreq_base_path = Path::new("/sys/devices/system/cpu/cpu0/cpufreq/"); let mut cpufreq_base_path_buf = PathBuf::from("/sys/devices/system/cpu/cpu0/cpufreq/");
if !cpufreq_base_path_buf.exists() {
let core_count = get_logical_core_count().unwrap_or_else(|e| {
eprintln!("Warning: {e}");
0
});
let path = (0..core_count)
.map(|i| PathBuf::from(format!("/sys/devices/system/cpu/cpu{i}/cpufreq/")))
.find(|path| path.exists());
if let Some(test_path_buf) = path {
cpufreq_base_path_buf = test_path_buf;
}
}
let turbo_status_path = Path::new("/sys/devices/system/cpu/intel_pstate/no_turbo"); let turbo_status_path = Path::new("/sys/devices/system/cpu/intel_pstate/no_turbo");
let boost_path = Path::new("/sys/devices/system/cpu/cpufreq/boost"); let boost_path = Path::new("/sys/devices/system/cpu/cpufreq/boost");
let current_governor = if cpufreq_base_path.join("scaling_governor").exists() {
read_sysfs_file_trimmed(cpufreq_base_path.join("scaling_governor")).ok() let current_governor = if cpufreq_base_path_buf.join("scaling_governor").exists() {
read_sysfs_file_trimmed(cpufreq_base_path_buf.join("scaling_governor")).ok()
} else { } else {
None None
}; };
let available_governors = if cpufreq_base_path let available_governors = if cpufreq_base_path_buf
.join("scaling_available_governors") .join("scaling_available_governors")
.exists() .exists()
{ {
read_sysfs_file_trimmed(cpufreq_base_path.join("scaling_available_governors")).map_or_else( read_sysfs_file_trimmed(cpufreq_base_path_buf.join("scaling_available_governors"))
|_| vec![], .map_or_else(
|s| s.split_whitespace().map(String::from).collect(), |_| vec![],
) |s| s.split_whitespace().map(String::from).collect(),
)
} else { } else {
vec![] vec![]
}; };
@ -424,17 +440,16 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
} else { } else {
None None
}; };
// EPP (Energy Performance Preference) // EPP (Energy Performance Preference)
let energy_perf_pref = let energy_perf_pref =
read_sysfs_file_trimmed(cpufreq_base_path.join("energy_performance_preference")).ok(); read_sysfs_file_trimmed(cpufreq_base_path_buf.join("energy_performance_preference")).ok();
// EPB (Energy Performance Bias) // EPB (Energy Performance Bias)
let energy_perf_bias = let energy_perf_bias =
read_sysfs_file_trimmed(cpufreq_base_path.join("energy_performance_bias")).ok(); read_sysfs_file_trimmed(cpufreq_base_path_buf.join("energy_performance_bias")).ok();
let platform_profile = read_sysfs_file_trimmed("/sys/firmware/acpi/platform_profile").ok(); let platform_profile = read_sysfs_file_trimmed("/sys/firmware/acpi/platform_profile").ok();
let _platform_profile_choices =
read_sysfs_file_trimmed("/sys/firmware/acpi/platform_profile_choices").ok();
// Calculate average CPU temperature from the core temperatures // Calculate average CPU temperature from the core temperatures
let average_temperature_celsius = if cpu_cores.is_empty() { let average_temperature_celsius = if cpu_cores.is_empty() {
@ -458,6 +473,7 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
} }
}; };
// Return the constructed CpuGlobalInfo
CpuGlobalInfo { CpuGlobalInfo {
current_governor, current_governor,
available_governors, available_governors,

View file

@ -9,6 +9,10 @@ pub enum ControlError {
NotSupported(String), NotSupported(String),
PermissionDenied(String), PermissionDenied(String),
InvalidProfile(String), InvalidProfile(String),
InvalidGovernor(String),
ParseError(String),
ReadError(String),
PathMissing(String),
} }
impl From<io::Error> for ControlError { impl From<io::Error> for ControlError {
@ -37,6 +41,18 @@ impl std::fmt::Display for ControlError {
"Invalid platform control profile {s} supplied, please provide a valid one." "Invalid platform control profile {s} supplied, please provide a valid one."
) )
} }
Self::InvalidGovernor(s) => {
write!(f, "Invalid governor: {s}")
}
Self::ParseError(s) => {
write!(f, "Failed to parse value: {s}")
}
Self::ReadError(s) => {
write!(f, "Failed to read sysfs path: {s}")
}
Self::PathMissing(s) => {
write!(f, "Path missing: {s}")
}
} }
} }
} }