Define complete Rust back-end API and functionality
This commit is contained in:
parent
20ce2f1d5f
commit
c225554f78
30 changed files with 1523 additions and 89 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -42,3 +42,4 @@ yalc.lock
|
|||
|
||||
# rust
|
||||
/powertools-rs/target
|
||||
/bin
|
||||
|
|
113
powertools-rs/Cargo.lock
generated
113
powertools-rs/Cargo.lock
generated
|
@ -2,6 +2,42 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm-siv"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589c637f0e68c877bbd59a4599bbe849cac8e5f3e4b5a3ebae8f528cd218dcdc"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"polyval",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -66,6 +102,15 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.2"
|
||||
|
@ -85,6 +130,15 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
|
@ -251,6 +305,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
|
@ -437,6 +497,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "obfstr"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b2b2cbbfd8defa51ff24450a61d73b3ff3e158484ddd274a883e886e6fbaa78"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
|
@ -487,9 +553,21 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powertools-rs"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
|
@ -691,6 +769,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
|
@ -945,6 +1029,16 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.2"
|
||||
|
@ -959,11 +1053,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "usdpl-back"
|
||||
version = "0.5.3"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d237439986405621b9b6da350aefcfca9e2b808c10695f55f8b80ccc324b2c0"
|
||||
checksum = "cbbc0781e83ba990f8239142e33173a2d2548701775f3db66702d1af4fd0319a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hex",
|
||||
"obfstr",
|
||||
"tokio",
|
||||
"usdpl-core",
|
||||
"warp",
|
||||
|
@ -971,10 +1067,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "usdpl-core"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd726b9f0121d4449082e3ce73586dea0a0448494031833b7b173e4476f0ea5"
|
||||
checksum = "862153581fac266458521f49e5906a71c1eee1665cb4c7d71e9586bd34b45394"
|
||||
dependencies = [
|
||||
"aes-gcm-siv",
|
||||
"base64",
|
||||
]
|
||||
|
||||
|
@ -1109,3 +1206,9 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
[package]
|
||||
name = "powertools-rs"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
usdpl-back = { version = "0.5.3", features = ["blocking"]}
|
||||
usdpl-back = { version = "0.6.0", features = ["blocking"]}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
# logging
|
||||
log = "0.4"
|
||||
simplelog = "0.12"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
decky = ["usdpl-back/decky"]
|
||||
crankshaft = ["usdpl-back/crankshaft"]
|
||||
encrypt = ["usdpl-back/encrypt"]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
cargo build --release
|
||||
mkdir ../bin
|
||||
# TODO replace "backend" \/ with binary name
|
||||
cp ./target/release/backend ../bin/backend
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
mkdir ../bin &> /dev/null
|
||||
cp ./target/release/powertools-rs ../bin/backend
|
||||
|
|
64
powertools-rs/src/api/battery.rs
Normal file
64
powertools-rs/src/api/battery.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use std::sync::{mpsc::Sender, Arc, Mutex};
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::settings::{Battery, OnSet};
|
||||
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||
|
||||
/// Current current (ha!) web method
|
||||
pub fn current_now(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||
super::utility::map_result(crate::settings::Battery::current_now())
|
||||
}
|
||||
|
||||
/// Generate set battery charge rate web method
|
||||
pub fn set_charge_rate(
|
||||
settings: Arc<Mutex<Battery>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(new_val)) = params_in.get(0) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "battery");
|
||||
settings_lock.charge_rate = Some(*new_val as _);
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
super::utility::map_empty_result(
|
||||
settings_lock.on_set(),
|
||||
settings_lock.charge_rate.unwrap(),
|
||||
)
|
||||
} else {
|
||||
vec!["set_charge_rate missing parameter".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate get battery charge rate web method
|
||||
pub fn get_charge_rate(
|
||||
settings: Arc<Mutex<Battery>>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
let settings_lock = unwrap_lock(settings.lock(), "battery");
|
||||
vec![settings_lock
|
||||
.charge_rate
|
||||
.map(|x| x.into())
|
||||
.unwrap_or(Primitive::Empty)]
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate unset battery charge rate web method
|
||||
pub fn unset_charge_rate(
|
||||
settings: Arc<Mutex<Battery>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |_: super::ApiParameterType| {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "battery");
|
||||
settings_lock.charge_rate = None;
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
}
|
194
powertools-rs/src/api/cpu.rs
Normal file
194
powertools-rs/src/api/cpu.rs
Normal file
|
@ -0,0 +1,194 @@
|
|||
use std::sync::{mpsc::Sender, Arc, Mutex};
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::settings::{Cpu, OnSet, SettingError, SettingVariant, MinMax};
|
||||
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||
|
||||
/// Available CPUs web method
|
||||
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||
super::utility::map_result(
|
||||
Cpu::cpu_count()
|
||||
.map(|x| x as u64)
|
||||
.ok_or_else(
|
||||
|| SettingError {
|
||||
msg: "Failed to parse CPU count".to_owned(),
|
||||
setting: SettingVariant::Cpu,
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate set CPU online web method
|
||||
pub fn set_cpu_online(
|
||||
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||
if let Some(Primitive::Bool(online)) = params_in.get(1) {
|
||||
cpu.online = *online;
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
super::utility::map_empty_result(
|
||||
cpu.on_set(),
|
||||
cpu.online,
|
||||
)
|
||||
} else {
|
||||
vec!["set_cpu_online missing parameter 1".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_cpu_online cpu index out of bounds".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_cpu_online missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cpus_online(
|
||||
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
let settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
let mut output = Vec::with_capacity(settings_lock.len());
|
||||
for cpu in settings_lock.as_slice() {
|
||||
output.push(cpu.online.into());
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_clock_limits(
|
||||
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||
if let Some(Primitive::F64(min)) = params_in.get(1) {
|
||||
if let Some(Primitive::F64(max)) = params_in.get(2) {
|
||||
cpu.clock_limits = Some(MinMax {
|
||||
min: *min as _,
|
||||
max: *max as _,
|
||||
});
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
match cpu.on_set() {
|
||||
Ok(_) => vec![
|
||||
cpu.clock_limits.as_ref().unwrap().min.into(),
|
||||
cpu.clock_limits.as_ref().unwrap().max.into(),
|
||||
],
|
||||
Err(e) => vec![e.msg.into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_clock_limits missing parameter 2".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_clock_limits missing parameter 1".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_clock_limits cpu index out of bounds".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_clock_limits missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_clock_limits(
|
||||
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||
if let Some(min_max) = &cpu.clock_limits {
|
||||
vec![min_max.max.into(), min_max.min.into()]
|
||||
} else {
|
||||
vec![Primitive::Empty, Primitive::Empty]
|
||||
}
|
||||
} else {
|
||||
vec!["get_clock_limits cpu index out of bounds".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["get_clock_limits missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unset_clock_limits(
|
||||
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||
cpu.clock_limits = None;
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
vec![]
|
||||
} else {
|
||||
vec!["get_clock_limits cpu index out of bounds".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["get_clock_limits missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cpu_governor(
|
||||
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||
if let Some(Primitive::String(governor)) = params_in.get(1) {
|
||||
cpu.governor = governor.to_owned();
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
super::utility::map_empty_result(
|
||||
cpu.on_set(),
|
||||
&cpu.governor as &str,
|
||||
)
|
||||
} else {
|
||||
vec!["set_cpu_governor missing parameter 1".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_cpu_governor cpu index out of bounds".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_cpu_governor missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cpu_governors(
|
||||
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
let settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
let mut output = Vec::with_capacity(settings_lock.len());
|
||||
for cpu in settings_lock.as_slice() {
|
||||
output.push(cpu.governor.clone().into());
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
161
powertools-rs/src/api/gpu.rs
Normal file
161
powertools-rs/src/api/gpu.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use std::sync::{mpsc::Sender, Arc, Mutex};
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::settings::{Gpu, OnSet, MinMax};
|
||||
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||
|
||||
pub fn set_ppt(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(fast_ppt)) = params_in.get(0) {
|
||||
if let Some(Primitive::F64(slow_ppt)) = params_in.get(1) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||
settings_lock.fast_ppt = Some(*fast_ppt as u64);
|
||||
settings_lock.slow_ppt = Some(*slow_ppt as u64);
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
match settings_lock.on_set() {
|
||||
Ok(_) => vec![
|
||||
settings_lock.fast_ppt.unwrap().into(),
|
||||
settings_lock.slow_ppt.unwrap().into()
|
||||
],
|
||||
Err(e) => vec![e.msg.into()],
|
||||
}
|
||||
} else {
|
||||
vec!["set_ppt missing parameter 1".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_ppt missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ppt(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
let settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||
let fast_ppt = settings_lock.fast_ppt.map(|x| x.into()).unwrap_or(Primitive::Empty);
|
||||
let slow_ppt = settings_lock.slow_ppt.map(|x| x.into()).unwrap_or(Primitive::Empty);
|
||||
vec![fast_ppt, slow_ppt]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unset_ppt(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |_: super::ApiParameterType| {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||
settings_lock.fast_ppt = None;
|
||||
settings_lock.slow_ppt = None;
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
super::utility::map_empty_result(
|
||||
settings_lock.on_set(),
|
||||
Primitive::Empty,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_clock_limits(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::F64(min)) = params_in.get(0) {
|
||||
if let Some(Primitive::F64(max)) = params_in.get(1) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||
settings_lock.clock_limits = Some(MinMax {
|
||||
min: *min as _,
|
||||
max: *max as _,
|
||||
});
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
match settings_lock.on_set() {
|
||||
Ok(_) => vec![
|
||||
settings_lock.clock_limits.as_ref().unwrap().min.into(),
|
||||
settings_lock.clock_limits.as_ref().unwrap().max.into(),
|
||||
],
|
||||
Err(e) => vec![e.msg.into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_clock_limits missing parameter 1".into()]
|
||||
}
|
||||
} else {
|
||||
vec!["set_clock_limits missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_clock_limits(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
let settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||
if let Some(min_max) = &settings_lock.clock_limits {
|
||||
vec![min_max.max.into(), min_max.min.into()]
|
||||
} else {
|
||||
vec![Primitive::Empty, Primitive::Empty]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unset_clock_limits(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |_: super::ApiParameterType| {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||
settings_lock.clock_limits = None;
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_slow_memory(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
saver: Sender<()>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::Bool(memory_is_slow)) = params_in.get(0) {
|
||||
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||
settings_lock.slow_memory = *memory_is_slow;
|
||||
unwrap_maybe_fatal(
|
||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
super::utility::map_empty_result(
|
||||
settings_lock.on_set(),
|
||||
settings_lock.slow_memory,
|
||||
)
|
||||
} else {
|
||||
vec!["set_slow_memory missing parameter 0".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_slow_memory(
|
||||
settings: Arc<Mutex<Gpu>>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
let settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||
vec![settings_lock.slow_memory.into()]
|
||||
}
|
||||
}
|
6
powertools-rs/src/api/mod.rs
Normal file
6
powertools-rs/src/api/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
pub mod battery;
|
||||
pub mod cpu;
|
||||
pub mod gpu;
|
||||
mod utility;
|
||||
|
||||
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;
|
21
powertools-rs/src/api/utility.rs
Normal file
21
powertools-rs/src/api/utility.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use std::convert::Into;
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::settings::SettingError;
|
||||
|
||||
pub fn map_result<T: Into<Primitive>>(result: Result<T, SettingError>) -> super::ApiParameterType {
|
||||
match result {
|
||||
Ok(val) => vec![val.into()],
|
||||
Err(e) => vec![e.msg.into()],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_empty_result<T: Into<Primitive>>(
|
||||
result: Result<(), SettingError>,
|
||||
success: T,
|
||||
) -> super::ApiParameterType {
|
||||
match result {
|
||||
Ok(_) => vec![success.into()],
|
||||
Err(e) => vec![e.msg.into()],
|
||||
}
|
||||
}
|
|
@ -1,35 +1,132 @@
|
|||
mod api;
|
||||
mod persist;
|
||||
mod settings;
|
||||
mod state;
|
||||
|
||||
use simplelog::{WriteLogger, LevelFilter};
|
||||
mod resume_worker;
|
||||
mod save_worker;
|
||||
mod utility;
|
||||
|
||||
use simplelog::{LevelFilter, WriteLogger};
|
||||
|
||||
use usdpl_back::Instance;
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
use usdpl_back::Instance;
|
||||
|
||||
const PORT: u16 = 44443;
|
||||
|
||||
const PACKAGE_NAME: &'static str = env!("CARGO_PKG_NAME");
|
||||
const PACKAGE_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
const DEFAULT_SETTINGS_FILE: &str = "default_settings.json";
|
||||
|
||||
fn main() -> Result<(), ()> {
|
||||
let log_filepath = format!("/tmp/{}.log", PACKAGE_NAME);
|
||||
WriteLogger::init(
|
||||
#[cfg(debug_assertions)]{LevelFilter::Debug},
|
||||
#[cfg(not(debug_assertions))]{LevelFilter::Info},
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
LevelFilter::Debug
|
||||
},
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
LevelFilter::Info
|
||||
},
|
||||
Default::default(),
|
||||
std::fs::File::create(&log_filepath).unwrap()
|
||||
).unwrap();
|
||||
std::fs::File::create(&log_filepath).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
log::info!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||
println!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||
|
||||
let default_settings: settings::Settings = persist::SettingsJson::open(
|
||||
settings_dir().join("default_settings.json")
|
||||
).unwrap_or_default().into();
|
||||
|
||||
|
||||
let default_settings = persist::SettingsJson::open(settings_dir().join(DEFAULT_SETTINGS_FILE))
|
||||
.map(|settings| settings::Settings::from_json(settings, DEFAULT_SETTINGS_FILE.into()))
|
||||
.unwrap_or_else(|_| settings::Settings::system_default(DEFAULT_SETTINGS_FILE.into()));
|
||||
|
||||
log::debug!("Settings: {:?}", default_settings);
|
||||
|
||||
|
||||
let (_save_handle, save_sender) = save_worker::spawn(default_settings.clone());
|
||||
let _resume_handle = resume_worker::spawn(default_settings.clone());
|
||||
|
||||
Instance::new(PORT)
|
||||
.register("hello", |_: Vec<Primitive>| vec![format!("Hello {}", PACKAGE_NAME).into()])
|
||||
.register("hello", |_: Vec<Primitive>| {
|
||||
vec![format!("Hello {}", PACKAGE_NAME).into()]
|
||||
})
|
||||
// battery API functions
|
||||
.register("BATTERY_current_now", api::battery::current_now)
|
||||
.register(
|
||||
"BATTERY_set_charge_rate",
|
||||
api::battery::set_charge_rate(default_settings.battery.clone(), save_sender.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_get_charge_rate",
|
||||
api::battery::get_charge_rate(default_settings.battery.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_unset_charge_rate",
|
||||
api::battery::unset_charge_rate(default_settings.battery.clone(), save_sender.clone()),
|
||||
)
|
||||
// cpu API functions
|
||||
.register("CPU_count", api::cpu::max_cpus)
|
||||
.register(
|
||||
"CPU_set_online",
|
||||
api::cpu::set_cpu_online(default_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_get_online",
|
||||
api::cpu::get_cpus_online(default_settings.cpus.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_set_clock_limits",
|
||||
api::cpu::set_clock_limits(default_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_get_clock_limits",
|
||||
api::cpu::get_clock_limits(default_settings.cpus.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_unset_clock_limits",
|
||||
api::cpu::unset_clock_limits(default_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_set_governor",
|
||||
api::cpu::set_cpu_governor(default_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_get_governors",
|
||||
api::cpu::get_cpu_governors(default_settings.cpus.clone())
|
||||
)
|
||||
// gpu API functions
|
||||
.register(
|
||||
"GPU_set_ppt",
|
||||
api::gpu::set_ppt(default_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_get_ppt",
|
||||
api::gpu::get_ppt(default_settings.gpu.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_unset_ppt",
|
||||
api::gpu::unset_ppt(default_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_set_clock_limits",
|
||||
api::gpu::set_clock_limits(default_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_get_clock_limits",
|
||||
api::gpu::get_clock_limits(default_settings.gpu.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_unset_clock_limits",
|
||||
api::gpu::unset_clock_limits(default_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_set_slow_memory",
|
||||
api::gpu::set_slow_memory(default_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_get_slow_memory",
|
||||
api::gpu::get_slow_memory(default_settings.gpu.clone())
|
||||
)
|
||||
.run_blocking()
|
||||
}
|
||||
|
||||
|
|
15
powertools-rs/src/persist/battery.rs
Normal file
15
powertools-rs/src/persist/battery.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use std::default::Default;
|
||||
//use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BatteryJson {
|
||||
pub charge_rate: Option<u64>,
|
||||
}
|
||||
|
||||
impl Default for BatteryJson {
|
||||
fn default() -> Self {
|
||||
Self { charge_rate: None }
|
||||
}
|
||||
}
|
|
@ -1,27 +1,25 @@
|
|||
use std::default::Default;
|
||||
//use std::fmt::Display;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const SCALING_FREQUENCIES: &[u64] = &[1700000, 2400000, 2800000];
|
||||
use super::MinMaxJson;
|
||||
|
||||
//const SCALING_FREQUENCIES: &[u64] = &[1700000, 2400000, 2800000];
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CpuJson {
|
||||
pub online: bool,
|
||||
pub max_boost: u64,
|
||||
pub min_boost: u64,
|
||||
pub clock_limits: Option<MinMaxJson<u64>>,
|
||||
pub governor: String,
|
||||
pub boost: bool,
|
||||
}
|
||||
|
||||
impl Default for CpuJson {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
online: true,
|
||||
max_boost: SCALING_FREQUENCIES[SCALING_FREQUENCIES.len() - 1],
|
||||
min_boost: SCALING_FREQUENCIES[0],
|
||||
clock_limits: None,
|
||||
governor: "schedutil".to_owned(),
|
||||
boost: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
powertools-rs/src/persist/error.rs
Normal file
14
powertools-rs/src/persist/error.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#[derive(Debug)]
|
||||
pub enum JsonError {
|
||||
Serde(serde_json::Error),
|
||||
Io(std::io::Error),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for JsonError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Serde(e) => (e as &dyn std::fmt::Display).fmt(f),
|
||||
Self::Io(e) => (e as &dyn std::fmt::Display).fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +1,29 @@
|
|||
use std::default::Default;
|
||||
use std::fmt::Display;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{CpuJson, GpuJson};
|
||||
use super::JsonError;
|
||||
use super::{BatteryJson, CpuJson, GpuJson};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SettingsJson {
|
||||
pub version: u64,
|
||||
pub name: String,
|
||||
pub persistent: bool,
|
||||
pub cpus: Vec<CpuJson>,
|
||||
pub gpu: GpuJson,
|
||||
pub battery: BatteryJson,
|
||||
}
|
||||
|
||||
impl Default for SettingsJson {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: 0,
|
||||
name: "default".to_owned(),
|
||||
persistent: false,
|
||||
cpus: Vec::with_capacity(8),
|
||||
gpu: GpuJson::default(),
|
||||
battery: BatteryJson::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,17 +44,8 @@ impl SettingsJson {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum JsonError {
|
||||
Serde(serde_json::Error),
|
||||
Io(std::io::Error),
|
||||
}
|
||||
|
||||
impl Display for JsonError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Serde(e) => (e as &dyn Display).fmt(f),
|
||||
Self::Io(e) => (e as &dyn Display).fmt(f),
|
||||
}
|
||||
}
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MinMaxJson<T> {
|
||||
pub max: T,
|
||||
pub min: T,
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use std::default::Default;
|
||||
//use std::fmt::Display;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use super::MinMaxJson;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GpuJson {
|
||||
pub fast_ppt: Option<u64>,
|
||||
pub slow_ppt: Option<u64>,
|
||||
pub clock_limits: Option<MinMaxJson<u64>>,
|
||||
pub slow_memory: bool,
|
||||
}
|
||||
|
||||
impl Default for GpuJson {
|
||||
|
@ -14,6 +17,8 @@ impl Default for GpuJson {
|
|||
Self {
|
||||
fast_ppt: None,
|
||||
slow_ppt: None,
|
||||
clock_limits: None,
|
||||
slow_memory: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
mod battery;
|
||||
mod cpu;
|
||||
mod error;
|
||||
mod general;
|
||||
mod gpu;
|
||||
|
||||
pub use battery::BatteryJson;
|
||||
pub use cpu::CpuJson;
|
||||
pub use general::SettingsJson;
|
||||
pub use general::{MinMaxJson, SettingsJson};
|
||||
pub use gpu::GpuJson;
|
||||
|
||||
pub use error::JsonError;
|
||||
|
|
27
powertools-rs/src/resume_worker.rs
Normal file
27
powertools-rs/src/resume_worker.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use std::thread::{self, JoinHandle};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::settings::{OnResume, Settings};
|
||||
use crate::utility::unwrap_maybe_fatal;
|
||||
|
||||
const ALLOWED_ERROR: f64 = 0.001;
|
||||
|
||||
pub fn spawn(settings: Settings) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
let duration = Duration::from_millis(5000);
|
||||
let mut start = Instant::now();
|
||||
loop {
|
||||
thread::sleep(duration);
|
||||
let old_start = start.elapsed();
|
||||
start = Instant::now();
|
||||
if old_start.as_secs_f64() > duration.as_secs_f64() * (1.0 + ALLOWED_ERROR) {
|
||||
// has just resumed from sleep
|
||||
unwrap_maybe_fatal(settings.on_resume(), "On resume failure");
|
||||
log::info!(
|
||||
"OnResume completed after sleeping for {}s",
|
||||
old_start.as_secs_f32()
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
19
powertools-rs/src/save_worker.rs
Normal file
19
powertools-rs/src/save_worker.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
use crate::persist::SettingsJson;
|
||||
use crate::settings::Settings;
|
||||
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||
|
||||
pub fn spawn(settings: Settings) -> (JoinHandle<()>, Sender<()>) {
|
||||
let (sender, receiver): (Sender<()>, Receiver<()>) = mpsc::channel();
|
||||
let worker = thread::spawn(move || {
|
||||
for _ in receiver.iter() {
|
||||
let save_path = unwrap_lock(settings.general.lock(), "general").path.clone();
|
||||
let save_json: SettingsJson = settings.clone().into();
|
||||
unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings");
|
||||
log::debug!("Saved settings to {}", save_path.display());
|
||||
}
|
||||
});
|
||||
(worker, sender)
|
||||
}
|
109
powertools-rs/src/settings/battery.rs
Normal file
109
powertools-rs/src/settings/battery.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::persist::BatteryJson;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Battery {
|
||||
pub charge_rate: Option<u64>,
|
||||
}
|
||||
|
||||
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
|
||||
const BATTERY_CURRENT_NOW_PATH: &str = "/sys/class/power_supply/BAT1/current_now"; // read-only
|
||||
|
||||
impl Battery {
|
||||
#[inline]
|
||||
pub fn from_json(other: BatteryJson, version: u64) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
},
|
||||
_ => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&self) -> Result<(), SettingError> {
|
||||
if let Some(charge_rate) = self.charge_rate {
|
||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, charge_rate).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(charge_rate) = &mut self.charge_rate {
|
||||
*charge_rate = (*charge_rate).clamp(min.charge_rate.unwrap(), max.charge_rate.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_now() -> Result<u64, SettingError> {
|
||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
|
||||
Err((Some(e), None)) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
BATTERY_CURRENT_NOW_PATH
|
||||
),
|
||||
// this value is in uA, while it's set in mA
|
||||
// so convert this to mA for consistency
|
||||
Ok(val) => Ok(val / 1000),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
Self { charge_rate: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BatteryJson> for Battery {
|
||||
#[inline]
|
||||
fn into(self) -> BatteryJson {
|
||||
BatteryJson {
|
||||
charge_rate: self.charge_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnSet for Battery {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
self.clamp_all();
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Battery {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Battery {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(2500),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(250),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +1,162 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use super::MinMax;
|
||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::persist::CpuJson;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cpu {
|
||||
pub online: bool,
|
||||
pub max_boost: u64,
|
||||
pub min_boost: u64,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub governor: String,
|
||||
pub boost: bool,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||
const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||
|
||||
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
||||
|
||||
impl Cpu {
|
||||
#[inline]
|
||||
pub fn from_json(other: CpuJson, version: u64) -> Self {
|
||||
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
online: other.online,
|
||||
max_boost: other.max_boost,
|
||||
min_boost: other.min_boost,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
governor: other.governor,
|
||||
boost: other.boost,
|
||||
index: i,
|
||||
},
|
||||
_ => Self {
|
||||
online: other.online,
|
||||
max_boost: other.max_boost,
|
||||
min_boost: other.min_boost,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
governor: other.governor,
|
||||
boost: other.boost,
|
||||
index: i,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&self) -> Result<(), SettingError> {
|
||||
// set cpu online/offline
|
||||
if self.index != 0 { // cpu0 cannot be disabled
|
||||
let online_path = cpu_online_path(self.index);
|
||||
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", &online_path, e),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// set clock limits
|
||||
if let Some(clock_limits) = &self.clock_limits {
|
||||
// set manual control
|
||||
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `manual` to `{}`: {}",
|
||||
CPU_FORCE_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
// max clock
|
||||
let payload_max = format!("p {} 1 {}", self.index / 2, clock_limits.max);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("p {} 0 {}", self.index / 2, clock_limits.min);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
// commit changes
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
} else {
|
||||
// disable manual clock limits
|
||||
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "auto").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `auto` to `{}`: {}",
|
||||
CPU_FORCE_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// set governor
|
||||
let governor_path = cpu_governor_path(self.index);
|
||||
usdpl_back::api::files::write_single(&governor_path, &self.governor).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&self.governor, &governor_path, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(clock_limits) = &mut self.clock_limits {
|
||||
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||
}
|
||||
}
|
||||
|
||||
fn from_sys(index: usize) -> Self {
|
||||
Self {
|
||||
online: usdpl_back::api::files::read_single(cpu_online_path(index)).unwrap_or(1u8) != 0,
|
||||
clock_limits: None,
|
||||
governor: usdpl_back::api::files::read_single(cpu_governor_path(index))
|
||||
.unwrap_or("schedutil".to_owned()),
|
||||
index: index,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpu_count() -> Option<usize> {
|
||||
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
|
||||
.unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */);
|
||||
if let Some(dash_index) = data.find('-') {
|
||||
let data = data.split_off(dash_index + 1);
|
||||
if let Ok(max_cpu) = data.parse::<usize>() {
|
||||
return Some(max_cpu);
|
||||
}
|
||||
}
|
||||
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
|
||||
None
|
||||
}
|
||||
|
||||
pub fn system_default() -> Vec<Self> {
|
||||
if let Some(max_cpu) = Self::cpu_count() {
|
||||
let mut cpus = Vec::with_capacity(max_cpu + 1);
|
||||
for i in 0..=max_cpu {
|
||||
cpus.push(Self::from_sys(i));
|
||||
}
|
||||
cpus
|
||||
} else {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +166,59 @@ impl Into<CpuJson> for Cpu {
|
|||
fn into(self) -> CpuJson {
|
||||
CpuJson {
|
||||
online: self.online,
|
||||
max_boost: self.max_boost,
|
||||
min_boost: self.min_boost,
|
||||
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||
governor: self.governor,
|
||||
boost: self.boost,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnSet for Cpu {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
self.clamp_all();
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Cpu {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Cpu {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
online: true,
|
||||
clock_limits: Some(MinMax {
|
||||
max: 3500,
|
||||
min: 3500,
|
||||
}),
|
||||
governor: "schedutil".to_owned(),
|
||||
index: usize::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
online: false,
|
||||
clock_limits: Some(MinMax { max: 500, min: 1400 }),
|
||||
governor: "schedutil".to_owned(),
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cpu_online_path(index: usize) -> String {
|
||||
format!("/sys/devices/system/cpu/cpu{}/online", index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cpu_governor_path(index: usize) -> String {
|
||||
format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_governor",
|
||||
index
|
||||
)
|
||||
}
|
||||
|
|
13
powertools-rs/src/settings/error.rs
Normal file
13
powertools-rs/src/settings/error.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use super::SettingVariant;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SettingError {
|
||||
pub msg: String,
|
||||
pub setting: SettingVariant,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SettingError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{} setting error: {}", self.setting, self.msg)
|
||||
}
|
||||
}
|
|
@ -1,43 +1,150 @@
|
|||
use std::convert::{Into, From};
|
||||
use std::convert::Into;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::persist::SettingsJson;
|
||||
use super::{Cpu, Gpu};
|
||||
use super::{Battery, Cpu, Gpu};
|
||||
use super::{OnResume, OnSet, SettingError};
|
||||
use crate::persist::{CpuJson, SettingsJson};
|
||||
use crate::utility::unwrap_lock;
|
||||
|
||||
const LATEST_VERSION: u64 = 0;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Settings {
|
||||
pub persistent: bool,
|
||||
pub cpus: Vec<Cpu>,
|
||||
pub gpu: Gpu,
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SettingVariant {
|
||||
Battery,
|
||||
Cpu,
|
||||
Gpu,
|
||||
General,
|
||||
}
|
||||
|
||||
impl From<SettingsJson> for Settings {
|
||||
#[inline]
|
||||
fn from(mut other: SettingsJson) -> Self {
|
||||
match other.version {
|
||||
0 => Self {
|
||||
persistent: other.persistent,
|
||||
cpus: other.cpus.drain(..).map(|cpu| Cpu::from_json(cpu, other.version)).collect(),
|
||||
gpu: Gpu::from_json(other.gpu, other.version),
|
||||
},
|
||||
_ => Self {
|
||||
persistent: other.persistent,
|
||||
cpus: other.cpus.drain(..).map(|cpu| Cpu::from_json(cpu, other.version)).collect(),
|
||||
gpu: Gpu::from_json(other.gpu, other.version),
|
||||
impl std::fmt::Display for SettingVariant {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Battery => write!(f, "Battery"),
|
||||
Self::Cpu => write!(f, "CPU"),
|
||||
Self::Gpu => write!(f, "GPU"),
|
||||
Self::General => write!(f, "General"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct General {
|
||||
pub persistent: bool,
|
||||
pub path: PathBuf,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Settings {
|
||||
pub general: Arc<Mutex<General>>,
|
||||
pub cpus: Arc<Mutex<Vec<Cpu>>>,
|
||||
pub gpu: Arc<Mutex<Gpu>>,
|
||||
pub battery: Arc<Mutex<Battery>>,
|
||||
}
|
||||
|
||||
impl OnSet for Settings {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
unwrap_lock(self.battery.lock(), "battery").on_set()?;
|
||||
{
|
||||
// cpu lock scope
|
||||
let mut cpu_lock = unwrap_lock(self.cpus.lock(), "cpu");
|
||||
for cpu in cpu_lock.iter_mut() {
|
||||
cpu.on_set()?;
|
||||
}
|
||||
}
|
||||
unwrap_lock(self.gpu.lock(), "gpu").on_set()?;
|
||||
{
|
||||
// general lock scope
|
||||
let gen_lock = unwrap_lock(self.general.lock(), "general");
|
||||
if !gen_lock.persistent && gen_lock.path.exists() {
|
||||
std::fs::remove_file(&gen_lock.path).map_err(|e| SettingError {
|
||||
msg: format!("Failed to delete `{}`: {}", gen_lock.path.display(), e),
|
||||
setting: SettingVariant::General,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
#[inline]
|
||||
pub fn from_json(other: SettingsJson, json_path: PathBuf) -> Self {
|
||||
match other.version {
|
||||
0 => Self {
|
||||
general: Arc::new(Mutex::new(General {
|
||||
persistent: other.persistent,
|
||||
path: json_path,
|
||||
name: other.name,
|
||||
})),
|
||||
cpus: Arc::new(Mutex::new(Self::convert_cpus(other.cpus, other.version))),
|
||||
gpu: Arc::new(Mutex::new(Gpu::from_json(other.gpu, other.version))),
|
||||
battery: Arc::new(Mutex::new(Battery::from_json(other.battery, other.version))),
|
||||
},
|
||||
_ => Self {
|
||||
general: Arc::new(Mutex::new(General {
|
||||
persistent: other.persistent,
|
||||
path: json_path,
|
||||
name: other.name,
|
||||
})),
|
||||
cpus: Arc::new(Mutex::new(Self::convert_cpus(other.cpus, other.version))),
|
||||
gpu: Arc::new(Mutex::new(Gpu::from_json(other.gpu, other.version))),
|
||||
battery: Arc::new(Mutex::new(Battery::from_json(other.battery, other.version))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_cpus(mut cpus: Vec<CpuJson>, version: u64) -> Vec<Cpu> {
|
||||
let mut result = Vec::with_capacity(cpus.len());
|
||||
for (i, cpu) in cpus.drain(..).enumerate() {
|
||||
result.push(Cpu::from_json(cpu, version, i));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn system_default(json_path: PathBuf) -> Self {
|
||||
Self {
|
||||
general: Arc::new(Mutex::new(General {
|
||||
persistent: false,
|
||||
path: json_path,
|
||||
name: "".to_owned(),
|
||||
})),
|
||||
cpus: Arc::new(Mutex::new(Cpu::system_default())),
|
||||
gpu: Arc::new(Mutex::new(Gpu::system_default())),
|
||||
battery: Arc::new(Mutex::new(Battery::system_default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Settings {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
unwrap_lock(self.battery.lock(), "battery").on_resume()?;
|
||||
{
|
||||
let mut cpu_lock = unwrap_lock(self.cpus.lock(), "cpu");
|
||||
for cpu in cpu_lock.iter_mut() {
|
||||
cpu.on_resume()?;
|
||||
}
|
||||
}
|
||||
unwrap_lock(self.gpu.lock(), "gpu").on_resume()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<SettingsJson> for Settings {
|
||||
#[inline]
|
||||
fn into(mut self) -> SettingsJson {
|
||||
fn into(self) -> SettingsJson {
|
||||
SettingsJson {
|
||||
version: LATEST_VERSION,
|
||||
persistent: self.persistent,
|
||||
cpus: self.cpus.drain(..).map(|cpu| cpu.into()).collect(),
|
||||
gpu: self.gpu.into()
|
||||
name: unwrap_lock(self.general.lock(), "general").name.clone(),
|
||||
persistent: unwrap_lock(self.general.lock(), "general").persistent,
|
||||
cpus: unwrap_lock(self.cpus.lock(), "cpu")
|
||||
.clone()
|
||||
.drain(..)
|
||||
.map(|cpu| cpu.into())
|
||||
.collect(),
|
||||
gpu: unwrap_lock(self.gpu.lock(), "gpu").clone().into(),
|
||||
battery: unwrap_lock(self.battery.lock(), "battery").clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use super::MinMax;
|
||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::persist::GpuJson;
|
||||
|
||||
const SLOW_PPT: u8 = 1;
|
||||
const FAST_PPT: u8 = 2;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Gpu {
|
||||
pub fast_ppt: Option<u64>,
|
||||
pub slow_ppt: Option<u64>,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub slow_memory: bool,
|
||||
}
|
||||
|
||||
// same as CPU
|
||||
const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||
const GPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||
const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk";
|
||||
|
||||
impl Gpu {
|
||||
#[inline]
|
||||
pub fn from_json(other: GpuJson, version: u64) -> Self {
|
||||
|
@ -15,11 +27,140 @@ impl Gpu {
|
|||
0 => Self {
|
||||
fast_ppt: other.fast_ppt,
|
||||
slow_ppt: other.slow_ppt,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
slow_memory: other.slow_memory,
|
||||
},
|
||||
_ => Self {
|
||||
fast_ppt: other.fast_ppt,
|
||||
slow_ppt: other.slow_ppt,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
slow_memory: other.slow_memory,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&self) -> Result<(), SettingError> {
|
||||
// set fast PPT
|
||||
if let Some(fast_ppt) = &self.fast_ppt {
|
||||
let fast_ppt_path = gpu_power_path(FAST_PPT);
|
||||
usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
fast_ppt, &fast_ppt_path, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// set slow PPT
|
||||
if let Some(slow_ppt) = &self.slow_ppt {
|
||||
let slow_ppt_path = gpu_power_path(SLOW_PPT);
|
||||
usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
slow_ppt, &slow_ppt_path, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// settings using force_performance_level
|
||||
if self.clock_limits.is_some() || self.slow_memory {
|
||||
// set manual control
|
||||
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `manual` to `{}`: {}",
|
||||
GPU_FORCE_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
// set clock limits
|
||||
if let Some(clock_limits) = &self.clock_limits {
|
||||
// max clock
|
||||
let payload_max = format!("s 1 {}", clock_limits.max);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("s 0 {}", clock_limits.min);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
// force downclock of GPU memory (to 400Mhz?)
|
||||
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
|
||||
.map_err(|e| SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
})?;
|
||||
// commit changes
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
} else {
|
||||
// disable manual clock limits
|
||||
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "auto").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `auto` to `{}`: {}",
|
||||
GPU_FORCE_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(()) // TODO
|
||||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(fast_ppt) = &mut self.fast_ppt {
|
||||
*fast_ppt = (*fast_ppt).clamp(
|
||||
*min.fast_ppt.as_ref().unwrap(),
|
||||
*max.fast_ppt.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
if let Some(slow_ppt) = &mut self.slow_ppt {
|
||||
*slow_ppt = (*slow_ppt).clamp(
|
||||
*min.slow_ppt.as_ref().unwrap(),
|
||||
*max.slow_ppt.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
if let Some(clock_limits) = &mut self.clock_limits {
|
||||
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
Self {
|
||||
fast_ppt: None,
|
||||
slow_ppt: None,
|
||||
clock_limits: None,
|
||||
slow_memory: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +171,51 @@ impl Into<GpuJson> for Gpu {
|
|||
GpuJson {
|
||||
fast_ppt: self.fast_ppt,
|
||||
slow_ppt: self.slow_ppt,
|
||||
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||
slow_memory: self.slow_memory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnSet for Gpu {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
self.clamp_all();
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Gpu {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Gpu {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
fast_ppt: Some(30000000),
|
||||
slow_ppt: Some(29000000),
|
||||
clock_limits: Some(MinMax {
|
||||
min: 1600,
|
||||
max: 1600,
|
||||
}),
|
||||
slow_memory: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
fast_ppt: Some(0),
|
||||
slow_ppt: Some(1000000),
|
||||
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
||||
slow_memory: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_power_path(power_number: u8) -> String {
|
||||
format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number)
|
||||
}
|
||||
|
|
29
powertools-rs/src/settings/min_max.rs
Normal file
29
powertools-rs/src/settings/min_max.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use crate::persist::MinMaxJson;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MinMax<T> {
|
||||
pub max: T,
|
||||
pub min: T,
|
||||
}
|
||||
|
||||
impl<T> MinMax<T> {
|
||||
#[inline]
|
||||
pub fn from_json<X: Into<T>>(other: MinMaxJson<X>, _version: u64) -> Self {
|
||||
Self {
|
||||
max: other.max.into(),
|
||||
min: other.min.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<X: Into<Y>, Y> Into<MinMaxJson<Y>> for MinMax<X> {
|
||||
#[inline]
|
||||
fn into(self) -> MinMaxJson<Y> {
|
||||
MinMaxJson {
|
||||
max: self.max.into(),
|
||||
min: self.min.into(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,25 @@
|
|||
mod battery;
|
||||
mod cpu;
|
||||
mod error;
|
||||
mod general;
|
||||
mod gpu;
|
||||
mod min_max;
|
||||
mod traits;
|
||||
|
||||
pub use battery::Battery;
|
||||
pub use cpu::Cpu;
|
||||
pub use general::Settings;
|
||||
pub use general::{SettingVariant, Settings};
|
||||
pub use gpu::Gpu;
|
||||
pub use min_max::MinMax;
|
||||
|
||||
pub use error::SettingError;
|
||||
pub use traits::{OnResume, OnSet, SettingsRange};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn system_defaults_test() {
|
||||
let settings = super::Settings::system_default("idc".into());
|
||||
println!("Loaded system settings: {:?}", settings);
|
||||
}
|
||||
}
|
||||
|
|
14
powertools-rs/src/settings/traits.rs
Normal file
14
powertools-rs/src/settings/traits.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use super::SettingError;
|
||||
|
||||
pub trait OnSet {
|
||||
fn on_set(&mut self) -> Result<(), SettingError>;
|
||||
}
|
||||
|
||||
pub trait OnResume {
|
||||
fn on_resume(&self) -> Result<(), SettingError>;
|
||||
}
|
||||
|
||||
pub trait SettingsRange {
|
||||
fn max() -> Self;
|
||||
fn min() -> Self;
|
||||
}
|
16
powertools-rs/src/state/error.rs
Normal file
16
powertools-rs/src/state/error.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::settings::SettingVariant;
|
||||
|
||||
pub struct StateError {
|
||||
pub msg: String,
|
||||
pub setting: Option<SettingVariant>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for StateError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
if let Some(setting) = self.setting {
|
||||
write!(f, "{} setting state error: {}", setting, self.msg)
|
||||
} else {
|
||||
write!(f, "State error: {}", self.msg)
|
||||
}
|
||||
}
|
||||
}
|
5
powertools-rs/src/state/mod.rs
Normal file
5
powertools-rs/src/state/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod error;
|
||||
mod traits;
|
||||
|
||||
pub use error::StateError;
|
||||
pub use traits::OnPoll;
|
5
powertools-rs/src/state/traits.rs
Normal file
5
powertools-rs/src/state/traits.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use super::StateError;
|
||||
|
||||
pub trait OnPoll {
|
||||
fn on_poll(&self) -> Result<(), StateError>;
|
||||
}
|
25
powertools-rs/src/utility.rs
Normal file
25
powertools-rs/src/utility.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use std::fmt::Display;
|
||||
use std::sync::{LockResult, MutexGuard};
|
||||
|
||||
pub fn unwrap_maybe_fatal<T: Sized, E: Display>(result: Result<T, E>, message: &str) -> T {
|
||||
match result {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("{}: {}", message, e);
|
||||
panic!("{}: {}", message, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_lock<'a, T: Sized>(
|
||||
result: LockResult<MutexGuard<'a, T>>,
|
||||
lock_name: &str,
|
||||
) -> MutexGuard<'a, T> {
|
||||
match result {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("Failed to acquire {} lock: {}", lock_name, e);
|
||||
panic!("Failed to acquire {} lock: {}", lock_name, e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue