mirror of
https://github.com/RGBCube/superfreq
synced 2025-07-27 17:07:44 +00:00
cpu&power: share fs impls
This commit is contained in:
parent
c062327457
commit
91cef3b8b1
5 changed files with 104 additions and 65 deletions
60
src/cpu.rs
60
src/cpu.rs
|
@ -1,33 +1,9 @@
|
||||||
use anyhow::{Context, bail};
|
use anyhow::{Context, bail};
|
||||||
use yansi::Paint as _;
|
use yansi::Paint as _;
|
||||||
|
|
||||||
use std::{fmt, fs, path::Path, string::ToString};
|
use std::{fmt, string::ToString};
|
||||||
|
|
||||||
fn exists(path: impl AsRef<Path>) -> bool {
|
use crate::fs;
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
path.exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not doing any anyhow stuff here as all the calls of this ignore errors.
|
|
||||||
fn read_u64(path: impl AsRef<Path>) -> anyhow::Result<u64> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
let content = fs::read_to_string(path)?;
|
|
||||||
|
|
||||||
Ok(content.trim().parse()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(path: impl AsRef<Path>, value: &str) -> anyhow::Result<()> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
fs::write(path, value).with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to write '{value}' to '{path}'",
|
|
||||||
path = path.display(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Cpu {
|
pub struct Cpu {
|
||||||
|
@ -96,11 +72,11 @@ impl Cpu {
|
||||||
pub fn rescan(&mut self) -> anyhow::Result<()> {
|
pub fn rescan(&mut self) -> anyhow::Result<()> {
|
||||||
let Self { number, .. } = self;
|
let Self { number, .. } = self;
|
||||||
|
|
||||||
if !exists(format!("/sys/devices/system/cpu/cpu{number}")) {
|
if !fs::exists(format!("/sys/devices/system/cpu/cpu{number}")) {
|
||||||
bail!("{self} does not exist");
|
bail!("{self} does not exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
let has_cpufreq = exists(format!("/sys/devices/system/cpu/cpu{number}/cpufreq"));
|
let has_cpufreq = fs::exists(format!("/sys/devices/system/cpu/cpu{number}/cpufreq"));
|
||||||
|
|
||||||
self.has_cpufreq = has_cpufreq;
|
self.has_cpufreq = has_cpufreq;
|
||||||
|
|
||||||
|
@ -110,7 +86,7 @@ impl Cpu {
|
||||||
pub fn get_available_governors(&self) -> Vec<String> {
|
pub fn get_available_governors(&self) -> Vec<String> {
|
||||||
let Self { number, .. } = self;
|
let Self { number, .. } = self;
|
||||||
|
|
||||||
let Ok(content) = fs::read_to_string(format!(
|
let Ok(Some(content)) = fs::read(format!(
|
||||||
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_available_governors"
|
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_available_governors"
|
||||||
)) else {
|
)) else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
|
@ -137,7 +113,7 @@ impl Cpu {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
write(
|
fs::write(
|
||||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_governor"),
|
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_governor"),
|
||||||
governor,
|
governor,
|
||||||
)
|
)
|
||||||
|
@ -151,7 +127,7 @@ impl Cpu {
|
||||||
pub fn get_available_epps(&self) -> Vec<String> {
|
pub fn get_available_epps(&self) -> Vec<String> {
|
||||||
let Self { number, .. } = self;
|
let Self { number, .. } = self;
|
||||||
|
|
||||||
let Ok(content) = fs::read_to_string(format!(
|
let Ok(Some(content)) = fs::read(format!(
|
||||||
"/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_available_preferences"
|
"/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_available_preferences"
|
||||||
)) else {
|
)) else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
|
@ -175,7 +151,7 @@ impl Cpu {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
write(
|
fs::write(
|
||||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_preference"),
|
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_preference"),
|
||||||
epp,
|
epp,
|
||||||
)
|
)
|
||||||
|
@ -226,7 +202,7 @@ impl Cpu {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
write(
|
fs::write(
|
||||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_bias"),
|
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_bias"),
|
||||||
epb,
|
epb,
|
||||||
)
|
)
|
||||||
|
@ -244,7 +220,7 @@ impl Cpu {
|
||||||
let frequency_khz = frequency_mhz * 1000;
|
let frequency_khz = frequency_mhz * 1000;
|
||||||
let frequency_khz = frequency_khz.to_string();
|
let frequency_khz = frequency_khz.to_string();
|
||||||
|
|
||||||
write(
|
fs::write(
|
||||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"),
|
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"),
|
||||||
&frequency_khz,
|
&frequency_khz,
|
||||||
)
|
)
|
||||||
|
@ -256,7 +232,7 @@ impl Cpu {
|
||||||
fn validate_frequency_minimum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
fn validate_frequency_minimum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
||||||
let Self { number, .. } = self;
|
let Self { number, .. } = self;
|
||||||
|
|
||||||
let Ok(minimum_frequency_khz) = read_u64(format!(
|
let Ok(minimum_frequency_khz) = fs::read_u64(format!(
|
||||||
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
|
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
|
||||||
)) else {
|
)) else {
|
||||||
// Just let it pass if we can't find anything.
|
// Just let it pass if we can't find anything.
|
||||||
|
@ -282,7 +258,7 @@ impl Cpu {
|
||||||
let frequency_khz = frequency_mhz * 1000;
|
let frequency_khz = frequency_mhz * 1000;
|
||||||
let frequency_khz = frequency_khz.to_string();
|
let frequency_khz = frequency_khz.to_string();
|
||||||
|
|
||||||
write(
|
fs::write(
|
||||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_max_freq"),
|
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_max_freq"),
|
||||||
&frequency_khz,
|
&frequency_khz,
|
||||||
)
|
)
|
||||||
|
@ -294,7 +270,7 @@ impl Cpu {
|
||||||
fn validate_frequency_maximum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
fn validate_frequency_maximum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
||||||
let Self { number, .. } = self;
|
let Self { number, .. } = self;
|
||||||
|
|
||||||
let Ok(maximum_frequency_khz) = read_u64(format!(
|
let Ok(maximum_frequency_khz) = fs::read_u64(format!(
|
||||||
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
|
"/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
|
||||||
)) else {
|
)) else {
|
||||||
// Just let it pass if we can't find anything.
|
// Just let it pass if we can't find anything.
|
||||||
|
@ -331,16 +307,16 @@ impl Cpu {
|
||||||
let generic_boost_path = "/sys/devices/system/cpu/cpufreq/boost";
|
let generic_boost_path = "/sys/devices/system/cpu/cpufreq/boost";
|
||||||
|
|
||||||
// Try each boost control path in order of specificity
|
// Try each boost control path in order of specificity
|
||||||
if write(intel_boost_path_negated, value_boost_negated).is_ok() {
|
if fs::write(intel_boost_path_negated, value_boost_negated).is_ok() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if write(amd_boost_path, value_boost).is_ok() {
|
if fs::write(amd_boost_path, value_boost).is_ok() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if write(msr_boost_path, value_boost).is_ok() {
|
if fs::write(msr_boost_path, value_boost).is_ok() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if write(generic_boost_path, value_boost).is_ok() {
|
if fs::write(generic_boost_path, value_boost).is_ok() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,7 +324,7 @@ impl Cpu {
|
||||||
if Self::all()?.iter().any(|cpu| {
|
if Self::all()?.iter().any(|cpu| {
|
||||||
let Cpu { number, .. } = cpu;
|
let Cpu { number, .. } = cpu;
|
||||||
|
|
||||||
write(
|
fs::write(
|
||||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/boost"),
|
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/boost"),
|
||||||
value_boost,
|
value_boost,
|
||||||
)
|
)
|
||||||
|
|
55
src/fs.rs
Normal file
55
src/fs.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use std::{fs, io, path::Path};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
|
pub fn exists(path: impl AsRef<Path>) -> bool {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
path.exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_dir(path: impl AsRef<Path>) -> anyhow::Result<fs::ReadDir> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
fs::read_dir(path)
|
||||||
|
.with_context(|| format!("failed to read directory '{path}'", path = path.display()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(path: impl AsRef<Path>) -> anyhow::Result<Option<String>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
match fs::read_to_string(path) {
|
||||||
|
Ok(string) => Ok(Some(string)),
|
||||||
|
|
||||||
|
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None),
|
||||||
|
|
||||||
|
Err(error) => {
|
||||||
|
Err(error).with_context(|| format!("failed to read '{path}", path = path.display()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_u64(path: impl AsRef<Path>) -> anyhow::Result<u64> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let content = fs::read_to_string(path)
|
||||||
|
.with_context(|| format!("failed to read '{path}'", path = path.display()))?;
|
||||||
|
|
||||||
|
Ok(content.trim().parse().with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to parse contents of '{path}' as a unsigned number",
|
||||||
|
path = path.display(),
|
||||||
|
)
|
||||||
|
})?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(path: impl AsRef<Path>, value: &str) -> anyhow::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
fs::write(path, value).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to write '{value}' to '{path}'",
|
||||||
|
path = path.display(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
|
mod cpu;
|
||||||
|
mod power_supply;
|
||||||
|
mod system;
|
||||||
|
|
||||||
|
mod fs;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
// mod core;
|
// mod core;
|
||||||
mod cpu;
|
|
||||||
mod daemon;
|
mod daemon;
|
||||||
// mod engine;
|
// mod engine;
|
||||||
// mod monitor;
|
// mod monitor;
|
||||||
mod power_supply;
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser as _;
|
use clap::Parser as _;
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
use anyhow::{Context, bail};
|
use anyhow::{Context, anyhow, bail};
|
||||||
use yansi::Paint as _;
|
use yansi::Paint as _;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt, fs,
|
fmt,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Migrate to central utils file. Same exists in cpu.rs.
|
use crate::fs;
|
||||||
fn write(path: impl AsRef<Path>, value: &str) -> anyhow::Result<()> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
fs::write(path, value).with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to write '{value}' to '{path}'",
|
|
||||||
path = path.display(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a pattern of path suffixes used to control charge thresholds
|
/// Represents a pattern of path suffixes used to control charge thresholds
|
||||||
/// for different device vendors.
|
/// for different device vendors.
|
||||||
|
@ -136,10 +126,10 @@ impl PowerSupply {
|
||||||
fn get_type(&self) -> anyhow::Result<String> {
|
fn get_type(&self) -> anyhow::Result<String> {
|
||||||
let type_path = self.path.join("type");
|
let type_path = self.path.join("type");
|
||||||
|
|
||||||
let type_ = fs::read_to_string(&type_path)
|
let type_ = fs::read(&type_path)
|
||||||
.with_context(|| format!("failed to read '{path}'", path = type_path.display()))?;
|
.with_context(|| format!("failed to read '{path}'", path = type_path.display()))?;
|
||||||
|
|
||||||
Ok(type_)
|
type_.ok_or_else(|| anyhow!("'{path}' doesn't exist", path = type_path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rescan(&mut self) -> anyhow::Result<()> {
|
pub fn rescan(&mut self) -> anyhow::Result<()> {
|
||||||
|
@ -180,9 +170,9 @@ impl PowerSupply {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_charge_threshold_start(&self, charge_threshold_start: u8) -> anyhow::Result<()> {
|
pub fn set_charge_threshold_start(&self, charge_threshold_start: u8) -> anyhow::Result<()> {
|
||||||
write(
|
fs::write(
|
||||||
&self.charge_threshold_path_start().ok_or_else(|| {
|
&self.charge_threshold_path_start().ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow!(
|
||||||
"power supply '{name}' does not support changing charge threshold levels",
|
"power supply '{name}' does not support changing charge threshold levels",
|
||||||
name = self.name,
|
name = self.name,
|
||||||
)
|
)
|
||||||
|
@ -197,9 +187,9 @@ impl PowerSupply {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_charge_threshold_end(&self, charge_threshold_end: u8) -> anyhow::Result<()> {
|
pub fn set_charge_threshold_end(&self, charge_threshold_end: u8) -> anyhow::Result<()> {
|
||||||
write(
|
fs::write(
|
||||||
&self.charge_threshold_path_end().ok_or_else(|| {
|
&self.charge_threshold_path_end().ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow!(
|
||||||
"power supply '{name}' does not support changing charge threshold levels",
|
"power supply '{name}' does not support changing charge threshold levels",
|
||||||
name = self.name,
|
name = self.name,
|
||||||
)
|
)
|
||||||
|
@ -216,7 +206,7 @@ impl PowerSupply {
|
||||||
pub fn get_available_platform_profiles() -> Vec<String> {
|
pub fn get_available_platform_profiles() -> Vec<String> {
|
||||||
let path = "/sys/firmware/acpi/platform_profile_choices";
|
let path = "/sys/firmware/acpi/platform_profile_choices";
|
||||||
|
|
||||||
let Ok(content) = fs::read_to_string(path) else {
|
let Ok(Some(content)) = fs::read(path) else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -245,7 +235,7 @@ impl PowerSupply {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
write("/sys/firmware/acpi/platform_profile", profile)
|
fs::write("/sys/firmware/acpi/platform_profile", profile)
|
||||||
.context("this probably means that your system does not support changing ACPI profiles")
|
.context("this probably means that your system does not support changing ACPI profiles")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/system.rs
Normal file
14
src/system.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
pub struct System {
|
||||||
|
pub is_desktop: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
pub fn new() -> anyhow::Result<Self> {
|
||||||
|
let mut system = Self { is_desktop: false };
|
||||||
|
system.rescan()?;
|
||||||
|
|
||||||
|
Ok(system)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rescan(&mut self) -> anyhow::Result<()> {}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue