Add experimental Decky CI back-end building, fix frequency controls crashing the kernel
This commit is contained in:
parent
18433bf5c5
commit
db3f4a85c0
45 changed files with 289 additions and 97 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -41,5 +41,6 @@ __pycache__/
|
||||||
yalc.lock
|
yalc.lock
|
||||||
|
|
||||||
# rust
|
# rust
|
||||||
/powertools-rs/target
|
/backend/target
|
||||||
/bin
|
/bin
|
||||||
|
/backend/out
|
||||||
|
|
6
powertools-rs/Cargo.lock → backend/Cargo.lock
generated
6
powertools-rs/Cargo.lock → backend/Cargo.lock
generated
|
@ -567,7 +567,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powertools-rs"
|
name = "powertools-rs"
|
||||||
version = "1.0.0"
|
version = "1.0.0-beta4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1054,6 +1054,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usdpl-back"
|
name = "usdpl-back"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbbc0781e83ba990f8239142e33173a2d2548701775f3db66702d1af4fd0319a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"hex",
|
"hex",
|
||||||
|
@ -1066,6 +1068,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usdpl-core"
|
name = "usdpl-core"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "862153581fac266458521f49e5906a71c1eee1665cb4c7d71e9586bd34b45394"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm-siv",
|
"aes-gcm-siv",
|
||||||
"base64",
|
"base64",
|
|
@ -1,12 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "powertools-rs"
|
name = "powertools-rs"
|
||||||
version = "1.0.0"
|
version = "1.0.0-beta4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
usdpl-back = { version = "0.6.0", features = ["blocking"], path = "../../usdpl-rs/usdpl-back"}
|
usdpl-back = { version = "0.6.0", features = ["blocking"]}
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
@ -19,3 +19,9 @@ default = []
|
||||||
decky = ["usdpl-back/decky"]
|
decky = ["usdpl-back/decky"]
|
||||||
crankshaft = ["usdpl-back/crankshaft"]
|
crankshaft = ["usdpl-back/crankshaft"]
|
||||||
encrypt = ["usdpl-back/encrypt"]
|
encrypt = ["usdpl-back/encrypt"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = false
|
||||||
|
strip = true
|
||||||
|
lto = true
|
||||||
|
codegen-units = 4
|
3
backend/Dockerfile
Normal file
3
backend/Dockerfile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
FROM ghcr.io/steamdeckhomebrew/holo-toolchain-rust:latest
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/backend/entrypoint.sh" ]
|
14
backend/Makefile
Normal file
14
backend/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# This is the default target, which will be built when
|
||||||
|
# you invoke make
|
||||||
|
.PHONY: all
|
||||||
|
all: hello
|
||||||
|
|
||||||
|
# This rule tells make how to build hello from hello.cpp
|
||||||
|
hello:
|
||||||
|
mkdir -p ./out
|
||||||
|
gcc -o ./out/hello ./src/main.c
|
||||||
|
|
||||||
|
# This rule tells make to delete hello and hello.o
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f hello
|
15
backend/build-docker.sh
Executable file
15
backend/build-docker.sh
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "--- Rust version info ---"
|
||||||
|
rustup --version
|
||||||
|
rustc --version
|
||||||
|
cargo --version
|
||||||
|
|
||||||
|
echo "--- Building plugin backend ---"
|
||||||
|
cargo build --release
|
||||||
|
mkdir -p out
|
||||||
|
cp target/release/powertools-rs out/backend
|
||||||
|
|
||||||
|
echo " --- Cleaning up ---"
|
||||||
|
# remove root-owned target folder
|
||||||
|
cargo clean
|
4
backend/create_docker_img.sh
Executable file
4
backend/create_docker_img.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# build docker container locally (for testing)
|
||||||
|
|
||||||
|
docker build -t powertools_backend .
|
8
backend/entrypoint.sh
Executable file
8
backend/entrypoint.sh
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Container's IP address: `awk 'END{print $1}' /etc/hosts`"
|
||||||
|
|
||||||
|
cd /backend
|
||||||
|
|
||||||
|
sudo bash build-docker.sh
|
7
backend/run_docker_img.sh
Executable file
7
backend/run_docker_img.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# run docker container locally (for testing)
|
||||||
|
# assumes you're running in the backend/ dir of the project
|
||||||
|
|
||||||
|
docker run -i --entrypoint /backend/entrypoint.sh -v $PWD:/backend powertools_backend
|
||||||
|
mkdir -p ../bin
|
||||||
|
cp ./out/backend ../bin
|
|
@ -59,6 +59,6 @@ pub fn unset_charge_rate(
|
||||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
"Failed to send on save channel",
|
"Failed to send on save channel",
|
||||||
);
|
);
|
||||||
vec![]
|
super::utility::map_empty_result(settings_lock.on_set(), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -139,7 +139,7 @@ pub fn unset_clock_limits(
|
||||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
"Failed to send on save channel",
|
"Failed to send on save channel",
|
||||||
);
|
);
|
||||||
vec![]
|
super::utility::map_empty_result(cpu.on_set(), true)
|
||||||
} else {
|
} else {
|
||||||
vec!["get_clock_limits cpu index out of bounds".into()]
|
vec!["get_clock_limits cpu index out of bounds".into()]
|
||||||
}
|
}
|
|
@ -97,7 +97,7 @@ pub fn get_name(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate get current settings name
|
/// Generate wait for all locks to be available web method
|
||||||
pub fn lock_unlock_all(
|
pub fn lock_unlock_all(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
|
@ -124,7 +124,7 @@ pub fn unset_clock_limits(
|
||||||
unwrap_lock(saver.lock(), "save channel").send(()),
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
"Failed to send on save channel",
|
"Failed to send on save channel",
|
||||||
);
|
);
|
||||||
vec![]
|
super::utility::map_empty_result(settings_lock.on_set(), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,13 @@ use usdpl_back::core::serdes::Primitive;
|
||||||
use usdpl_back::Instance;
|
use usdpl_back::Instance;
|
||||||
|
|
||||||
fn main() -> Result<(), ()> {
|
fn main() -> Result<(), ()> {
|
||||||
let log_filepath = format!("/tmp/{}.log", PACKAGE_NAME);
|
let log_filepath = format!("/home/deck/{}.log", PACKAGE_NAME);
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
if std::path::Path::new(&log_filepath).exists() {
|
||||||
|
std::fs::copy(&log_filepath, "/home/deck/powertools.log.old").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
WriteLogger::init(
|
WriteLogger::init(
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
|
@ -6,6 +6,7 @@ use crate::persist::BatteryJson;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Battery {
|
pub struct Battery {
|
||||||
pub charge_rate: Option<u64>,
|
pub charge_rate: Option<u64>,
|
||||||
|
state: crate::state::Battery,
|
||||||
}
|
}
|
||||||
|
|
||||||
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
|
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
|
||||||
|
@ -17,21 +18,32 @@ impl Battery {
|
||||||
match version {
|
match version {
|
||||||
0 => Self {
|
0 => Self {
|
||||||
charge_rate: other.charge_rate,
|
charge_rate: other.charge_rate,
|
||||||
|
state: crate::state::Battery::default(),
|
||||||
},
|
},
|
||||||
_ => Self {
|
_ => Self {
|
||||||
charge_rate: other.charge_rate,
|
charge_rate: other.charge_rate,
|
||||||
|
state: crate::state::Battery::default(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_all(&self) -> Result<(), SettingError> {
|
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||||
if let Some(charge_rate) = self.charge_rate {
|
if let Some(charge_rate) = self.charge_rate {
|
||||||
|
self.state.charge_rate_set = true;
|
||||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, charge_rate).map_err(
|
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, charge_rate).map_err(
|
||||||
|e| SettingError {
|
|e| SettingError {
|
||||||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: super::SettingVariant::Battery,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
} else if self.state.charge_rate_set {
|
||||||
|
self.state.charge_rate_set = false;
|
||||||
|
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, Self::max().charge_rate.unwrap()).map_err(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||||
|
setting: super::SettingVariant::Battery,
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -66,7 +78,10 @@ impl Battery {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn system_default() -> Self {
|
pub fn system_default() -> Self {
|
||||||
Self { charge_rate: None }
|
Self {
|
||||||
|
charge_rate: None,
|
||||||
|
state: crate::state::Battery::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +103,7 @@ impl OnSet for Battery {
|
||||||
|
|
||||||
impl OnResume for Battery {
|
impl OnResume for Battery {
|
||||||
fn on_resume(&self) -> Result<(), SettingError> {
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
self.set_all()
|
self.clone().set_all()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +112,7 @@ impl SettingsRange for Battery {
|
||||||
fn max() -> Self {
|
fn max() -> Self {
|
||||||
Self {
|
Self {
|
||||||
charge_rate: Some(2500),
|
charge_rate: Some(2500),
|
||||||
|
state: crate::state::Battery::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +120,7 @@ impl SettingsRange for Battery {
|
||||||
fn min() -> Self {
|
fn min() -> Self {
|
||||||
Self {
|
Self {
|
||||||
charge_rate: Some(250),
|
charge_rate: Some(250),
|
||||||
|
state: crate::state::Battery::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,7 @@ pub struct Cpu {
|
||||||
pub clock_limits: Option<MinMax<u64>>,
|
pub clock_limits: Option<MinMax<u64>>,
|
||||||
pub governor: String,
|
pub governor: String,
|
||||||
index: usize,
|
index: usize,
|
||||||
|
state: crate::state::Cpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||||
|
@ -26,17 +27,19 @@ impl Cpu {
|
||||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
governor: other.governor,
|
governor: other.governor,
|
||||||
index: i,
|
index: i,
|
||||||
|
state: crate::state::Cpu::default(),
|
||||||
},
|
},
|
||||||
_ => Self {
|
_ => Self {
|
||||||
online: other.online,
|
online: other.online,
|
||||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
governor: other.governor,
|
governor: other.governor,
|
||||||
index: i,
|
index: i,
|
||||||
|
state: crate::state::Cpu::default(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_all(&self) -> Result<(), SettingError> {
|
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||||
// set cpu online/offline
|
// set cpu online/offline
|
||||||
if self.index != 0 { // cpu0 cannot be disabled
|
if self.index != 0 { // cpu0 cannot be disabled
|
||||||
let online_path = cpu_online_path(self.index);
|
let online_path = cpu_online_path(self.index);
|
||||||
|
@ -48,7 +51,9 @@ impl Cpu {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
// set clock limits
|
// set clock limits
|
||||||
if let Some(clock_limits) = &self.clock_limits {
|
log::debug!("Setting {} to manual", CPU_FORCE_LIMITS_PATH);
|
||||||
|
let mode: String = usdpl_back::api::files::read_single(CPU_FORCE_LIMITS_PATH.to_owned()).unwrap();
|
||||||
|
if mode != "manual" {
|
||||||
// set manual control
|
// set manual control
|
||||||
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||||
SettingError {
|
SettingError {
|
||||||
|
@ -59,8 +64,12 @@ impl Cpu {
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: super::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
}
|
||||||
|
if let Some(clock_limits) = &self.clock_limits {
|
||||||
|
log::debug!("Setting CPU {} (min, max) clockspeed to ({}, {})", self.index, clock_limits.min, clock_limits.max);
|
||||||
|
self.state.clock_limits_set = true;
|
||||||
// max clock
|
// max clock
|
||||||
let payload_max = format!("p {} 1 {}", self.index / 2, clock_limits.max);
|
let payload_max = format!("p {} 1 {}\n", self.index / 2, clock_limits.max);
|
||||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||||
|e| SettingError {
|
|e| SettingError {
|
||||||
msg: format!(
|
msg: format!(
|
||||||
|
@ -71,7 +80,7 @@ impl Cpu {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
// min clock
|
// min clock
|
||||||
let payload_min = format!("p {} 0 {}", self.index / 2, clock_limits.min);
|
let payload_min = format!("p {} 0 {}\n", self.index / 2, clock_limits.min);
|
||||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||||
|e| SettingError {
|
|e| SettingError {
|
||||||
msg: format!(
|
msg: format!(
|
||||||
|
@ -81,25 +90,41 @@ impl Cpu {
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: super::SettingVariant::Cpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
// commit changes
|
} else if self.state.clock_limits_set {
|
||||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c").map_err(|e| {
|
self.state.clock_limits_set = false;
|
||||||
SettingError {
|
|
||||||
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
|
|
||||||
setting: super::SettingVariant::Cpu,
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
} else {
|
|
||||||
// disable manual clock limits
|
// disable manual clock limits
|
||||||
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "auto").map_err(|e| {
|
log::debug!("Setting CPU {} to default clockspeed", self.index);
|
||||||
SettingError {
|
// max clock
|
||||||
|
let payload_max = format!("p {} 1 {}\n", self.index / 2, Self::max().clock_limits.unwrap().max);
|
||||||
|
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||||
|
|e| SettingError {
|
||||||
msg: format!(
|
msg: format!(
|
||||||
"Failed to write `auto` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
CPU_FORCE_LIMITS_PATH, e
|
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: super::SettingVariant::Cpu,
|
||||||
}
|
},
|
||||||
})?;
|
)?;
|
||||||
|
// min clock
|
||||||
|
let payload_min = format!("p {} 0 {}\n", self.index / 2, Self::min().clock_limits.unwrap().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\n").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
// set governor
|
// set governor
|
||||||
if self.index == 0 || self.online {
|
if self.index == 0 || self.online {
|
||||||
let governor_path = cpu_governor_path(self.index);
|
let governor_path = cpu_governor_path(self.index);
|
||||||
|
@ -134,6 +159,7 @@ impl Cpu {
|
||||||
governor: usdpl_back::api::files::read_single(cpu_governor_path(index))
|
governor: usdpl_back::api::files::read_single(cpu_governor_path(index))
|
||||||
.unwrap_or("schedutil".to_owned()),
|
.unwrap_or("schedutil".to_owned()),
|
||||||
index: index,
|
index: index,
|
||||||
|
state: crate::state::Cpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,8 +178,8 @@ impl Cpu {
|
||||||
|
|
||||||
pub fn system_default() -> Vec<Self> {
|
pub fn system_default() -> Vec<Self> {
|
||||||
if let Some(max_cpu) = Self::cpu_count() {
|
if let Some(max_cpu) = Self::cpu_count() {
|
||||||
let mut cpus = Vec::with_capacity(max_cpu + 1);
|
let mut cpus = Vec::with_capacity(max_cpu);
|
||||||
for i in 0..=max_cpu {
|
for i in 0..max_cpu {
|
||||||
cpus.push(Self::from_sys(i));
|
cpus.push(Self::from_sys(i));
|
||||||
}
|
}
|
||||||
cpus
|
cpus
|
||||||
|
@ -183,7 +209,7 @@ impl OnSet for Cpu {
|
||||||
|
|
||||||
impl OnResume for Cpu {
|
impl OnResume for Cpu {
|
||||||
fn on_resume(&self) -> Result<(), SettingError> {
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
self.set_all()
|
self.clone().set_all()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +224,7 @@ impl SettingsRange for Cpu {
|
||||||
}),
|
}),
|
||||||
governor: "schedutil".to_owned(),
|
governor: "schedutil".to_owned(),
|
||||||
index: usize::MAX,
|
index: usize::MAX,
|
||||||
|
state: crate::state::Cpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +234,8 @@ impl SettingsRange for Cpu {
|
||||||
online: false,
|
online: false,
|
||||||
clock_limits: Some(MinMax { max: 500, min: 1400 }),
|
clock_limits: Some(MinMax { max: 500, min: 1400 }),
|
||||||
governor: "schedutil".to_owned(),
|
governor: "schedutil".to_owned(),
|
||||||
index: 0,
|
index: usize::MIN,
|
||||||
|
state: crate::state::Cpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -120,7 +120,8 @@ impl Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_file(&self, json_path: PathBuf, name: String) -> Result<bool, SettingError> {
|
pub fn load_file(&self, filename: PathBuf, name: String) -> Result<bool, SettingError> {
|
||||||
|
let json_path = crate::utility::settings_dir().join(filename);
|
||||||
let mut general_lock = unwrap_lock(self.general.lock(), "general");
|
let mut general_lock = unwrap_lock(self.general.lock(), "general");
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
let settings_json = SettingsJson::open(&json_path).map_err(|e| SettingError {
|
let settings_json = SettingsJson::open(&json_path).map_err(|e| SettingError {
|
|
@ -13,6 +13,7 @@ pub struct Gpu {
|
||||||
pub slow_ppt: Option<u64>,
|
pub slow_ppt: Option<u64>,
|
||||||
pub clock_limits: Option<MinMax<u64>>,
|
pub clock_limits: Option<MinMax<u64>>,
|
||||||
pub slow_memory: bool,
|
pub slow_memory: bool,
|
||||||
|
state: crate::state::Gpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
// same as CPU
|
// same as CPU
|
||||||
|
@ -29,17 +30,19 @@ impl Gpu {
|
||||||
slow_ppt: other.slow_ppt,
|
slow_ppt: other.slow_ppt,
|
||||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
slow_memory: other.slow_memory,
|
slow_memory: other.slow_memory,
|
||||||
|
state: crate::state::Gpu::default(),
|
||||||
},
|
},
|
||||||
_ => Self {
|
_ => Self {
|
||||||
fast_ppt: other.fast_ppt,
|
fast_ppt: other.fast_ppt,
|
||||||
slow_ppt: other.slow_ppt,
|
slow_ppt: other.slow_ppt,
|
||||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
slow_memory: other.slow_memory,
|
slow_memory: other.slow_memory,
|
||||||
|
state: crate::state::Gpu::default(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_all(&self) -> Result<(), SettingError> {
|
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||||
// set fast PPT
|
// set fast PPT
|
||||||
if let Some(fast_ppt) = &self.fast_ppt {
|
if let Some(fast_ppt) = &self.fast_ppt {
|
||||||
let fast_ppt_path = gpu_power_path(FAST_PPT);
|
let fast_ppt_path = gpu_power_path(FAST_PPT);
|
||||||
|
@ -67,7 +70,8 @@ impl Gpu {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
// settings using force_performance_level
|
// settings using force_performance_level
|
||||||
if self.clock_limits.is_some() || self.slow_memory {
|
let mode: String = usdpl_back::api::files::read_single(GPU_FORCE_LIMITS_PATH.to_owned()).unwrap();
|
||||||
|
if mode != "manual" {
|
||||||
// set manual control
|
// set manual control
|
||||||
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||||
SettingError {
|
SettingError {
|
||||||
|
@ -78,58 +82,73 @@ impl Gpu {
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: super::SettingVariant::Gpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
// set clock limits
|
}
|
||||||
if let Some(clock_limits) = &self.clock_limits {
|
// enable/disable downclock of GPU memory (to 400Mhz?)
|
||||||
// max clock
|
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
|
||||||
let payload_max = format!("s 1 {}", clock_limits.max);
|
.map_err(|e| SettingError {
|
||||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
|
||||||
|e| SettingError {
|
setting: super::SettingVariant::Gpu,
|
||||||
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 {
|
if let Some(clock_limits) = &self.clock_limits {
|
||||||
// disable manual clock limits
|
// set clock limits
|
||||||
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "auto").map_err(|e| {
|
self.state.clock_limits_set = true;
|
||||||
SettingError {
|
// max clock
|
||||||
|
let payload_max = format!("s 1 {}\n", clock_limits.max);
|
||||||
|
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||||
|
|e| SettingError {
|
||||||
msg: format!(
|
msg: format!(
|
||||||
"Failed to write `auto` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
GPU_FORCE_LIMITS_PATH, e
|
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: super::SettingVariant::Gpu,
|
||||||
}
|
},
|
||||||
})?;
|
)?;
|
||||||
|
// min clock
|
||||||
|
let payload_min = format!("s 0 {}\n", 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,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
} else if self.state.clock_limits_set {
|
||||||
|
self.state.clock_limits_set = false;
|
||||||
|
// disable manual clock limits
|
||||||
|
// max clock
|
||||||
|
let payload_max = format!("s 1 {}\n", Self::max().clock_limits.unwrap().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 {}\n", Self::min().clock_limits.unwrap().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,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
// commit changes
|
||||||
|
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(()) // TODO
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clamp_all(&mut self) {
|
fn clamp_all(&mut self) {
|
||||||
|
@ -161,6 +180,7 @@ impl Gpu {
|
||||||
slow_ppt: None,
|
slow_ppt: None,
|
||||||
clock_limits: None,
|
clock_limits: None,
|
||||||
slow_memory: false,
|
slow_memory: false,
|
||||||
|
state: crate::state::Gpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +206,7 @@ impl OnSet for Gpu {
|
||||||
|
|
||||||
impl OnResume for Gpu {
|
impl OnResume for Gpu {
|
||||||
fn on_resume(&self) -> Result<(), SettingError> {
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
self.set_all()
|
self.clone().set_all()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +221,7 @@ impl SettingsRange for Gpu {
|
||||||
max: 1600,
|
max: 1600,
|
||||||
}),
|
}),
|
||||||
slow_memory: false,
|
slow_memory: false,
|
||||||
|
state: crate::state::Gpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +232,7 @@ impl SettingsRange for Gpu {
|
||||||
slow_ppt: Some(1000000),
|
slow_ppt: Some(1000000),
|
||||||
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
||||||
slow_memory: true,
|
slow_memory: true,
|
||||||
|
state: crate::state::Gpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
12
backend/src/state/battery.rs
Normal file
12
backend/src/state/battery.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Battery {
|
||||||
|
pub charge_rate_set: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Battery {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
charge_rate_set: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
backend/src/state/cpu.rs
Normal file
12
backend/src/state/cpu.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Cpu {
|
||||||
|
pub clock_limits_set: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Cpu {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
clock_limits_set: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
backend/src/state/gpu.rs
Normal file
12
backend/src/state/gpu.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Gpu {
|
||||||
|
pub clock_limits_set: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Gpu {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
clock_limits_set: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
backend/src/state/mod.rs
Normal file
11
backend/src/state/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
mod battery;
|
||||||
|
mod cpu;
|
||||||
|
mod error;
|
||||||
|
mod gpu;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
pub use battery::Battery;
|
||||||
|
pub use cpu::Cpu;
|
||||||
|
pub use error::StateError;
|
||||||
|
pub use gpu::Gpu;
|
||||||
|
pub use traits::OnPoll;
|
2
main.py
2
main.py
|
@ -11,6 +11,6 @@ class Plugin:
|
||||||
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
||||||
async def _main(self):
|
async def _main(self):
|
||||||
# startup
|
# startup
|
||||||
self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend"])
|
#self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend"])
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
mod error;
|
|
||||||
mod traits;
|
|
||||||
|
|
||||||
pub use error::StateError;
|
|
||||||
pub use traits::OnPoll;
|
|
|
@ -173,16 +173,21 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
onChange={(smt: boolean) => {
|
onChange={(smt: boolean) => {
|
||||||
console.debug("SMT is now " + smt.toString());
|
console.debug("SMT is now " + smt.toString());
|
||||||
const cpus = get_value(ONLINE_CPUS);
|
const cpus = get_value(ONLINE_CPUS);
|
||||||
set_value(ONLINE_CPUS, 0);
|
|
||||||
smtGlobal = smt && smtAllowed;
|
smtGlobal = smt && smtAllowed;
|
||||||
for (let i = 0; i < total_cpus; i++) {
|
for (let i = 0; i < total_cpus; i++) {
|
||||||
const online = (smtGlobal? i < cpus : (i % 2 == 0) && (i < cpus * 2))
|
const online = (smtGlobal? i < cpus : (i % 2 == 0) && (i < cpus * 2))
|
||||||
|| (!smtGlobal && cpus == 4);
|
|| (!smtGlobal && cpus == 4);
|
||||||
backend.resolve(backend.setCpuOnline(i, online), (value: boolean) => {
|
backend.resolve(backend.setCpuOnline(i, online), (_value: boolean) => {});
|
||||||
if (value) {set_value(ONLINE_CPUS, get_value(ONLINE_CPUS) + 1)}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
backend.resolve(backend.waitForComplete(), (_: boolean[]) => {
|
backend.resolve(backend.getCpusOnline(), (statii: boolean[]) => {
|
||||||
|
// TODO: allow for per-core control of online status
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i < statii.length; i++) {
|
||||||
|
if (statii[i]) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_value(ONLINE_CPUS, count);
|
||||||
reloadGUI("SMT");
|
reloadGUI("SMT");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
@ -200,14 +205,21 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
console.debug("CPU slider is now " + cpus.toString());
|
console.debug("CPU slider is now " + cpus.toString());
|
||||||
const onlines = get_value(ONLINE_CPUS);
|
const onlines = get_value(ONLINE_CPUS);
|
||||||
if (cpus != onlines) {
|
if (cpus != onlines) {
|
||||||
set_value(ONLINE_CPUS, 0);
|
|
||||||
for (let i = 0; i < total_cpus; i++) {
|
for (let i = 0; i < total_cpus; i++) {
|
||||||
const online = smtGlobal? i < cpus : (i % 2 == 0) && (i < cpus * 2);
|
const online = smtGlobal? i < cpus : (i % 2 == 0) && (i < cpus * 2);
|
||||||
backend.resolve(backend.setCpuOnline(i, online), (value: boolean) => {
|
backend.resolve(backend.setCpuOnline(i, online), (value: boolean) => {
|
||||||
if (value) {set_value(ONLINE_CPUS, get_value(ONLINE_CPUS) + 1)}
|
if (value) {set_value(ONLINE_CPUS, get_value(ONLINE_CPUS) + 1)}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
backend.resolve(backend.waitForComplete(), (_: boolean[]) => {
|
backend.resolve(backend.getCpusOnline(), (statii: boolean[]) => {
|
||||||
|
// TODO: allow for per-core control of online status
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i < statii.length; i++) {
|
||||||
|
if (statii[i]) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_value(ONLINE_CPUS, count);
|
||||||
reloadGUI("CPUs");
|
reloadGUI("CPUs");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -218,6 +230,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
<ToggleField
|
<ToggleField
|
||||||
checked={get_value(CLOCK_MIN_CPU) != null && get_value(CLOCK_MAX_CPU) != null}
|
checked={get_value(CLOCK_MIN_CPU) != null && get_value(CLOCK_MAX_CPU) != null}
|
||||||
label="Frequency Limits"
|
label="Frequency Limits"
|
||||||
|
description="Set bounds on clock speed"
|
||||||
onChange={(value: boolean) => {
|
onChange={(value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
set_value(CLOCK_MIN_CPU, 1400);
|
set_value(CLOCK_MIN_CPU, 1400);
|
||||||
|
@ -365,6 +378,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
<ToggleField
|
<ToggleField
|
||||||
checked={get_value(CLOCK_MIN_GPU) != null && get_value(CLOCK_MAX_GPU) != null}
|
checked={get_value(CLOCK_MIN_GPU) != null && get_value(CLOCK_MAX_GPU) != null}
|
||||||
label="Frequency Limits"
|
label="Frequency Limits"
|
||||||
|
description="Override bounds on gpu clock"
|
||||||
onChange={(value: boolean) => {
|
onChange={(value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
set_value(CLOCK_MIN_GPU, 200);
|
set_value(CLOCK_MIN_GPU, 200);
|
||||||
|
|
Loading…
Reference in a new issue