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 yansi::Paint as _;
|
||||
|
||||
use std::{fmt, fs, path::Path, string::ToString};
|
||||
use std::{fmt, string::ToString};
|
||||
|
||||
fn exists(path: impl AsRef<Path>) -> bool {
|
||||
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(),
|
||||
)
|
||||
})
|
||||
}
|
||||
use crate::fs;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Cpu {
|
||||
|
@ -96,11 +72,11 @@ impl Cpu {
|
|||
pub fn rescan(&mut self) -> anyhow::Result<()> {
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -110,7 +86,7 @@ impl Cpu {
|
|||
pub fn get_available_governors(&self) -> Vec<String> {
|
||||
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"
|
||||
)) else {
|
||||
return Vec::new();
|
||||
|
@ -137,7 +113,7 @@ impl Cpu {
|
|||
);
|
||||
}
|
||||
|
||||
write(
|
||||
fs::write(
|
||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_governor"),
|
||||
governor,
|
||||
)
|
||||
|
@ -151,7 +127,7 @@ impl Cpu {
|
|||
pub fn get_available_epps(&self) -> Vec<String> {
|
||||
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"
|
||||
)) else {
|
||||
return Vec::new();
|
||||
|
@ -175,7 +151,7 @@ impl Cpu {
|
|||
);
|
||||
}
|
||||
|
||||
write(
|
||||
fs::write(
|
||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_preference"),
|
||||
epp,
|
||||
)
|
||||
|
@ -226,7 +202,7 @@ impl Cpu {
|
|||
);
|
||||
}
|
||||
|
||||
write(
|
||||
fs::write(
|
||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_bias"),
|
||||
epb,
|
||||
)
|
||||
|
@ -244,7 +220,7 @@ impl Cpu {
|
|||
let frequency_khz = frequency_mhz * 1000;
|
||||
let frequency_khz = frequency_khz.to_string();
|
||||
|
||||
write(
|
||||
fs::write(
|
||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"),
|
||||
&frequency_khz,
|
||||
)
|
||||
|
@ -256,7 +232,7 @@ impl Cpu {
|
|||
fn validate_frequency_minimum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
||||
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"
|
||||
)) else {
|
||||
// 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_khz.to_string();
|
||||
|
||||
write(
|
||||
fs::write(
|
||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_max_freq"),
|
||||
&frequency_khz,
|
||||
)
|
||||
|
@ -294,7 +270,7 @@ impl Cpu {
|
|||
fn validate_frequency_maximum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
|
||||
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"
|
||||
)) else {
|
||||
// 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";
|
||||
|
||||
// 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(());
|
||||
}
|
||||
if write(amd_boost_path, value_boost).is_ok() {
|
||||
if fs::write(amd_boost_path, value_boost).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
if write(msr_boost_path, value_boost).is_ok() {
|
||||
if fs::write(msr_boost_path, value_boost).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
if write(generic_boost_path, value_boost).is_ok() {
|
||||
if fs::write(generic_boost_path, value_boost).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -348,7 +324,7 @@ impl Cpu {
|
|||
if Self::all()?.iter().any(|cpu| {
|
||||
let Cpu { number, .. } = cpu;
|
||||
|
||||
write(
|
||||
fs::write(
|
||||
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/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 core;
|
||||
mod cpu;
|
||||
mod daemon;
|
||||
// mod engine;
|
||||
// mod monitor;
|
||||
mod power_supply;
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Parser as _;
|
||||
|
|
|
@ -1,22 +1,12 @@
|
|||
use anyhow::{Context, bail};
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use yansi::Paint as _;
|
||||
|
||||
use std::{
|
||||
fmt, fs,
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
// TODO: Migrate to central utils file. Same exists in cpu.rs.
|
||||
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(),
|
||||
)
|
||||
})
|
||||
}
|
||||
use crate::fs;
|
||||
|
||||
/// Represents a pattern of path suffixes used to control charge thresholds
|
||||
/// for different device vendors.
|
||||
|
@ -136,10 +126,10 @@ impl PowerSupply {
|
|||
fn get_type(&self) -> anyhow::Result<String> {
|
||||
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()))?;
|
||||
|
||||
Ok(type_)
|
||||
type_.ok_or_else(|| anyhow!("'{path}' doesn't exist", path = type_path.display()))
|
||||
}
|
||||
|
||||
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<()> {
|
||||
write(
|
||||
fs::write(
|
||||
&self.charge_threshold_path_start().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
anyhow!(
|
||||
"power supply '{name}' does not support changing charge threshold levels",
|
||||
name = self.name,
|
||||
)
|
||||
|
@ -197,9 +187,9 @@ impl PowerSupply {
|
|||
}
|
||||
|
||||
pub fn set_charge_threshold_end(&self, charge_threshold_end: u8) -> anyhow::Result<()> {
|
||||
write(
|
||||
fs::write(
|
||||
&self.charge_threshold_path_end().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
anyhow!(
|
||||
"power supply '{name}' does not support changing charge threshold levels",
|
||||
name = self.name,
|
||||
)
|
||||
|
@ -216,7 +206,7 @@ impl PowerSupply {
|
|||
pub fn get_available_platform_profiles() -> Vec<String> {
|
||||
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();
|
||||
};
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
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