Replace pt_oc functionality with cross-provider equivalent (& resultant refactor)

This commit is contained in:
NGnius (Graham) 2023-10-24 18:33:56 -04:00
parent a8ad9a9e62
commit a90932d813
47 changed files with 2727 additions and 934 deletions

2
backend/Cargo.lock generated
View file

@ -808,7 +808,7 @@ dependencies = [
[[package]]
name = "limits_core"
version = "2.0.1"
version = "3.0.0"
dependencies = [
"serde",
"serde_json",

View file

@ -26,7 +26,7 @@ log = "0.4"
simplelog = "0.12"
# limits & driver functionality
limits_core = { version = "2", path = "./limits_core" }
limits_core = { version = "3", path = "./limits_core" }
regex = "1"
libryzenadj = { version = "0.12" }
# ureq's tls feature does not like musl targets

View file

@ -10,7 +10,7 @@ checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
[[package]]
name = "limits_core"
version = "2.0.1"
version = "3.0.0"
dependencies = [
"serde",
"serde_json",

View file

@ -1,6 +1,6 @@
[package]
name = "limits_core"
version = "2.0.1"
version = "3.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -0,0 +1,267 @@
use std::default::Default;
use serde::{Deserialize, Serialize};
/// Base JSON limits information
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Base {
/// System-specific configurations
pub configs: Vec<super::Config>,
/// Server messages
pub messages: Vec<super::DeveloperMessage>,
/// URL from which to grab the next update
pub refresh: Option<String>,
}
impl Default for Base {
fn default() -> Self {
Base {
configs: vec![
super::Config {
name: "Steam Deck Custom".to_owned(),
conditions: super::Conditions {
dmi: None,
cpuinfo: Some("model name\t: AMD Custom APU 0405\n".to_owned()),
os: None,
command: None,
file_exists: Some("./limits_override.json".into()),
},
limits: super::Limits {
cpu: super::Limit {
provider: super::CpuLimitType::SteamDeckAdvance,
limits: super::GenericCpusLimit::default_for(super::CpuLimitType::SteamDeckAdvance),
},
gpu: super::Limit {
provider: super::GpuLimitType::SteamDeckAdvance,
limits: super::GenericGpuLimit::default_for(super::GpuLimitType::SteamDeckAdvance),
},
battery: super::Limit {
provider: super::BatteryLimitType::SteamDeckAdvance,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::SteamDeckAdvance),
},
}
},
super::Config {
name: "Steam Deck".to_owned(),
conditions: super::Conditions {
dmi: None,
cpuinfo: Some("model name\t: AMD Custom APU 0405\n".to_owned()),
os: None,
command: None,
file_exists: None,
},
limits: super::Limits {
cpu: super::Limit {
provider: super::CpuLimitType::SteamDeck,
limits: super::GenericCpusLimit::default_for(super::CpuLimitType::SteamDeck),
},
gpu: super::Limit {
provider: super::GpuLimitType::SteamDeck,
limits: super::GenericGpuLimit::default_for(super::GpuLimitType::SteamDeck),
},
battery: super::Limit {
provider: super::BatteryLimitType::SteamDeck,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::SteamDeck),
},
}
},
super::Config {
name: "AMD R3 2300U".to_owned(),
conditions: super::Conditions {
dmi: None,
cpuinfo: Some("model name\t+: AMD Ryzen 3 2300U\n".to_owned()),
os: None,
command: None,
file_exists: None,
},
limits: super::Limits {
cpu: super::CpuLimit {
provider: super::CpuLimitType::GenericAMD,
limits: super::GenericCpusLimit {
cpus: vec![
super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(3700) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(3700) }),
clock_step: Some(100),
skip_resume_reclock: false,
}; 4],
global_governors: true,
}
},
gpu: super::GpuLimit {
provider: super::GpuLimitType::GenericAMD,
limits: super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(1100) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(1100) }),
clock_step: Some(100),
..Default::default()
}
},
battery: super::Limit {
provider: super::BatteryLimitType::Generic,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::Generic),
}
},
},
super::Config {
name: "AMD R5 5560U".to_owned(),
conditions: super::Conditions {
dmi: None,
cpuinfo: Some("model name\t+: AMD Ryzen 5 5560U\n".to_owned()),
os: None,
command: None,
file_exists: None,
},
limits: super::Limits {
cpu: super::CpuLimit {
provider: super::CpuLimitType::GenericAMD,
limits: super::GenericCpusLimit {
cpus: vec![
super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(4000) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(4000) }),
clock_step: Some(100),
skip_resume_reclock: false,
}; 12], // 6 cores with SMTx2
global_governors: true,
}
},
gpu: super::GpuLimit {
provider: super::GpuLimitType::GenericAMD,
limits: super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(1600) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(1600) }),
clock_step: Some(100),
..Default::default()
}
},
battery: super::Limit {
provider: super::BatteryLimitType::Generic,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::Generic),
}
}
},
super::Config {
name: "AMD R7 5825U".to_owned(),
conditions: super::Conditions {
dmi: None,
cpuinfo: Some("model name\t+: AMD Ryzen 7 5825U\n".to_owned()),
os: None,
command: None,
file_exists: None,
},
limits: super::Limits {
cpu: super::CpuLimit {
provider: super::CpuLimitType::GenericAMD,
limits: super::GenericCpusLimit {
cpus: vec![
super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(4500) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(4500) }),
clock_step: Some(100),
skip_resume_reclock: false,
}; 16], // 8 cores with SMTx2
global_governors: true,
}
},
gpu: super::GpuLimit {
provider: super::GpuLimitType::GenericAMD,
limits: super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(28_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(28_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(2200) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(2200) }),
clock_step: Some(100),
..Default::default()
}
},
battery: super::Limit {
provider: super::BatteryLimitType::Generic,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::Generic),
}
}
},
super::Config {
name: "AMD R7 6800U".to_owned(),
conditions: super::Conditions {
dmi: None,
cpuinfo: Some("model name\t+: AMD Ryzen 7 6800U\n".to_owned()),
os: None,
command: None,
file_exists: None,
},
limits: super::Limits {
cpu: super::CpuLimit {
provider: super::CpuLimitType::GenericAMD,
limits: super::GenericCpusLimit {
cpus: vec![
super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(4700) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(4700) }),
clock_step: Some(100),
skip_resume_reclock: false,
}; 16], // 8 cores with SMTx2
global_governors: true,
}
},
gpu: super::GpuLimit {
provider: super::GpuLimitType::GenericAMD,
limits: super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(28_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(28_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(2200) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(2200) }),
clock_step: Some(100),
..Default::default()
}
},
battery: super::Limit {
provider: super::BatteryLimitType::Generic,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::Generic),
}
}
},
super::Config {
name: "Fallback".to_owned(),
conditions: super::Conditions {
dmi: None,
cpuinfo: None,
os: None,
command: None,
file_exists: None,
},
limits: super::Limits {
cpu: super::Limit {
provider: super::CpuLimitType::Unknown,
limits: super::GenericCpusLimit::default_for(super::CpuLimitType::Unknown),
},
gpu: super::Limit {
provider: super::GpuLimitType::Unknown,
limits: super::GenericGpuLimit::default_for(super::GpuLimitType::Unknown),
},
battery: super::Limit {
provider: super::BatteryLimitType::Unknown,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::Unknown),
}
}
}
],
messages: vec![
super::DeveloperMessage {
id: 1,
title: "Welcome".to_owned(),
body: "Thanks for installing PowerTools! For more information, please check the wiki. For bugs and requests, please create an issue.".to_owned(),
url: Some("https://git.ngni.us/NG-SD-Plugins/PowerTools/wiki".to_owned()),
}
],
refresh: Some("http://limits.ngni.us:45000/powertools/v2".to_owned())
}
}
}

View file

@ -0,0 +1,69 @@
use serde::{Deserialize, Serialize};
use super::RangeLimit;
#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(tag = "target")]
pub enum BatteryLimitType {
SteamDeck,
SteamDeckAdvance,
Generic,
Unknown,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct GenericBatteryLimit {
pub charge_rate: Option<RangeLimit<u64>>,
pub charge_modes: Vec<String>,
pub charge_limit: Option<RangeLimit<f64>>, // battery charge %
pub extra_readouts: bool,
}
impl GenericBatteryLimit {
pub fn default_for(t: BatteryLimitType) -> Self {
match t {
BatteryLimitType::SteamDeck | BatteryLimitType::SteamDeckAdvance => Self::default_steam_deck(),
_t => Self::default(),
}
}
fn default_steam_deck() -> Self {
Self {
charge_rate: Some(RangeLimit {
min: Some(250),
max: Some(2500),
}),
charge_modes: vec![
"normal".to_owned(),
"discharge".to_owned(),
"idle".to_owned(),
],
charge_limit: Some(RangeLimit {
min: Some(10.0),
max: Some(90.0),
}),
extra_readouts: false,
}
}
pub fn apply_override(&mut self, limit_override: Self) {
if let Some(range) = limit_override.charge_rate {
if range.min.is_none() && range.max.is_none() {
self.charge_rate = None;
} else {
self.charge_rate = Some(range);
}
}
if self.charge_modes.len() != limit_override.charge_modes.len() && !limit_override.charge_modes.is_empty() {
// assume limit_override.cpus wants to override even the cpu count
self.charge_modes = limit_override.charge_modes;
}
if let Some(range) = limit_override.charge_limit {
if range.min.is_none() && range.max.is_none() {
self.charge_limit = None;
} else {
self.charge_limit = Some(range);
}
}
self.extra_readouts = limit_override.extra_readouts;
}
}

View file

@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
/// Conditions under which a config applies (ANDed together)
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Conditions {
/// Regex pattern for dmidecode output
pub dmi: Option<String>,
/// Regex pattern for /proc/cpuinfo reading
pub cpuinfo: Option<String>,
/// Regex pattern for /etc/os-release reading
pub os: Option<String>,
/// Custom command to run, where an exit code of 0 means a successful match
pub command: Option<String>,
/// Check if file exists
pub file_exists: Option<String>,
}
impl Conditions {
pub fn is_empty(&self) -> bool {
self.dmi.is_none()
&& self.cpuinfo.is_none()
&& self.os.is_none()
&& self.command.is_none()
&& self.file_exists.is_none()
}
}

View file

@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Config {
pub name: String,
pub conditions: super::Conditions,
pub limits: super::Limits,
}

View file

@ -0,0 +1,126 @@
use serde::{Deserialize, Serialize};
use super::RangeLimit;
#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(tag = "target")]
pub enum CpuLimitType {
SteamDeck,
SteamDeckAdvance,
Generic,
GenericAMD,
Unknown,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct GenericCpusLimit {
pub cpus: Vec<GenericCpuLimit>,
pub global_governors: bool,
}
impl GenericCpusLimit {
pub fn default_for(t: CpuLimitType) -> Self {
match t {
CpuLimitType::SteamDeck | CpuLimitType::SteamDeckAdvance => {
Self {
cpus: [(); 8].iter().enumerate().map(|(i, _)| GenericCpuLimit::default_for(&t, i)).collect(),
global_governors: true,
}
},
t => {
let cpu_count = Self::cpu_count().unwrap_or(8);
let mut cpus = Vec::with_capacity(cpu_count);
for i in 0..cpu_count {
cpus.push(GenericCpuLimit::default_for(&t, i));
}
Self {
cpus,
global_governors: true,
}
}
}
}
fn cpu_count() -> Option<usize> {
let mut data: String = std::fs::read_to_string("/sys/devices/system/cpu/present")
.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 + 1);
}
}
None
}
pub fn apply_override(&mut self, limit_override: Self) {
if self.cpus.len() != limit_override.cpus.len() && !limit_override.cpus.is_empty() {
// assume limit_override.cpus wants to override even the cpu count
self.cpus = limit_override.cpus;
} else {
self.cpus.iter_mut()
.zip(limit_override.cpus.into_iter())
.for_each(|(cpu, limit_override)| cpu.apply_override(limit_override));
}
self.global_governors = limit_override.global_governors;
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct GenericCpuLimit {
pub clock_min: Option<RangeLimit<u64>>,
pub clock_max: Option<RangeLimit<u64>>,
pub clock_step: Option<u64>,
pub skip_resume_reclock: bool,
}
impl GenericCpuLimit {
pub fn default_for(t: &CpuLimitType, _index: usize) -> Self {
match t {
CpuLimitType::SteamDeck | CpuLimitType::SteamDeckAdvance => Self::default_steam_deck(),
_ => Self {
clock_min: None,
clock_max: None,
clock_step: Some(100),
skip_resume_reclock: false,
},
}
}
fn default_steam_deck() -> Self {
Self {
clock_min: Some(RangeLimit {
min: Some(1400),
max: Some(3500),
}),
clock_max: Some(RangeLimit {
min: Some(400),
max: Some(3500),
}),
clock_step: Some(100),
skip_resume_reclock: false,
}
}
pub fn apply_override(&mut self, limit_override: Self) {
if let Some(range) = limit_override.clock_min {
if range.min.is_none() && range.max.is_none() {
self.clock_min = None;
} else {
self.clock_min = Some(range);
}
}
if let Some(range) = limit_override.clock_max {
if range.min.is_none() && range.max.is_none() {
self.clock_max = None;
} else {
self.clock_max = Some(range);
}
}
if let Some(val) = limit_override.clock_step {
self.clock_step = Some(val);
}
self.clock_step = limit_override.clock_step;
self.skip_resume_reclock = limit_override.skip_resume_reclock;
}
}

View file

@ -0,0 +1,14 @@
use serde::{Deserialize, Serialize};
/// Message from the developers
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DeveloperMessage {
/// Message identifier
pub id: u64,
/// Message title
pub title: String,
/// Message content
pub body: String,
/// Link for further information
pub url: Option<String>,
}

View file

@ -0,0 +1,132 @@
use serde::{Deserialize, Serialize};
use super::RangeLimit;
#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(tag = "target")]
pub enum GpuLimitType {
SteamDeck,
SteamDeckAdvance,
Generic,
GenericAMD,
Unknown,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct GenericGpuLimit {
pub fast_ppt: Option<RangeLimit<u64>>,
pub fast_ppt_default: Option<u64>,
pub slow_ppt: Option<RangeLimit<u64>>,
pub slow_ppt_default: Option<u64>,
pub ppt_divisor: Option<u64>,
pub ppt_step: Option<u64>,
pub tdp: Option<RangeLimit<u64>>,
pub tdp_boost: Option<RangeLimit<u64>>,
pub tdp_step: Option<u64>,
pub clock_min: Option<RangeLimit<u64>>,
pub clock_max: Option<RangeLimit<u64>>,
pub clock_step: Option<u64>,
pub skip_resume_reclock: bool,
}
impl GenericGpuLimit {
pub fn default_for(t: GpuLimitType) -> Self {
match t {
GpuLimitType::SteamDeck | GpuLimitType::SteamDeckAdvance => Self::default_steam_deck(),
_t => Self::default(),
}
}
fn default_steam_deck() -> Self {
Self {
fast_ppt: Some(RangeLimit {
min: Some(1000000),
max: Some(30_000_000),
}),
fast_ppt_default: Some(15_000_000),
slow_ppt: Some(RangeLimit {
min: Some(1000000),
max: Some(29_000_000),
}),
slow_ppt_default: Some(15_000_000),
ppt_divisor: Some(1_000_000),
ppt_step: Some(1),
tdp: None,
tdp_boost: None,
tdp_step: None,
clock_min: Some(RangeLimit {
min: Some(400),
max: Some(1600),
}),
clock_max: Some(RangeLimit {
min: Some(400),
max: Some(1600),
}),
clock_step: Some(100),
skip_resume_reclock: false,
}
}
pub fn apply_override(&mut self, limit_override: Self) {
if let Some(range) = limit_override.fast_ppt {
if range.min.is_none() && range.max.is_none() {
self.fast_ppt = None;
} else {
self.fast_ppt = Some(range);
}
}
if let Some(def) = limit_override.fast_ppt_default {
self.fast_ppt_default = Some(def);
}
if let Some(range) = limit_override.slow_ppt {
if range.min.is_none() && range.max.is_none() {
self.slow_ppt = None;
} else {
self.slow_ppt = Some(range);
}
}
if let Some(def) = limit_override.slow_ppt_default {
self.slow_ppt_default = Some(def);
}
if let Some(val) = limit_override.ppt_divisor {
self.ppt_divisor = Some(val);
}
if let Some(val) = limit_override.ppt_step {
self.ppt_step = Some(val);
}
if let Some(range) = limit_override.tdp {
if range.min.is_none() && range.max.is_none() {
self.tdp = None;
} else {
self.tdp = Some(range);
}
}
if let Some(range) = limit_override.tdp_boost {
if range.min.is_none() && range.max.is_none() {
self.tdp_boost = None;
} else {
self.tdp_boost = Some(range);
}
}
if let Some(val) = limit_override.tdp_step {
self.tdp_step = Some(val);
}
if let Some(range) = limit_override.clock_min {
if range.min.is_none() && range.max.is_none() {
self.clock_min = None;
} else {
self.clock_min = Some(range);
}
}
if let Some(range) = limit_override.clock_max {
if range.min.is_none() && range.max.is_none() {
self.clock_max = None;
} else {
self.clock_max = Some(range);
}
}
if let Some(val) = limit_override.clock_step {
self.clock_step = Some(val);
}
self.skip_resume_reclock = limit_override.skip_resume_reclock;
}
}

View file

@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Limits {
pub cpu: CpuLimit,
pub gpu: GpuLimit,
pub battery: BatteryLimit,
}
impl Limits {
pub fn apply_override(&mut self, limit_override: Option<Self>) {
if let Some(limit_override) = limit_override {
self.cpu.limits.apply_override(limit_override.cpu.limits);
self.gpu.limits.apply_override(limit_override.gpu.limits);
self.battery.limits.apply_override(limit_override.battery.limits);
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Limit<P, L> {
pub provider: P,
pub limits: L,
}
pub type CpuLimit = Limit<super::CpuLimitType, super::GenericCpusLimit>;
pub type GpuLimit = Limit<super::GpuLimitType, super::GenericGpuLimit>;
pub type BatteryLimit = Limit<super::BatteryLimitType, super::GenericBatteryLimit>;

View file

@ -0,0 +1,21 @@
mod base;
mod battery_limit;
mod conditions;
mod config;
mod cpu_limit;
mod devel_message;
mod gpu_limit;
mod limits;
mod range;
mod target;
pub use base::Base;
pub use battery_limit::{BatteryLimitType, GenericBatteryLimit};
pub use conditions::Conditions;
pub use cpu_limit::{CpuLimitType, GenericCpusLimit, GenericCpuLimit};
pub use devel_message::DeveloperMessage;
pub use gpu_limit::{GpuLimitType, GenericGpuLimit};
pub use config::Config;
pub use limits::{Limits, Limit, CpuLimit, GpuLimit, BatteryLimit};
pub use range::RangeLimit;
pub use target::Target;

View file

@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
/// Base JSON limits information
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct RangeLimit<T> {
pub min: Option<T>,
pub max: Option<T>,
}

View file

@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Target {
SteamDeck,
SteamDeckAdvance,
Generic,
Unknown,
}

View file

@ -1 +1,2 @@
pub mod json;
pub mod json_v2;

View file

@ -433,7 +433,7 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "limits_core"
version = "2.0.1"
version = "3.0.0"
dependencies = [
"serde",
"serde_json",
@ -441,7 +441,7 @@ dependencies = [
[[package]]
name = "limits_srv"
version = "2.0.1"
version = "3.0.0"
dependencies = [
"chrono",
"limits_core",

View file

@ -1,12 +1,12 @@
[package]
name = "limits_srv"
version = "2.0.1"
version = "3.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
limits_core = { version = "2.0.1", path = "../limits_core" }
limits_core = { version = "3.0.0", path = "../limits_core" }
chrono = { version = "0.4" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View file

@ -293,8 +293,8 @@
{
"id": 1,
"title": "Welcome",
"body": "Thanks for installing PowerTools! For more information, please check the wiki. For bugs and requests, please create an issue on GitHub.",
"url": "https://github.com/NGnius/PowerTools/wiki"
"body": "Thanks for installing PowerTools! For more information, please check the wiki. For bugs and requests, please create an issue.",
"url": "https://git.ngni.us/NG-SD-Plugins/PowerTools/wiki"
}
],
"refresh": "http://limits.ngni.us:45000/powertools/v1"

File diff suppressed because it is too large Load diff

View file

@ -4,46 +4,61 @@ use std::sync::{RwLock, Arc};
use serde::Serialize;
use warp::Filter;
use limits_core::json::Base;
static VISIT_COUNT: AtomicU64 = AtomicU64::new(0);
static VISIT_V1_COUNT: AtomicU64 = AtomicU64::new(0);
static VISIT_V2_COUNT: AtomicU64 = AtomicU64::new(0);
static START_TIME: AtomicI64 = AtomicI64::new(0);
fn get_limits(base: Base) -> impl warp::Reply {
VISIT_COUNT.fetch_add(1, Ordering::AcqRel);
fn get_limits_v1(base: &limits_core::json::Base) -> impl warp::Reply {
VISIT_V1_COUNT.fetch_add(1, Ordering::AcqRel);
//println!("Limits got");
warp::reply::json(&base)
warp::reply::json(base)
}
fn get_limits_v2(base: &limits_core::json_v2::Base) -> impl warp::Reply {
VISIT_V2_COUNT.fetch_add(1, Ordering::AcqRel);
//println!("Limits got");
warp::reply::json(base)
}
#[derive(Serialize)]
struct Visits {
visits: u64,
visits_v1: u64,
visits_v2: u64,
since: i64, // Unix time (since epoch)
}
fn get_visits() -> impl warp::Reply {
let count = VISIT_COUNT.load(Ordering::Relaxed);
let count_v1 = VISIT_V1_COUNT.load(Ordering::Relaxed);
let count_v2 = VISIT_V2_COUNT.load(Ordering::Relaxed);
let start = START_TIME.load(Ordering::Relaxed);
//println!("Count got");
warp::reply::json(&Visits {
visits: count,
visits_v1: count_v1,
visits_v2: count_v2,
since: start,
})
}
#[allow(opaque_hidden_inferred_bound)]
fn routes(base: Arc<RwLock<Base>>) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
fn routes(base: Arc<RwLock<limits_core::json::Base>>, base2: Arc<RwLock<limits_core::json_v2::Base>>) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::get().and(
warp::path!("powertools" / "v1")
.map(move || {
let base = base.read().expect("Failed to acquire base limits read lock").clone();
get_limits(base)
let base = base.read().expect("Failed to acquire base limits read lock");
get_limits_v1(&base)
})
.or(
warp::path!("powertools" / "count")
.map(get_visits)
)
.or(
warp::path!("powertools" / "v2")
.map(move || {
let base2 = base2.read().expect("Failed to acquire base limits read lock");
get_limits_v2(&base2)
})
)
).recover(recovery)
}
@ -59,10 +74,14 @@ pub async fn recovery(reject: warp::Rejection) -> Result<impl warp::Reply, warp:
async fn main() {
START_TIME.store(chrono::Utc::now().timestamp(), Ordering::Relaxed);
let file = std::fs::File::open("./pt_limits.json").expect("Failed to read limits file");
let limits: Base = serde_json::from_reader(file).expect("Failed to parse limits file");
let limits: limits_core::json::Base = serde_json::from_reader(file).expect("Failed to parse limits file");
assert!(limits.refresh.is_some(), "`refresh` cannot be null, since it will brick future refreshes");
warp::serve(routes(Arc::new(RwLock::new(limits))))
let file = std::fs::File::open("./pt_limits_v2.json").expect("Failed to read limits file");
let limits_v2: limits_core::json_v2::Base = serde_json::from_reader(file).expect("Failed to parse limits file");
assert!(limits_v2.refresh.is_some(), "`refresh` cannot be null, since it will brick future refreshes");
warp::serve(routes(Arc::new(RwLock::new(limits)), Arc::new(RwLock::new(limits_v2))))
.run(([0, 0, 0, 0], 8080))
.await;
}
@ -75,4 +94,11 @@ mod test {
let output_file = std::fs::File::create("./pt_limits.json").unwrap();
serde_json::to_writer_pretty(output_file, &limits).unwrap();
}
#[test]
fn generate_default_pt_limits_v2() {
let limits = limits_core::json_v2::Base::default();
let output_file = std::fs::File::create("./pt_limits_v2.json").unwrap();
serde_json::to_writer_pretty(output_file, &limits).unwrap();
}
}

View file

@ -7,5 +7,7 @@ pub const DEFAULT_SETTINGS_FILE: &str = "default_settings.json";
pub const DEFAULT_SETTINGS_NAME: &str = "Main";
pub const LIMITS_FILE: &str = "limits_cache.json";
pub const LIMITS_OVERRIDE_FILE: &str = "limits_override.json";
pub const MESSAGE_SEEN_ID_FILE: &str = "seen_message.bin";

View file

@ -2,12 +2,12 @@ use std::fs::File;
use regex::RegexBuilder;
use limits_core::json::{BatteryLimit, CpuLimit, GpuLimit, Limits};
use limits_core::json_v2::{BatteryLimitType, CpuLimitType, GpuLimitType, Limits};
use crate::persist::{DriverJson, SettingsJson};
use crate::settings::{Driver, General, TBattery, TCpus, TGeneral, TGpu};
use crate::settings::{Driver, General, TBattery, TCpus, TGeneral, TGpu, ProviderBuilder};
fn get_limits() -> limits_core::json::Base {
fn get_limits() -> limits_core::json_v2::Base {
let limits_path = super::utility::limits_path();
match File::open(&limits_path) {
Ok(f) => match serde_json::from_reader(f) {
@ -18,7 +18,7 @@ fn get_limits() -> limits_core::json::Base {
limits_path.display(),
e
);
limits_core::json::Base::default()
limits_core::json_v2::Base::default()
}
},
Err(e) => {
@ -32,6 +32,31 @@ fn get_limits() -> limits_core::json::Base {
}
}
fn get_limits_overrides() -> Option<Limits> {
let limits_override_path = super::utility::limits_override_path();
match File::open(&limits_override_path) {
Ok(f) => match serde_json::from_reader(f) {
Ok(lim) => Some(lim),
Err(e) => {
log::warn!(
"Failed to parse limits override file `{}`, cannot use for auto_detect: {}",
limits_override_path.display(),
e
);
None
}
},
Err(e) => {
log::info!(
"Failed to open limits override file `{}`: {}",
limits_override_path.display(),
e
);
None
}
}
}
#[inline]
pub fn auto_detect_provider() -> DriverJson {
let provider = auto_detect0(
@ -51,7 +76,13 @@ pub fn auto_detect0(
json_path: std::path::PathBuf,
name: String,
) -> Driver {
let mut builder = DriverBuilder::new(json_path, name);
let mut general_driver = Box::new(General {
persistent: false,
path: json_path,
name,
driver: DriverJson::AutoDetect,
events: Default::default(),
});
let cpu_info: String = usdpl_back::api::files::read_single("/proc/cpuinfo").unwrap_or_default();
log::debug!("Read from /proc/cpuinfo:\n{}", cpu_info);
@ -65,268 +96,229 @@ pub fn auto_detect0(
log::debug!("Read dmidecode:\n{}", dmi_info);
let limits = get_limits();
let limits_override = get_limits_overrides();
// build driver based on limits conditions
for conf in limits.configs {
let conditions = conf.conditions;
let mut matches = true;
if conditions.is_empty() {
matches = !builder.is_complete();
} else {
if let Some(dmi) = &conditions.dmi {
let pattern = RegexBuilder::new(dmi)
.multi_line(true)
.build()
.expect("Invalid DMI regex");
matches &= pattern.is_match(&dmi_info);
}
if let Some(cpuinfo) = &conditions.cpuinfo {
let pattern = RegexBuilder::new(cpuinfo)
.multi_line(true)
.build()
.expect("Invalid CPU regex");
matches &= pattern.is_match(&cpu_info);
}
if let Some(os) = &conditions.os {
let pattern = RegexBuilder::new(os)
.multi_line(true)
.build()
.expect("Invalid OS regex");
matches &= pattern.is_match(&os_info);
}
if let Some(cmd) = &conditions.command {
match std::process::Command::new("bash")
.args(["-c", cmd])
.status()
{
Ok(status) => matches &= status.code().map(|c| c == 0).unwrap_or(false),
Err(e) => log::warn!("Ignoring bash limits error: {}", e),
}
}
if let Some(file_exists) = &conditions.file_exists {
let exists = std::path::Path::new(file_exists).exists();
matches &= exists;
if let Some(dmi) = &conditions.dmi {
let pattern = RegexBuilder::new(dmi)
.multi_line(true)
.build()
.expect("Invalid DMI regex");
matches &= pattern.is_match(&dmi_info);
}
if let Some(cpuinfo) = &conditions.cpuinfo {
let pattern = RegexBuilder::new(cpuinfo)
.multi_line(true)
.build()
.expect("Invalid CPU regex");
matches &= pattern.is_match(&cpu_info);
}
if let Some(os) = &conditions.os {
let pattern = RegexBuilder::new(os)
.multi_line(true)
.build()
.expect("Invalid OS regex");
matches &= pattern.is_match(&os_info);
}
if let Some(cmd) = &conditions.command {
match std::process::Command::new("bash")
.args(["-c", cmd])
.status()
{
Ok(status) => matches &= status.code().map(|c| c == 0).unwrap_or(false),
Err(e) => log::warn!("Ignoring bash limits error: {}", e),
}
}
if let Some(file_exists) = &conditions.file_exists {
let exists = std::path::Path::new(file_exists).exists();
matches &= exists;
}
if matches {
let mut relevant_limits = conf.limits.clone();
relevant_limits.apply_override(limits_override);
if let Some(settings) = &settings_opt {
*builder.general.persistent() = true;
builder.general.name(settings.name.clone());
for limit in conf.limits {
match limit {
Limits::Cpu(cpus) => {
let cpu_driver: Box<dyn TCpus> = match cpus {
CpuLimit::SteamDeck => {
Box::new(crate::settings::steam_deck::Cpus::from_json(
settings.cpus.clone(),
settings.version,
))
}
CpuLimit::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Cpus::from_json(
settings.cpus.clone(),
settings.version,
))
}
CpuLimit::Generic(x) => Box::new(crate::settings::generic::Cpus::<
crate::settings::generic::Cpu,
>::from_json_and_limits(
settings.cpus.clone(),
settings.version,
x,
)),
CpuLimit::GenericAMD(x) => Box::new(
crate::settings::generic_amd::Cpus::from_json_and_limits(
settings.cpus.clone(),
settings.version,
x,
),
),
CpuLimit::Unknown => {
Box::new(crate::settings::unknown::Cpus::from_json(
settings.cpus.clone(),
settings.version,
))
}
};
builder.cpus = Some(cpu_driver);
}
Limits::Gpu(gpu) => {
let driver: Box<dyn TGpu> = match gpu {
GpuLimit::SteamDeck => {
Box::new(crate::settings::steam_deck::Gpu::from_json(
settings.gpu.clone(),
settings.version,
))
}
GpuLimit::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Gpu::from_json(
settings.gpu.clone(),
settings.version,
))
}
GpuLimit::Generic(x) => {
Box::new(crate::settings::generic::Gpu::from_json_and_limits(
settings.gpu.clone(),
settings.version,
x,
))
}
GpuLimit::GenericAMD(x) => Box::new(
crate::settings::generic_amd::Gpu::from_json_and_limits(
settings.gpu.clone(),
settings.version,
x,
),
),
GpuLimit::Unknown => {
Box::new(crate::settings::unknown::Gpu::from_json(
settings.gpu.clone(),
settings.version,
))
}
};
builder.gpu = Some(driver);
}
Limits::Battery(batt) => {
let driver: Box<dyn TBattery> = match batt {
BatteryLimit::SteamDeck => {
Box::new(crate::settings::steam_deck::Battery::from_json(
settings.battery.clone(),
settings.version,
))
}
BatteryLimit::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Battery::from_json(
settings.battery.clone(),
settings.version,
))
}
BatteryLimit::Generic(x) => Box::new(
crate::settings::generic::Battery::from_json_and_limits(
settings.battery.clone(),
settings.version,
x,
),
),
BatteryLimit::Unknown => {
Box::new(crate::settings::unknown::Battery)
}
};
builder.battery = Some(driver);
}
*general_driver.persistent() = true;
let cpu_driver: Box<dyn TCpus> = match relevant_limits.cpu.provider {
CpuLimitType::SteamDeck => {
Box::new(crate::settings::steam_deck::Cpus::from_json_and_limits(
settings.cpus.clone(),
settings.version,
relevant_limits.cpu.limits,
))
}
}
CpuLimitType::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Cpus::from_json_and_limits(
settings.cpus.clone(),
settings.version,
relevant_limits.cpu.limits,
))
}
CpuLimitType::Generic => Box::new(crate::settings::generic::Cpus::<
crate::settings::generic::Cpu,
>::from_json_and_limits(
settings.cpus.clone(),
settings.version,
relevant_limits.cpu.limits,
)),
CpuLimitType::GenericAMD => Box::new(
crate::settings::generic_amd::Cpus::from_json_and_limits(
settings.cpus.clone(),
settings.version,
relevant_limits.cpu.limits,
),
),
CpuLimitType::Unknown => {
Box::new(crate::settings::unknown::Cpus::from_json_and_limits(
settings.cpus.clone(),
settings.version,
relevant_limits.cpu.limits,
))
}
};
let gpu_driver: Box<dyn TGpu> = match relevant_limits.gpu.provider {
GpuLimitType::SteamDeck => {
Box::new(crate::settings::steam_deck::Gpu::from_json_and_limits(
settings.gpu.clone(),
settings.version,
relevant_limits.gpu.limits,
))
}
GpuLimitType::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Gpu::from_json_and_limits(
settings.gpu.clone(),
settings.version,
relevant_limits.gpu.limits,
))
}
GpuLimitType::Generic => {
Box::new(crate::settings::generic::Gpu::from_json_and_limits(
settings.gpu.clone(),
settings.version,
relevant_limits.gpu.limits,
))
}
GpuLimitType::GenericAMD => Box::new(
crate::settings::generic_amd::Gpu::from_json_and_limits(
settings.gpu.clone(),
settings.version,
relevant_limits.gpu.limits,
),
),
GpuLimitType::Unknown => {
Box::new(crate::settings::unknown::Gpu::from_json_and_limits(
settings.gpu.clone(),
settings.version,
relevant_limits.gpu.limits,
))
}
};
let battery_driver: Box<dyn TBattery> = match relevant_limits.battery.provider {
BatteryLimitType::SteamDeck => {
Box::new(crate::settings::steam_deck::Battery::from_json_and_limits(
settings.battery.clone(),
settings.version,
relevant_limits.battery.limits,
))
}
BatteryLimitType::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Battery::from_json_and_limits(
settings.battery.clone(),
settings.version,
relevant_limits.battery.limits,
))
}
BatteryLimitType::Generic => Box::new(
crate::settings::generic::Battery::from_json_and_limits(
settings.battery.clone(),
settings.version,
relevant_limits.battery.limits,
),
),
BatteryLimitType::Unknown => {
Box::new(crate::settings::unknown::Battery::from_json_and_limits(
settings.battery.clone(),
settings.version,
relevant_limits.battery.limits,
))
}
};
return Driver {
general: general_driver,
cpus: cpu_driver,
gpu: gpu_driver,
battery: battery_driver,
};
} else {
for limit in conf.limits {
match limit {
Limits::Cpu(cpus) => {
let cpu_driver: Box<dyn TCpus> = match cpus {
CpuLimit::SteamDeck => {
Box::new(crate::settings::steam_deck::Cpus::system_default())
}
CpuLimit::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Cpus::system_default())
}
CpuLimit::Generic(x) => {
Box::new(crate::settings::generic::Cpus::<
crate::settings::generic::Cpu,
>::from_limits(x))
}
CpuLimit::GenericAMD(x) => {
Box::new(crate::settings::generic_amd::Cpus::from_limits(x))
}
CpuLimit::Unknown => {
Box::new(crate::settings::unknown::Cpus::system_default())
}
};
builder.cpus = Some(cpu_driver);
}
Limits::Gpu(gpu) => {
let driver: Box<dyn TGpu> = match gpu {
GpuLimit::SteamDeck => {
Box::new(crate::settings::steam_deck::Gpu::system_default())
}
GpuLimit::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Gpu::system_default())
}
GpuLimit::Generic(x) => {
Box::new(crate::settings::generic::Gpu::from_limits(x))
}
GpuLimit::GenericAMD(x) => {
Box::new(crate::settings::generic_amd::Gpu::from_limits(x))
}
GpuLimit::Unknown => {
Box::new(crate::settings::unknown::Gpu::system_default())
}
};
builder.gpu = Some(driver);
}
Limits::Battery(batt) => {
let driver: Box<dyn TBattery> = match batt {
BatteryLimit::SteamDeck => {
Box::new(crate::settings::steam_deck::Battery::system_default())
}
BatteryLimit::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Battery::system_default())
}
BatteryLimit::Generic(x) => {
Box::new(crate::settings::generic::Battery::from_limits(x))
}
BatteryLimit::Unknown => {
Box::new(crate::settings::unknown::Battery)
}
};
builder.battery = Some(driver);
}
let cpu_driver: Box<dyn TCpus> = match relevant_limits.cpu.provider {
CpuLimitType::SteamDeck => {
Box::new(crate::settings::steam_deck::Cpus::from_limits(relevant_limits.cpu.limits))
}
}
CpuLimitType::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Cpus::from_limits(relevant_limits.cpu.limits))
}
CpuLimitType::Generic => {
Box::new(crate::settings::generic::Cpus::<
crate::settings::generic::Cpu,
>::from_limits(relevant_limits.cpu.limits))
}
CpuLimitType::GenericAMD => {
Box::new(crate::settings::generic_amd::Cpus::from_limits(relevant_limits.cpu.limits))
}
CpuLimitType::Unknown => {
Box::new(crate::settings::unknown::Cpus::from_limits(relevant_limits.cpu.limits))
}
};
let gpu_driver: Box<dyn TGpu> = match relevant_limits.gpu.provider {
GpuLimitType::SteamDeck => {
Box::new(crate::settings::steam_deck::Gpu::from_limits(relevant_limits.gpu.limits))
}
GpuLimitType::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Gpu::from_limits(relevant_limits.gpu.limits))
}
GpuLimitType::Generic => {
Box::new(crate::settings::generic::Gpu::from_limits(relevant_limits.gpu.limits))
}
GpuLimitType::GenericAMD => {
Box::new(crate::settings::generic_amd::Gpu::from_limits(relevant_limits.gpu.limits))
}
GpuLimitType::Unknown => {
Box::new(crate::settings::unknown::Gpu::from_limits(relevant_limits.gpu.limits))
}
};
let battery_driver: Box<dyn TBattery> = match relevant_limits.battery.provider {
BatteryLimitType::SteamDeck => {
Box::new(crate::settings::steam_deck::Battery::from_limits(relevant_limits.battery.limits))
}
BatteryLimitType::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Battery::from_limits(relevant_limits.battery.limits))
}
BatteryLimitType::Generic => {
Box::new(crate::settings::generic::Battery::from_limits(relevant_limits.battery.limits))
}
BatteryLimitType::Unknown => {
Box::new(crate::settings::unknown::Battery::from_limits(relevant_limits.battery.limits))
}
};
return Driver {
general: general_driver,
cpus: cpu_driver,
gpu: gpu_driver,
battery: battery_driver,
};
}
}
}
builder.build()
}
struct DriverBuilder {
general: Box<dyn TGeneral>,
cpus: Option<Box<dyn TCpus>>,
gpu: Option<Box<dyn TGpu>>,
battery: Option<Box<dyn TBattery>>,
}
impl DriverBuilder {
fn new(json_path: std::path::PathBuf, profile_name: String) -> Self {
Self {
general: Box::new(General {
persistent: false,
path: json_path,
name: profile_name,
driver: DriverJson::AutoDetect,
events: Default::default(),
}),
cpus: None,
gpu: None,
battery: None,
}
}
fn is_complete(&self) -> bool {
self.cpus.is_some() && self.gpu.is_some() && self.battery.is_some()
}
fn build(self) -> Driver {
Driver {
general: self.general,
cpus: self
.cpus
.unwrap_or_else(|| Box::new(crate::settings::unknown::Cpus::system_default())),
gpu: self
.gpu
.unwrap_or_else(|| Box::new(crate::settings::unknown::Gpu::system_default())),
battery: self
.battery
.unwrap_or_else(|| Box::new(crate::settings::unknown::Battery)),
}
Driver {
general: general_driver,
cpus: Box::new(crate::settings::unknown::Cpus::system_default()),
gpu: Box::new(crate::settings::unknown::Gpu::system_default()),
battery: Box::new(crate::settings::unknown::Battery),
}
}

View file

@ -2,7 +2,7 @@ use std::thread::{self, JoinHandle};
#[cfg(feature = "online")]
use std::time::Duration;
use limits_core::json::Base;
use limits_core::json_v2::Base;
#[cfg(feature = "online")]
pub fn spawn() -> JoinHandle<()> {

View file

@ -4,6 +4,10 @@ pub fn limits_path() -> std::path::PathBuf {
crate::utility::settings_dir().join(crate::consts::LIMITS_FILE)
}
pub fn limits_override_path() -> std::path::PathBuf {
crate::utility::settings_dir().join(crate::consts::LIMITS_OVERRIDE_FILE)
}
// NOTE: eats errors
pub fn get_dev_messages() -> Vec<DeveloperMessage> {
let limits_path = limits_path();

View file

@ -1,4 +1,4 @@
use super::{auto_detect0, General, SettingError, TBattery, TCpus, TGeneral, TGpu};
use super::{auto_detect0, TBattery, TCpus, TGeneral, TGpu};
use crate::persist::{DriverJson, SettingsJson};
pub struct Driver {
@ -12,96 +12,9 @@ impl Driver {
pub fn init(
settings: SettingsJson,
json_path: std::path::PathBuf,
) -> Result<Self, SettingError> {
Ok(match settings.version {
0 => Self::version0(settings, json_path)?,
_ => Self {
general: Box::new(General {
persistent: settings.persistent,
path: json_path,
name: settings.name,
driver: DriverJson::SteamDeck,
events: settings.events.unwrap_or_default(),
}),
cpus: Box::new(super::steam_deck::Cpus::from_json(
settings.cpus,
settings.version,
)),
gpu: Box::new(super::steam_deck::Gpu::from_json(
settings.gpu,
settings.version,
)),
battery: Box::new(super::steam_deck::Battery::from_json(
settings.battery,
settings.version,
)),
},
})
}
fn version0(
settings: SettingsJson,
json_path: std::path::PathBuf,
) -> Result<Self, SettingError> {
let name = settings.name.clone();
if let Some(provider) = &settings.provider {
match provider {
DriverJson::SteamDeck => Ok(Self {
general: Box::new(General {
persistent: settings.persistent,
path: json_path,
name: settings.name,
driver: DriverJson::SteamDeck,
events: settings.events.unwrap_or_default(),
}),
cpus: Box::new(super::steam_deck::Cpus::from_json(
settings.cpus,
settings.version,
)),
gpu: Box::new(super::steam_deck::Gpu::from_json(
settings.gpu,
settings.version,
)),
battery: Box::new(super::steam_deck::Battery::from_json(
settings.battery,
settings.version,
)),
}),
// There's nothing special about SteamDeckAdvance, it just appears different
DriverJson::SteamDeckAdvance => Ok(Self {
general: Box::new(General {
persistent: settings.persistent,
path: json_path,
name: settings.name,
driver: DriverJson::SteamDeckAdvance,
events: settings.events.unwrap_or_default(),
}),
cpus: Box::new(super::steam_deck::Cpus::from_json(
settings.cpus,
settings.version,
)),
gpu: Box::new(super::steam_deck::Gpu::from_json(
settings.gpu,
settings.version,
)),
battery: Box::new(super::steam_deck::Battery::from_json(
settings.battery,
settings.version,
)),
}),
DriverJson::Generic | DriverJson::GenericAMD => {
Ok(super::detect::auto_detect0(Some(settings), json_path, name))
}
DriverJson::Unknown => {
Ok(super::detect::auto_detect0(Some(settings), json_path, name))
}
DriverJson::AutoDetect => {
Ok(super::detect::auto_detect0(Some(settings), json_path, name))
}
}
} else {
Ok(super::detect::auto_detect0(Some(settings), json_path, name))
}
) -> Self {
let name_bup = settings.name.clone();
auto_detect0(Some(settings), json_path, name_bup)
}
pub fn system_default(json_path: std::path::PathBuf, name: String) -> Self {

View file

@ -156,27 +156,19 @@ impl OnSet for Settings {
impl Settings {
#[inline]
pub fn from_json(other: SettingsJson, json_path: PathBuf) -> Self {
let name_bup = other.name.clone();
match super::Driver::init(other, json_path.clone()) {
Ok(x) => {
log::info!(
"Loaded settings with drivers general:{:?},cpus:{:?},gpu:{:?},battery:{:?}",
x.general.provider(),
x.cpus.provider(),
x.gpu.provider(),
x.battery.provider()
);
Self {
general: x.general,
cpus: x.cpus,
gpu: x.gpu,
battery: x.battery,
}
}
Err(e) => {
log::error!("Driver init error: {}", e);
Self::system_default(json_path, name_bup)
}
let x = super::Driver::init(other, json_path.clone());
log::info!(
"Loaded settings with drivers general:{:?},cpus:{:?},gpu:{:?},battery:{:?}",
x.general.provider(),
x.cpus.provider(),
x.gpu.provider(),
x.battery.provider()
);
Self {
general: x.general,
cpus: x.cpus,
gpu: x.gpu,
battery: x.battery,
}
}
@ -219,22 +211,12 @@ impl Settings {
*self.general.persistent() = false;
self.general.name(name);
} else {
match super::Driver::init(settings_json, json_path.clone()) {
Ok(x) => {
log::info!("Loaded settings with drivers general:{:?},cpus:{:?},gpu:{:?},battery:{:?}", x.general.provider(), x.cpus.provider(), x.gpu.provider(), x.battery.provider());
self.general = x.general;
self.cpus = x.cpus;
self.gpu = x.gpu;
self.battery = x.battery;
}
Err(e) => {
log::error!("Driver init error: {}", e);
self.general.name(name);
*self.general.persistent() = false;
self.general.path(json_path);
return Err(e);
}
};
let x = super::Driver::init(settings_json, json_path.clone());
log::info!("Loaded settings with drivers general:{:?},cpus:{:?},gpu:{:?},battery:{:?}", x.general.provider(), x.cpus.provider(), x.gpu.provider(), x.battery.provider());
self.general = x.general;
self.cpus = x.cpus;
self.gpu = x.gpu;
self.battery = x.battery;
}
} else {
if system_defaults {

View file

@ -1,10 +1,10 @@
use std::convert::Into;
use limits_core::json::GenericBatteryLimit;
use limits_core::json_v2::GenericBatteryLimit;
use sysfuss::SysEntity;
use crate::persist::BatteryJson;
use crate::settings::TBattery;
use crate::settings::{TBattery, ProviderBuilder};
use crate::settings::{OnResume, OnSet, SettingError};
#[derive(Debug, Clone)]
@ -56,24 +56,26 @@ impl Battery {
}
}
}
}
pub fn from_limits(limits: limits_core::json::GenericBatteryLimit) -> Self {
// TODO
Self {
limits,
sysfs: Self::find_psu_sysfs(None::<&'static str>),
}
}
pub fn from_json_and_limits(
other: BatteryJson,
impl ProviderBuilder<BatteryJson, GenericBatteryLimit> for Battery {
fn from_json_and_limits(
persistent: BatteryJson,
_version: u64,
limits: limits_core::json::GenericBatteryLimit,
limits: GenericBatteryLimit,
) -> Self {
// TODO
Self {
limits,
sysfs: Self::find_psu_sysfs(other.root)
sysfs: Self::find_psu_sysfs(persistent.root)
}
}
fn from_limits(limits: GenericBatteryLimit) -> Self {
// TODO
Self {
limits,
sysfs: Self::find_psu_sysfs(None::<&'static str>),
}
}
}

View file

@ -1,13 +1,13 @@
use std::convert::{AsMut, AsRef, Into};
use limits_core::json::GenericCpuLimit;
use limits_core::json_v2::{GenericCpusLimit, GenericCpuLimit};
use super::FromGenericCpuInfo;
use crate::api::RangeLimit;
use crate::persist::CpuJson;
use crate::settings::{min_max_from_json, MinMax};
use crate::settings::{OnResume, OnSet, SettingError};
use crate::settings::{TCpu, TCpus};
use crate::settings::{TCpu, TCpus, ProviderBuilder};
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
@ -87,31 +87,15 @@ impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + FromGenericCpuInfo> Cpus<C> {
Err(_) => (false, false),
}
}
}
pub fn from_limits(limits: limits_core::json::GenericCpuLimit) -> Self {
let cpu_count = Self::cpu_count().unwrap_or(8);
let (_, can_smt) = Self::system_smt_capabilities();
let mut new_cpus = Vec::with_capacity(cpu_count);
for i in 0..cpu_count {
let new_cpu = C::from_limits(i, limits.clone());
new_cpus.push(new_cpu);
}
Self {
cpus: new_cpus,
smt: true,
smt_capable: can_smt,
}
}
pub fn from_json_and_limits(
mut other: Vec<CpuJson>,
version: u64,
limits: limits_core::json::GenericCpuLimit,
) -> Self {
impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + FromGenericCpuInfo> ProviderBuilder<Vec<CpuJson>, GenericCpusLimit> for Cpus<C> {
fn from_json_and_limits(mut other: Vec<CpuJson>, version: u64, limits: GenericCpusLimit) -> Self {
let (_, can_smt) = Self::system_smt_capabilities();
let mut result = Vec::with_capacity(other.len());
let max_cpus = Self::cpu_count();
let smt_guess = crate::settings::util::guess_smt(&other) && can_smt;
let fallback_cpu_limit = GenericCpuLimit::default();
for (i, cpu) in other.drain(..).enumerate() {
// prevent having more CPUs than available
if let Some(max_cpus) = max_cpus {
@ -119,7 +103,10 @@ impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + FromGenericCpuInfo> Cpus<C> {
break;
}
}
let new_cpu = C::from_json_and_limits(cpu, version, i, limits.clone());
let cpu_limit = limits.cpus.get(i)
.or_else(|| limits.cpus.get(0))
.unwrap_or_else(|| &fallback_cpu_limit).clone();
let new_cpu = C::from_json_and_limits(cpu, version, i, cpu_limit);
result.push(new_cpu);
}
if let Some(max_cpus) = max_cpus {
@ -136,6 +123,25 @@ impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + FromGenericCpuInfo> Cpus<C> {
smt_capable: can_smt,
}
}
fn from_limits(limits: GenericCpusLimit) -> Self {
let cpu_count = Self::cpu_count().unwrap_or(8);
let (_, can_smt) = Self::system_smt_capabilities();
let mut new_cpus = Vec::with_capacity(cpu_count);
let fallback_cpu_limit = GenericCpuLimit::default();
for i in 0..cpu_count {
let cpu_limit = limits.cpus.get(i)
.or_else(|| limits.cpus.get(0))
.unwrap_or_else(|| &fallback_cpu_limit).clone();
let new_cpu = C::from_limits(i, cpu_limit);
new_cpus.push(new_cpu);
}
Self {
cpus: new_cpus,
smt: true,
smt_capable: can_smt,
}
}
}
impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + crate::settings::OnPowerEvent>
@ -345,7 +351,7 @@ impl Cpu {
.clock_max
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(5_000))),
clock_step: self.limits.clock_step,
clock_step: self.limits.clock_step.unwrap_or(100),
governors: self.governors(),
}
}

View file

@ -1,11 +1,11 @@
use std::convert::Into;
use limits_core::json::GenericGpuLimit;
use limits_core::json_v2::GenericGpuLimit;
use sysfuss::{BasicEntityPath, SysEntity};
use crate::api::RangeLimit;
use crate::persist::GpuJson;
use crate::settings::TGpu;
use crate::settings::{TGpu, ProviderBuilder};
use crate::settings::{min_max_from_json, MinMax};
use crate::settings::{OnResume, OnSet, SettingError};
@ -49,8 +49,34 @@ impl Gpu {
}
}
}
}
pub fn from_limits(limits: limits_core::json::GenericGpuLimit) -> Self {
impl ProviderBuilder<GpuJson, GenericGpuLimit> for Gpu {
fn from_json_and_limits(persistent: GpuJson, version: u64, limits: GenericGpuLimit) -> Self {
let clock_lims = if limits.clock_min.is_some() && limits.clock_max.is_some() {
persistent.clock_limits.map(|x| min_max_from_json(x, version))
} else {
None
};
Self {
slow_memory: false,
fast_ppt: if limits.fast_ppt.is_some() {
persistent.fast_ppt
} else {
None
},
slow_ppt: if limits.slow_ppt.is_some() {
persistent.slow_ppt
} else {
None
},
clock_limits: clock_lims,
limits,
sysfs: Self::find_card_sysfs(persistent.root)
}
}
fn from_limits(limits: GenericGpuLimit) -> Self {
Self {
slow_memory: false,
fast_ppt: None,
@ -60,34 +86,6 @@ impl Gpu {
sysfs: Self::find_card_sysfs(None::<&'static str>),
}
}
pub fn from_json_and_limits(
other: GpuJson,
version: u64,
limits: limits_core::json::GenericGpuLimit,
) -> Self {
let clock_lims = if limits.clock_min.is_some() && limits.clock_max.is_some() {
other.clock_limits.map(|x| min_max_from_json(x, version))
} else {
None
};
Self {
slow_memory: false,
fast_ppt: if limits.fast_ppt.is_some() {
other.fast_ppt
} else {
None
},
slow_ppt: if limits.slow_ppt.is_some() {
other.slow_ppt
} else {
None
},
clock_limits: clock_lims,
limits,
sysfs: Self::find_card_sysfs(other.root)
}
}
}
impl Into<GpuJson> for Gpu {

View file

@ -7,3 +7,11 @@ pub use battery::Battery;
pub use cpu::{Cpu, Cpus};
pub use gpu::Gpu;
pub use traits::FromGenericCpuInfo;
fn _impl_checker() {
fn impl_provider_builder<T: crate::settings::ProviderBuilder<J, L>, J, L>() {}
impl_provider_builder::<Battery, crate::persist::BatteryJson, limits_core::json_v2::GenericBatteryLimit>();
impl_provider_builder::<Cpus<Cpu>, Vec<crate::persist::CpuJson>, limits_core::json_v2::GenericCpusLimit>();
impl_provider_builder::<Gpu, crate::persist::GpuJson, limits_core::json_v2::GenericGpuLimit>();
}

View file

@ -1,6 +1,7 @@
use crate::persist::CpuJson;
use limits_core::json::GenericCpuLimit;
use limits_core::json_v2::GenericCpuLimit;
// similar to crate::settings::ProviderBuilder<CpuJson, GenericCpuLimit>
pub trait FromGenericCpuInfo {
fn from_limits(cpu_index: usize, limits: GenericCpuLimit) -> Self;

View file

@ -2,24 +2,24 @@ use crate::persist::CpuJson;
use crate::settings::generic::{Cpu as GenericCpu, Cpus as GenericCpus, FromGenericCpuInfo};
use crate::settings::MinMax;
use crate::settings::{OnResume, OnSet, SettingError};
use crate::settings::{TCpu, TCpus};
use crate::settings::{TCpu, TCpus, ProviderBuilder};
#[derive(Debug)]
pub struct Cpus {
generic: GenericCpus<Cpu>,
}
impl Cpus {
pub fn from_limits(limits: limits_core::json::GenericCpuLimit) -> Self {
impl ProviderBuilder<Vec<CpuJson>, limits_core::json_v2::GenericCpusLimit> for Cpus {
fn from_limits(limits: limits_core::json_v2::GenericCpusLimit) -> Self {
Self {
generic: GenericCpus::from_limits(limits),
}
}
pub fn from_json_and_limits(
fn from_json_and_limits(
other: Vec<CpuJson>,
version: u64,
limits: limits_core::json::GenericCpuLimit,
limits: limits_core::json_v2::GenericCpusLimit,
) -> Self {
Self {
generic: GenericCpus::from_json_and_limits(other, version, limits),
@ -75,7 +75,7 @@ pub struct Cpu {
}
impl FromGenericCpuInfo for Cpu {
fn from_limits(cpu_index: usize, limits: limits_core::json::GenericCpuLimit) -> Self {
fn from_limits(cpu_index: usize, limits: limits_core::json_v2::GenericCpuLimit) -> Self {
let gen = GenericCpu::from_limits(cpu_index, limits.clone());
Self { generic: gen }
}
@ -84,7 +84,7 @@ impl FromGenericCpuInfo for Cpu {
other: CpuJson,
version: u64,
cpu_index: usize,
limits: limits_core::json::GenericCpuLimit,
limits: limits_core::json_v2::GenericCpuLimit,
) -> Self {
let gen = GenericCpu::from_json_and_limits(other, version, cpu_index, limits);
Self { generic: gen }

View file

@ -4,7 +4,7 @@ use std::sync::Mutex;
use crate::persist::GpuJson;
use crate::settings::generic::Gpu as GenericGpu;
use crate::settings::MinMax;
use crate::settings::TGpu;
use crate::settings::{TGpu, ProviderBuilder};
use crate::settings::{OnResume, OnSet, SettingError, SettingVariant};
fn ryzen_adj_or_log() -> Option<Mutex<RyzenAdj>> {
@ -35,8 +35,8 @@ impl std::fmt::Debug for Gpu {
}
}
impl Gpu {
pub fn from_limits(limits: limits_core::json::GenericGpuLimit) -> Self {
impl ProviderBuilder<GpuJson, limits_core::json_v2::GenericGpuLimit> for Gpu {
fn from_limits(limits: limits_core::json_v2::GenericGpuLimit) -> Self {
Self {
generic: GenericGpu::from_limits(limits),
implementor: ryzen_adj_or_log(),
@ -44,10 +44,10 @@ impl Gpu {
}
}
pub fn from_json_and_limits(
fn from_json_and_limits(
other: GpuJson,
version: u64,
limits: limits_core::json::GenericGpuLimit,
limits: limits_core::json_v2::GenericGpuLimit,
) -> Self {
Self {
generic: GenericGpu::from_json_and_limits(other, version, limits),
@ -55,6 +55,9 @@ impl Gpu {
state: Default::default(),
}
}
}
impl Gpu {
fn set_all(&mut self) -> Result<(), Vec<SettingError>> {
let mutex = match &self.implementor {

View file

@ -3,3 +3,10 @@ mod gpu;
pub use cpu::{Cpu, Cpus};
pub use gpu::Gpu;
fn _impl_checker() {
fn impl_provider_builder<T: crate::settings::ProviderBuilder<J, L>, J, L>() {}
impl_provider_builder::<Cpus, Vec<crate::persist::CpuJson>, limits_core::json_v2::GenericCpusLimit>();
impl_provider_builder::<Gpu, crate::persist::GpuJson, limits_core::json_v2::GenericGpuLimit>();
}

View file

@ -17,7 +17,7 @@ pub use general::{General, SettingVariant, Settings};
pub use min_max::{min_max_from_json, MinMax};
pub use error::SettingError;
pub use traits::{OnPowerEvent, OnResume, OnSet, PowerMode, TBattery, TCpu, TCpus, TGeneral, TGpu};
pub use traits::{OnPowerEvent, OnResume, OnSet, PowerMode, TBattery, TCpu, TCpus, TGeneral, TGpu, ProviderBuilder};
#[cfg(test)]
mod tests {

View file

@ -4,11 +4,12 @@ use std::sync::Arc;
use sysfuss::{PowerSupplyAttribute, PowerSupplyPath, HwMonAttribute, HwMonAttributeItem, HwMonAttributeType, HwMonPath, SysEntity, SysEntityAttributesExt, SysAttribute};
use sysfuss::capability::attributes;
use super::oc_limits::{BatteryLimits, OverclockLimits};
use limits_core::json_v2::GenericBatteryLimit;
use super::util::ChargeMode;
use crate::api::RangeLimit;
use crate::persist::{BatteryEventJson, BatteryJson};
use crate::settings::TBattery;
use crate::settings::{TBattery, ProviderBuilder};
use crate::settings::{OnPowerEvent, OnResume, OnSet, PowerMode, SettingError};
#[derive(Debug, Clone)]
@ -16,9 +17,8 @@ pub struct Battery {
pub charge_rate: Option<u64>,
pub charge_mode: Option<ChargeMode>,
events: Vec<EventInstruction>,
limits: BatteryLimits,
limits: GenericBatteryLimit,
state: crate::state::steam_deck::Battery,
driver_mode: crate::persist::DriverJson,
sysfs_bat: PowerSupplyPath,
sysfs_hwmon: Arc<HwMonPath>,
}
@ -217,55 +217,10 @@ const MAXIMUM_BATTERY_CHARGE_RATE_ATTR: HwMonAttribute = HwMonAttribute::custom(
const MAX_BATTERY_CHARGE_RATE_ATTR: HwMonAttribute = HwMonAttribute::custom("maximum_battery_charge_rate");
const MAX_BATTERY_CHARGE_LEVEL_ATTR: HwMonAttribute = HwMonAttribute::custom("max_battery_charge_level");
impl Battery {
#[inline]
pub fn from_json(other: BatteryJson, version: u64) -> Self {
let (oc_limits, is_default) = OverclockLimits::load_or_default();
let oc_limits = oc_limits.battery;
let driver = if is_default {
crate::persist::DriverJson::SteamDeck
} else {
crate::persist::DriverJson::SteamDeckAdvance
};
let hwmon_sys = Arc::new(Self::find_hwmon_sysfs(None::<&'static str>));
match version {
0 => Self {
charge_rate: other.charge_rate,
charge_mode: other
.charge_mode
.map(|x| Self::str_to_charge_mode(&x))
.flatten(),
events: other
.events
.into_iter()
.map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone()))
.collect(),
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(),
driver_mode: driver,
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: hwmon_sys,
},
_ => Self {
charge_rate: other.charge_rate,
charge_mode: other
.charge_mode
.map(|x| Self::str_to_charge_mode(&x))
.flatten(),
events: other
.events
.into_iter()
.map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone()))
.collect(),
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(),
driver_mode: driver,
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: hwmon_sys,
},
}
}
const MAX_CHARGE_RATE: u64 = 2500;
const MIN_CHARGE_RATE: u64 = 250;
impl Battery {
fn find_battery_sysfs(root: Option<impl AsRef<std::path::Path>>) -> PowerSupplyPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.power_supply(attributes(BATTERY_NEEDS.into_iter().copied())) {
@ -360,7 +315,7 @@ impl Battery {
MAXIMUM_BATTERY_CHARGE_RATE_ATTR
};
let path = attr.path(&*self.sysfs_hwmon);
self.sysfs_hwmon.set(attr, self.limits.charge_rate.max,).map_err(
self.sysfs_hwmon.set(attr, self.limits.charge_rate.and_then(|lim| lim.max).unwrap_or(2500)).map_err(
|e| SettingError {
msg: format!("Failed to write to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::Battery,
@ -407,7 +362,7 @@ impl Battery {
fn clamp_all(&mut self) {
if let Some(charge_rate) = &mut self.charge_rate {
*charge_rate =
(*charge_rate).clamp(self.limits.charge_rate.min, self.limits.charge_rate.max);
(*charge_rate).clamp(self.limits.charge_rate.and_then(|lim| lim.min).unwrap_or(MIN_CHARGE_RATE), self.limits.charge_rate.and_then(|lim| lim.max).unwrap_or(MAX_CHARGE_RATE));
}
}
@ -489,26 +444,6 @@ impl Battery {
}
}
pub fn system_default() -> Self {
let (oc_limits, is_default) = OverclockLimits::load_or_default();
let oc_limits = oc_limits.battery;
let driver = if is_default {
crate::persist::DriverJson::SteamDeck
} else {
crate::persist::DriverJson::SteamDeckAdvance
};
Self {
charge_rate: None,
charge_mode: None,
events: Vec::new(),
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(),
driver_mode: driver,
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: Arc::new(Self::find_hwmon_sysfs(None::<&'static str>)),
}
}
fn find_limit_event(&self) -> Option<usize> {
for (i, event) in self.events.iter().enumerate() {
match event.trigger {
@ -550,6 +485,58 @@ impl Into<BatteryJson> for Battery {
}
}
impl ProviderBuilder<BatteryJson, GenericBatteryLimit> for Battery {
fn from_json_and_limits(persistent: BatteryJson, version: u64, limits: GenericBatteryLimit) -> Self {
let hwmon_sys = Arc::new(Self::find_hwmon_sysfs(None::<&'static str>));
match version {
0 => Self {
charge_rate: persistent.charge_rate,
charge_mode: persistent
.charge_mode
.map(|x| Self::str_to_charge_mode(&x))
.flatten(),
events: persistent
.events
.into_iter()
.map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone()))
.collect(),
limits: limits,
state: crate::state::steam_deck::Battery::default(),
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: hwmon_sys,
},
_ => Self {
charge_rate: persistent.charge_rate,
charge_mode: persistent
.charge_mode
.map(|x| Self::str_to_charge_mode(&x))
.flatten(),
events: persistent
.events
.into_iter()
.map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone()))
.collect(),
limits: limits,
state: crate::state::steam_deck::Battery::default(),
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: hwmon_sys,
},
}
}
fn from_limits(limits: GenericBatteryLimit) -> Self {
Self {
charge_rate: None,
charge_mode: None,
events: Vec::new(),
limits: limits,
state: crate::state::steam_deck::Battery::default(),
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: Arc::new(Self::find_hwmon_sysfs(None::<&'static str>)),
}
}
}
impl OnSet for Battery {
fn on_set(&mut self) -> Result<(), Vec<SettingError>> {
self.clamp_all();
@ -631,8 +618,8 @@ impl TBattery for Battery {
fn limits(&self) -> crate::api::BatteryLimits {
crate::api::BatteryLimits {
charge_current: Some(RangeLimit {
min: self.limits.charge_rate.min,
max: self.limits.charge_rate.max,
min: self.limits.charge_rate.and_then(|lim| lim.min).unwrap_or(MIN_CHARGE_RATE),
max: self.limits.charge_rate.and_then(|lim| lim.max).unwrap_or(MAX_CHARGE_RATE),
}),
charge_current_step: 50,
charge_modes: vec![
@ -844,6 +831,6 @@ impl TBattery for Battery {
}
fn provider(&self) -> crate::persist::DriverJson {
self.driver_mode.clone()
crate::persist::DriverJson::SteamDeck
}
}

View file

@ -2,13 +2,15 @@ use std::convert::Into;
use sysfuss::{BasicEntityPath, SysEntity, SysEntityAttributesExt};
use super::oc_limits::{CpuLimits, CpusLimits, OverclockLimits};
use limits_core::json_v2::{GenericCpusLimit, GenericCpuLimit};
use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT;
use super::util::{range_max_or_fallback, range_min_or_fallback};
use crate::api::RangeLimit;
use crate::persist::CpuJson;
use crate::settings::{min_max_from_json, MinMax};
use crate::settings::{OnResume, OnSet, SettingError};
use crate::settings::{TCpu, TCpus};
use crate::settings::{TCpu, TCpus, ProviderBuilder};
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
@ -17,13 +19,17 @@ const CARD_EXTENSIONS: &[&'static str] = &[
super::DPM_FORCE_LIMITS_ATTRIBUTE
];
const MAX_CLOCK: u64 = 3500;
const MIN_MAX_CLOCK: u64 = 200; // minimum value allowed for maximum CPU clock, MHz
const MIN_MIN_CLOCK: u64 = 1400; // minimum value allowed for minimum CPU clock, MHz
const CLOCK_STEP: u64 = 100;
#[derive(Debug, Clone)]
pub struct Cpus {
pub cpus: Vec<Cpu>,
pub smt: bool,
pub smt_capable: bool,
pub(super) limits: CpusLimits,
driver_mode: crate::persist::DriverJson,
pub(super) limits: GenericCpusLimit,
}
impl OnSet for Cpus {
@ -94,85 +100,42 @@ impl Cpus {
Err(_) => (false, false),
}
}
}
pub fn system_default() -> Self {
impl ProviderBuilder<Vec<CpuJson>, GenericCpusLimit> for Cpus {
fn from_json_and_limits(mut persistent: Vec<CpuJson>, version: u64, limits: GenericCpusLimit) -> Self {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.reset();
let (oc_limits, is_default) = OverclockLimits::load_or_default();
let oc_limits = oc_limits.cpus;
let driver = if is_default {
crate::persist::DriverJson::SteamDeck
} else {
crate::persist::DriverJson::SteamDeckAdvance
};
if let Some(max_cpu) = Self::cpu_count() {
let mut sys_cpus = Vec::with_capacity(max_cpu);
for i in 0..max_cpu {
sys_cpus.push(Cpu::system_default(
i,
oc_limits
.cpus
.get(i)
.map(|x| x.to_owned())
.unwrap_or_default(),
));
}
let (_, can_smt) = Self::system_smt_capabilities();
Self {
cpus: sys_cpus,
smt: true,
smt_capable: can_smt,
limits: oc_limits,
driver_mode: driver,
}
} else {
Self {
cpus: vec![],
smt: false,
smt_capable: false,
limits: oc_limits,
driver_mode: driver,
}
}
}
#[inline]
pub fn from_json(mut other: Vec<CpuJson>, version: u64) -> Self {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.reset();
let (oc_limits, is_default) = OverclockLimits::load_or_default();
let oc_limits = oc_limits.cpus;
let driver = if is_default {
crate::persist::DriverJson::SteamDeck
} else {
crate::persist::DriverJson::SteamDeckAdvance
};
let (_, can_smt) = Self::system_smt_capabilities();
let mut result = Vec::with_capacity(other.len());
let mut result = Vec::with_capacity(persistent.len());
let max_cpus = Self::cpu_count();
let smt_guess = crate::settings::util::guess_smt(&other) && can_smt;
for (i, cpu) in other.drain(..).enumerate() {
let smt_guess = crate::settings::util::guess_smt(&persistent) && can_smt;
for (i, cpu) in persistent.drain(..).enumerate() {
// prevent having more CPUs than available
if let Some(max_cpus) = max_cpus {
if i == max_cpus {
break;
}
}
let new_cpu = Cpu::from_json(
cpu,
version,
i,
oc_limits
.cpus
.get(i)
.map(|x| x.to_owned())
.unwrap_or_default(),
);
let new_cpu = if let Some(cpu_limit) = limits.cpus.get(i) {
Cpu::from_json_and_limits(
cpu,
version,
i,
cpu_limit.to_owned()
)
} else {
Cpu::from_json(
cpu,
version,
i,
)
};
result.push(new_cpu);
}
if let Some(max_cpus) = max_cpus {
if result.len() != max_cpus {
let mut sys_cpus = Cpus::system_default();
for i in result.len()..sys_cpus.cpus.len() {
result.push(sys_cpus.cpus.remove(i));
for i in result.len()..max_cpus {
result.push(Cpu::system_default(i));
}
}
}
@ -180,8 +143,39 @@ impl Cpus {
cpus: result,
smt: smt_guess,
smt_capable: can_smt,
limits: oc_limits,
driver_mode: driver,
limits: limits,
}
}
fn from_limits(limits: GenericCpusLimit) -> Self {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.reset();
if let Some(max_cpu) = Self::cpu_count() {
let mut sys_cpus = Vec::with_capacity(max_cpu);
for i in 0..max_cpu {
let new_cpu = if let Some(cpu_limit) = limits.cpus.get(i) {
Cpu::from_limits(
i,
cpu_limit.to_owned()
)
} else {
Cpu::system_default(i)
};
sys_cpus.push(new_cpu);
}
let (_, can_smt) = Self::system_smt_capabilities();
Self {
cpus: sys_cpus,
smt: true,
smt_capable: can_smt,
limits: limits,
}
} else {
Self {
cpus: vec![],
smt: false,
smt_capable: false,
limits: limits,
}
}
}
}
@ -224,7 +218,7 @@ impl TCpus for Cpus {
}
fn provider(&self) -> crate::persist::DriverJson {
self.driver_mode.clone()
crate::persist::DriverJson::SteamDeck
}
}
@ -233,7 +227,7 @@ pub struct Cpu {
pub online: bool,
pub clock_limits: Option<MinMax<u64>>,
pub governor: String,
limits: CpuLimits,
limits: GenericCpuLimit,
index: usize,
state: crate::state::steam_deck::Cpu,
sysfs: BasicEntityPath,
@ -249,7 +243,7 @@ enum ClockType {
impl Cpu {
#[inline]
fn from_json(other: CpuJson, version: u64, i: usize, oc_limits: CpuLimits) -> Self {
fn from_json_and_limits(other: CpuJson, version: u64, i: usize, oc_limits: GenericCpuLimit) -> Self {
match version {
0 => Self {
online: other.online,
@ -272,6 +266,12 @@ impl Cpu {
}
}
#[inline]
fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
let oc_limits = GenericCpuLimit::default_for(&limits_core::json_v2::CpuLimitType::SteamDeck, i);
Self::from_json_and_limits(other, version, i, oc_limits)
}
fn find_card_sysfs(root: Option<impl AsRef<std::path::Path>>) -> BasicEntityPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.class("drm", sysfuss::capability::attributes(crate::settings::util::CARD_NEEDS.into_iter().map(|s| s.to_string()))) {
@ -338,8 +338,8 @@ impl Cpu {
}
// min clock
if let Some(min) = clock_limits.min {
let valid_min = if min < self.limits.clock_min.min {
self.limits.clock_min.min
let valid_min = if min < range_min_or_fallback(&self.limits.clock_min, MIN_MIN_CLOCK) {
range_min_or_fallback(&self.limits.clock_min, MIN_MIN_CLOCK)
} else {
min
};
@ -367,10 +367,10 @@ impl Cpu {
// disable manual clock limits
log::debug!("Setting CPU {} to default clockspeed", self.index);
// max clock
self.set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max)
self.set_clock_limit(self.index, range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK), ClockType::Max)
.unwrap_or_else(|e| errors.push(e));
// min clock
self.set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min)
self.set_clock_limit(self.index, range_min_or_fallback(&self.limits.clock_min, MIN_MIN_CLOCK), ClockType::Min)
.unwrap_or_else(|e| errors.push(e));
}
// TODO remove this when it's no longer needed
@ -395,10 +395,10 @@ impl Cpu {
// disable manual clock limits
log::debug!("Setting CPU {} to default clockspeed", self.index);
// max clock
self.set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max)
self.set_clock_limit(self.index, range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK), ClockType::Max)
.unwrap_or_else(|e| errors.push(e));
// min clock
self.set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min)
self.set_clock_limit(self.index, range_min_or_fallback(&self.limits.clock_min, MIN_MIN_CLOCK), ClockType::Min)
.unwrap_or_else(|e| errors.push(e));
self.set_confirm().unwrap_or_else(|e| errors.push(e));
@ -493,11 +493,11 @@ impl Cpu {
if let Some(clock_limits) = &mut self.clock_limits {
if let Some(min) = clock_limits.min {
clock_limits.min =
Some(min.clamp(self.limits.clock_max.min, self.limits.clock_min.max));
Some(min.clamp(range_min_or_fallback(&self.limits.clock_max, MIN_MAX_CLOCK), range_max_or_fallback(&self.limits.clock_min, MAX_CLOCK)));
}
if let Some(max) = clock_limits.max {
clock_limits.max =
Some(max.clamp(self.limits.clock_max.min, self.limits.clock_max.max));
Some(max.clamp(range_min_or_fallback(&self.limits.clock_max, MIN_MAX_CLOCK), range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK)));
}
}
}
@ -514,7 +514,7 @@ impl Cpu {
}
}*/
fn system_default(cpu_index: usize, oc_limits: CpuLimits) -> Self {
fn from_limits(cpu_index: usize, oc_limits: GenericCpuLimit) -> Self {
Self {
online: true,
clock_limits: None,
@ -526,17 +526,21 @@ impl Cpu {
}
}
fn system_default(cpu_index: usize) -> Self {
Self::from_limits(cpu_index, GenericCpuLimit::default_for(&limits_core::json_v2::CpuLimitType::SteamDeck, cpu_index))
}
fn limits(&self) -> crate::api::CpuLimits {
crate::api::CpuLimits {
clock_min_limits: Some(RangeLimit {
min: self.limits.clock_max.min, // allows min to be set by max (it's weird, blame the kernel)
max: self.limits.clock_min.max,
min: range_min_or_fallback(&self.limits.clock_max, MIN_MAX_CLOCK), // allows min to be set by max (it's weird, blame the kernel)
max: range_max_or_fallback(&self.limits.clock_min, MAX_CLOCK),
}),
clock_max_limits: Some(RangeLimit {
min: self.limits.clock_max.min,
max: self.limits.clock_max.max,
min: range_min_or_fallback(&self.limits.clock_max, MIN_MAX_CLOCK),
max: range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK),
}),
clock_step: self.limits.clock_step,
clock_step: self.limits.clock_step.unwrap_or(CLOCK_STEP),
governors: self.governors(),
}
}

View file

@ -2,11 +2,12 @@ use std::convert::Into;
use sysfuss::{BasicEntityPath, HwMonPath, SysEntity, capability::attributes, SysEntityAttributesExt, SysAttribute};
use super::oc_limits::{GpuLimits, OverclockLimits};
use limits_core::json_v2::GenericGpuLimit;
use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT;
use crate::api::RangeLimit;
use crate::persist::GpuJson;
use crate::settings::TGpu;
use crate::settings::{TGpu, ProviderBuilder};
use crate::settings::{min_max_from_json, MinMax};
use crate::settings::{OnResume, OnSet, SettingError};
@ -20,9 +21,8 @@ pub struct Gpu {
pub slow_ppt: Option<u64>,
pub clock_limits: Option<MinMax<u64>>,
pub slow_memory: bool,
limits: GpuLimits,
limits: GenericGpuLimit,
state: crate::state::steam_deck::Gpu,
driver_mode: crate::persist::DriverJson,
sysfs_card: BasicEntityPath,
sysfs_hwmon: HwMonPath
}
@ -45,41 +45,16 @@ enum ClockType {
Max = 1,
}
impl Gpu {
#[inline]
pub fn from_json(other: GpuJson, version: u64) -> Self {
let (oc_limits, is_default) = OverclockLimits::load_or_default();
let driver = if is_default {
crate::persist::DriverJson::SteamDeck
} else {
crate::persist::DriverJson::SteamDeckAdvance
};
match version {
0 => Self {
fast_ppt: other.fast_ppt,
slow_ppt: other.slow_ppt,
clock_limits: other.clock_limits.map(|x| min_max_from_json(x, version)),
slow_memory: other.slow_memory,
limits: oc_limits.gpu,
state: crate::state::steam_deck::Gpu::default(),
driver_mode: driver,
sysfs_card: Self::find_card_sysfs(other.root.clone()),
sysfs_hwmon: Self::find_hwmon_sysfs(other.root),
},
_ => Self {
fast_ppt: other.fast_ppt,
slow_ppt: other.slow_ppt,
clock_limits: other.clock_limits.map(|x| min_max_from_json(x, version)),
slow_memory: other.slow_memory,
limits: oc_limits.gpu,
state: crate::state::steam_deck::Gpu::default(),
driver_mode: driver,
sysfs_card: Self::find_card_sysfs(other.root.clone()),
sysfs_hwmon: Self::find_hwmon_sysfs(other.root),
},
}
}
const MAX_CLOCK: u64 = 1600;
const MIN_CLOCK: u64 = 200;
const MAX_FAST_PPT: u64 = 30_000_000;
const MIN_FAST_PPT: u64 = 1_000_000;
const MAX_SLOW_PPT: u64 = 29_000_000;
const MIN_SLOW_PPT: u64 = 1_000_000;
const MIDDLE_PPT: u64 = 15_000_000;
const PPT_DIVISOR: u64 = 1_000;
impl Gpu {
fn find_card_sysfs(root: Option<impl AsRef<std::path::Path>>) -> BasicEntityPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.class("drm", attributes(crate::settings::util::CARD_NEEDS.into_iter().map(|s| s.to_string()))) {
@ -160,10 +135,10 @@ impl Gpu {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?;
// disable manual clock limits
// max clock
self.set_clock_limit(self.limits.clock_max.max, ClockType::Max)
self.set_clock_limit(self.limits.clock_max.and_then(|lim| lim.max).unwrap_or(MAX_CLOCK), ClockType::Max)
.unwrap_or_else(|e| errors.push(e));
// min clock
self.set_clock_limit(self.limits.clock_min.min, ClockType::Min)
self.set_clock_limit(self.limits.clock_min.and_then(|lim| lim.min).unwrap_or(MIN_CLOCK), ClockType::Min)
.unwrap_or_else(|e| errors.push(e));
self.set_confirm().unwrap_or_else(|e| errors.push(e));
@ -251,7 +226,7 @@ impl Gpu {
});
} else if self.state.fast_ppt_set {
self.state.fast_ppt_set = false;
let fast_ppt = self.limits.fast_ppt_default;
let fast_ppt = self.limits.fast_ppt_default.unwrap_or(MIDDLE_PPT);
self.sysfs_hwmon.set(FAST_PPT_ATTRIBUTE, fast_ppt)
.map_err(|e| SettingError {
msg: format!(
@ -280,7 +255,7 @@ impl Gpu {
});
} else if self.state.slow_ppt_set {
self.state.slow_ppt_set = false;
let slow_ppt = self.limits.slow_ppt_default;
let slow_ppt = self.limits.slow_ppt_default.unwrap_or(MIDDLE_PPT);
self.sysfs_hwmon.set(SLOW_PPT_ATTRIBUTE, slow_ppt)
.map_err(|e| SettingError {
msg: format!(
@ -304,41 +279,22 @@ impl Gpu {
fn clamp_all(&mut self) {
if let Some(fast_ppt) = &mut self.fast_ppt {
*fast_ppt = (*fast_ppt).clamp(self.limits.fast_ppt.min, self.limits.fast_ppt.max);
*fast_ppt = (*fast_ppt).clamp(self.limits.fast_ppt.and_then(|lim| lim.min).unwrap_or(MIN_FAST_PPT), self.limits.fast_ppt.and_then(|lim| lim.max).unwrap_or(MAX_FAST_PPT));
}
if let Some(slow_ppt) = &mut self.slow_ppt {
*slow_ppt = (*slow_ppt).clamp(self.limits.slow_ppt.min, self.limits.slow_ppt.max);
*slow_ppt = (*slow_ppt).clamp(self.limits.slow_ppt.and_then(|lim| lim.min).unwrap_or(MIN_SLOW_PPT), self.limits.slow_ppt.and_then(|lim| lim.max).unwrap_or(MAX_SLOW_PPT));
}
if let Some(clock_limits) = &mut self.clock_limits {
if let Some(min) = clock_limits.min {
clock_limits.min =
Some(min.clamp(self.limits.clock_min.min, self.limits.clock_min.max));
Some(min.clamp(self.limits.clock_min.and_then(|lim| lim.min).unwrap_or(MIN_CLOCK), self.limits.clock_min.and_then(|lim| lim.max).unwrap_or(MAX_CLOCK)));
}
if let Some(max) = clock_limits.max {
clock_limits.max =
Some(max.clamp(self.limits.clock_max.min, self.limits.clock_max.max));
Some(max.clamp(self.limits.clock_max.and_then(|lim| lim.min).unwrap_or(MIN_CLOCK), self.limits.clock_max.and_then(|lim| lim.max).unwrap_or(MAX_CLOCK)));
}
}
}
pub fn system_default() -> Self {
let (oc_limits, is_default) = OverclockLimits::load_or_default();
Self {
fast_ppt: None,
slow_ppt: None,
clock_limits: None,
slow_memory: false,
limits: oc_limits.gpu,
state: crate::state::steam_deck::Gpu::default(),
driver_mode: if is_default {
crate::persist::DriverJson::SteamDeck
} else {
crate::persist::DriverJson::SteamDeckAdvance
},
sysfs_card: Self::find_card_sysfs(None::<&'static str>),
sysfs_hwmon: Self::find_hwmon_sysfs(None::<&'static str>),
}
}
}
impl Into<GpuJson> for Gpu {
@ -354,6 +310,46 @@ impl Into<GpuJson> for Gpu {
}
}
impl ProviderBuilder<GpuJson, GenericGpuLimit> for Gpu {
fn from_json_and_limits(persistent: GpuJson, version: u64, limits: GenericGpuLimit) -> Self {
match version {
0 => Self {
fast_ppt: persistent.fast_ppt,
slow_ppt: persistent.slow_ppt,
clock_limits: persistent.clock_limits.map(|x| min_max_from_json(x, version)),
slow_memory: persistent.slow_memory,
limits: limits,
state: crate::state::steam_deck::Gpu::default(),
sysfs_card: Self::find_card_sysfs(persistent.root.clone()),
sysfs_hwmon: Self::find_hwmon_sysfs(persistent.root),
},
_ => Self {
fast_ppt: persistent.fast_ppt,
slow_ppt: persistent.slow_ppt,
clock_limits: persistent.clock_limits.map(|x| min_max_from_json(x, version)),
slow_memory: persistent.slow_memory,
limits: limits,
state: crate::state::steam_deck::Gpu::default(),
sysfs_card: Self::find_card_sysfs(persistent.root.clone()),
sysfs_hwmon: Self::find_hwmon_sysfs(persistent.root),
},
}
}
fn from_limits(limits: GenericGpuLimit) -> Self {
Self {
fast_ppt: None,
slow_ppt: None,
clock_limits: None,
slow_memory: false,
limits: limits,
state: crate::state::steam_deck::Gpu::default(),
sysfs_card: Self::find_card_sysfs(None::<&'static str>),
sysfs_hwmon: Self::find_hwmon_sysfs(None::<&'static str>),
}
}
}
impl OnSet for Gpu {
fn on_set(&mut self) -> Result<(), Vec<SettingError>> {
self.clamp_all();
@ -375,26 +371,26 @@ impl TGpu for Gpu {
fn limits(&self) -> crate::api::GpuLimits {
crate::api::GpuLimits {
fast_ppt_limits: Some(RangeLimit {
min: self.limits.fast_ppt.min / self.limits.ppt_divisor,
max: self.limits.fast_ppt.max / self.limits.ppt_divisor,
min: super::util::range_min_or_fallback(&self.limits.fast_ppt, MIN_FAST_PPT) / self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR),
max: super::util::range_max_or_fallback(&self.limits.fast_ppt, MAX_FAST_PPT) / self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR),
}),
slow_ppt_limits: Some(RangeLimit {
min: self.limits.slow_ppt.min / self.limits.ppt_divisor,
max: self.limits.slow_ppt.max / self.limits.ppt_divisor,
min: super::util::range_min_or_fallback(&self.limits.slow_ppt, MIN_SLOW_PPT) / self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR),
max: super::util::range_max_or_fallback(&self.limits.slow_ppt, MIN_SLOW_PPT) / self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR),
}),
ppt_step: self.limits.ppt_step,
ppt_step: self.limits.ppt_step.unwrap_or(1),
tdp_limits: None,
tdp_boost_limits: None,
tdp_step: 42,
clock_min_limits: Some(RangeLimit {
min: self.limits.clock_min.min,
max: self.limits.clock_min.max,
min: super::util::range_min_or_fallback(&self.limits.clock_min, MIN_CLOCK),
max: super::util::range_max_or_fallback(&self.limits.clock_min, MAX_CLOCK),
}),
clock_max_limits: Some(RangeLimit {
min: self.limits.clock_max.min,
max: self.limits.clock_max.max,
min: super::util::range_min_or_fallback(&self.limits.clock_max, MIN_CLOCK),
max: super::util::range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK),
}),
clock_step: self.limits.clock_step,
clock_step: self.limits.clock_step.unwrap_or(100),
memory_control_capable: true,
}
}
@ -404,14 +400,14 @@ impl TGpu for Gpu {
}
fn ppt(&mut self, fast: Option<u64>, slow: Option<u64>) {
self.fast_ppt = fast.map(|x| x * self.limits.ppt_divisor);
self.slow_ppt = slow.map(|x| x * self.limits.ppt_divisor);
self.fast_ppt = fast.map(|x| x * self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR));
self.slow_ppt = slow.map(|x| x * self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR));
}
fn get_ppt(&self) -> (Option<u64>, Option<u64>) {
(
self.fast_ppt.map(|x| x / self.limits.ppt_divisor),
self.slow_ppt.map(|x| x / self.limits.ppt_divisor),
self.fast_ppt.map(|x| x / self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR)),
self.slow_ppt.map(|x| x / self.limits.ppt_divisor.unwrap_or(PPT_DIVISOR)),
)
}
@ -428,6 +424,6 @@ impl TGpu for Gpu {
}
fn provider(&self) -> crate::persist::DriverJson {
self.driver_mode.clone()
crate::persist::DriverJson::SteamDeck
}
}

View file

@ -1,7 +1,6 @@
mod battery;
mod cpu;
mod gpu;
mod oc_limits;
mod power_dpm_force;
mod util;
@ -11,3 +10,11 @@ pub use gpu::Gpu;
pub(self) use power_dpm_force::{POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT, DPM_FORCE_LIMITS_ATTRIBUTE};
pub use util::flash_led;
fn _impl_checker() {
fn impl_provider_builder<T: crate::settings::ProviderBuilder<J, L>, J, L>() {}
impl_provider_builder::<Battery, crate::persist::BatteryJson, limits_core::json_v2::GenericBatteryLimit>();
impl_provider_builder::<Cpus, Vec<crate::persist::CpuJson>, limits_core::json_v2::GenericCpusLimit>();
impl_provider_builder::<Gpu, crate::persist::GpuJson, limits_core::json_v2::GenericGpuLimit>();
}

View file

@ -1,190 +0,0 @@
use crate::api::RangeLimit as MinMax;
use serde::{Deserialize, Serialize};
const OC_LIMITS_FILEPATH: &str = "pt_oc.json";
#[derive(Serialize, Deserialize, Debug)]
pub(super) struct OverclockLimits {
pub battery: BatteryLimits,
pub cpus: CpusLimits,
pub gpu: GpuLimits,
}
impl Default for OverclockLimits {
fn default() -> Self {
Self {
battery: BatteryLimits::default(),
cpus: CpusLimits::default(),
gpu: GpuLimits::default(),
}
}
}
impl OverclockLimits {
/// (Self, is_default)
pub fn load_or_default() -> (Self, bool) {
let path = oc_limits_filepath();
if path.exists() {
log::info!("Steam Deck limits file {} found", path.display());
let mut file = match std::fs::File::open(&path) {
Ok(f) => f,
Err(e) => {
log::warn!(
"Steam Deck limits file {} err: {} (using default fallback)",
path.display(),
e
);
return (Self::default(), true);
}
};
match serde_json::from_reader(&mut file) {
Ok(result) => {
log::debug!(
"Steam Deck limits file {} successfully loaded",
path.display()
);
(result, false)
}
Err(e) => {
log::warn!(
"Steam Deck limits file {} json err: {} (using default fallback)",
path.display(),
e
);
(Self::default(), true)
}
}
} else {
log::info!(
"Steam Deck limits file {} not found (using default fallback)",
path.display()
);
(Self::default(), true)
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub(super) struct BatteryLimits {
pub charge_rate: MinMax<u64>,
pub extra_readouts: bool,
}
impl Default for BatteryLimits {
fn default() -> Self {
Self {
charge_rate: MinMax {
min: 250,
max: 2500,
},
extra_readouts: false,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub(super) struct CpusLimits {
pub cpus: Vec<CpuLimits>,
pub global_governors: bool,
}
impl Default for CpusLimits {
fn default() -> Self {
Self {
cpus: [(); 8].iter().map(|_| CpuLimits::default()).collect(),
global_governors: true,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub(super) struct CpuLimits {
pub clock_min: MinMax<u64>,
pub clock_max: MinMax<u64>,
pub clock_step: u64,
pub skip_resume_reclock: bool,
}
impl Default for CpuLimits {
fn default() -> Self {
Self {
clock_min: MinMax {
min: 1400,
max: 3500,
},
clock_max: MinMax {
min: 400,
max: 3500,
},
clock_step: 100,
skip_resume_reclock: false,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub(super) struct GpuLimits {
pub fast_ppt: MinMax<u64>,
pub fast_ppt_default: u64,
pub slow_ppt: MinMax<u64>,
pub slow_ppt_default: u64,
pub ppt_divisor: u64,
pub ppt_step: u64,
pub clock_min: MinMax<u64>,
pub clock_max: MinMax<u64>,
pub clock_step: u64,
pub skip_resume_reclock: bool,
}
impl Default for GpuLimits {
fn default() -> Self {
Self {
fast_ppt: MinMax {
min: 1000000,
max: 30_000_000,
},
fast_ppt_default: 15_000_000,
slow_ppt: MinMax {
min: 1000000,
max: 29_000_000,
},
slow_ppt_default: 15_000_000,
ppt_divisor: 1_000_000,
ppt_step: 1,
clock_min: MinMax {
min: 400,
max: 1600,
},
clock_max: MinMax {
min: 400,
max: 1600,
},
clock_step: 100,
skip_resume_reclock: false,
}
}
}
fn oc_limits_filepath() -> std::path::PathBuf {
crate::utility::settings_dir().join(OC_LIMITS_FILEPATH)
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "dev_stuff"))] // this can fail due to reading from incompletely-written file otherwise
#[test]
fn load_pt_oc() {
let mut file = std::fs::File::open("../pt_oc.json").unwrap();
let settings: OverclockLimits = serde_json::from_reader(&mut file).unwrap();
assert!(settings.cpus.cpus.len() == 8);
}
#[cfg(feature = "dev_stuff")]
#[test]
fn emit_default_pt_oc() {
let mut file = std::fs::File::create("../pt_oc.json").unwrap();
serde_json::to_writer_pretty(&mut file, &OverclockLimits::default()).unwrap();
}
}

View file

@ -12,6 +12,14 @@ pub const JUPITER_HWMON_NAME: &'static str = "jupiter";
pub const STEAMDECK_HWMON_NAME: &'static str = "steamdeck_hwmon";
pub const GPU_HWMON_NAME: &'static str = "amdgpu";
pub fn range_min_or_fallback<I: Copy>(range: &Option<limits_core::json_v2::RangeLimit<I>>, fallback: I) -> I {
range.and_then(|lim| lim.min).unwrap_or(fallback)
}
pub fn range_max_or_fallback<I: Copy>(range: &Option<limits_core::json_v2::RangeLimit<I>>, fallback: I) -> I {
range.and_then(|lim| lim.max).unwrap_or(fallback)
}
pub fn card_also_has(card: &dyn sysfuss::SysEntity, extensions: &'static [&'static str]) -> bool {
extensions.iter()
.all(|ext| card.as_ref().join(ext).exists())

View file

@ -40,6 +40,12 @@ pub trait OnPowerEvent {
}
}
pub trait ProviderBuilder<J, L> {
fn from_json_and_limits(persistent: J, version: u64, limits: L) -> Self;
fn from_limits(limits: L) -> Self;
}
pub trait TGpu: OnSet + OnResume + OnPowerEvent + Debug + Send {
fn limits(&self) -> crate::api::GpuLimits;

View file

@ -1,12 +1,21 @@
use std::convert::Into;
use limits_core::json_v2::GenericBatteryLimit;
use crate::persist::BatteryJson;
use crate::settings::TBattery;
use crate::settings::{TBattery, ProviderBuilder};
use crate::settings::{OnResume, OnSet, SettingError};
#[derive(Debug, Clone)]
pub struct Battery;
impl Battery {
#[inline]
fn system_default() -> Self {
Battery
}
}
impl Into<BatteryJson> for Battery {
#[inline]
fn into(self) -> BatteryJson {
@ -19,6 +28,16 @@ impl Into<BatteryJson> for Battery {
}
}
impl ProviderBuilder<BatteryJson, GenericBatteryLimit> for Battery {
fn from_json_and_limits(_persistent: BatteryJson, _version: u64, _limits: GenericBatteryLimit) -> Self {
Battery::system_default()
}
fn from_limits(_limits: GenericBatteryLimit) -> Self {
Battery::system_default()
}
}
impl OnSet for Battery {
fn on_set(&mut self) -> Result<(), Vec<SettingError>> {
Ok(())

View file

@ -1,9 +1,11 @@
use std::convert::Into;
use limits_core::json_v2::GenericCpusLimit;
use crate::persist::CpuJson;
use crate::settings::MinMax;
use crate::settings::{OnResume, OnSet, SettingError};
use crate::settings::{TCpu, TCpus};
use crate::settings::{TCpu, TCpus, ProviderBuilder};
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
@ -111,7 +113,7 @@ impl Cpus {
}
}
#[inline]
/*#[inline]
pub fn from_json(mut other: Vec<CpuJson>, version: u64) -> Self {
let (_, can_smt) = Self::system_smt_capabilities();
let mut result = Vec::with_capacity(other.len());
@ -140,6 +142,42 @@ impl Cpus {
smt: smt_guess,
smt_capable: can_smt,
}
}*/
}
impl ProviderBuilder<Vec<CpuJson>, GenericCpusLimit> for Cpus {
fn from_json_and_limits(mut persistent: Vec<CpuJson>, version: u64, _limits: GenericCpusLimit) -> Self {
let (_, can_smt) = Self::system_smt_capabilities();
let mut result = Vec::with_capacity(persistent.len());
let max_cpus = Self::cpu_count();
let smt_guess = crate::settings::util::guess_smt(&persistent) && can_smt;
for (i, cpu) in persistent.drain(..).enumerate() {
// prevent having more CPUs than available
if let Some(max_cpus) = max_cpus {
if i == max_cpus {
break;
}
}
let new_cpu = Cpu::from_json(cpu, version, i);
result.push(new_cpu);
}
if let Some(max_cpus) = max_cpus {
if result.len() != max_cpus {
let mut sys_cpus = Cpus::system_default();
for i in result.len()..sys_cpus.cpus.len() {
result.push(sys_cpus.cpus.remove(i));
}
}
}
Self {
cpus: result,
smt: smt_guess,
smt_capable: can_smt,
}
}
fn from_limits(_limits: GenericCpusLimit) -> Self {
Self::system_default()
}
}
@ -153,7 +191,7 @@ impl TCpus for Cpus {
}
}
fn json(&self) -> Vec<crate::persist::CpuJson> {
fn json(&self) -> Vec<CpuJson> {
self.cpus.iter().map(|x| x.to_owned().into()).collect()
}

View file

@ -1,8 +1,10 @@
use std::convert::Into;
use limits_core::json_v2::GenericGpuLimit;
use crate::persist::GpuJson;
use crate::settings::MinMax;
use crate::settings::TGpu;
use crate::settings::{TGpu, ProviderBuilder};
use crate::settings::{OnResume, OnSet, SettingError};
#[derive(Debug, Clone)]
@ -11,16 +13,21 @@ pub struct Gpu {
}
impl Gpu {
#[inline]
pub fn from_json(_other: GpuJson, _version: u64) -> Self {
Self { slow_memory: false }
}
pub fn system_default() -> Self {
Self { slow_memory: false }
}
}
impl ProviderBuilder<GpuJson, GenericGpuLimit> for Gpu {
fn from_json_and_limits(_persistent: GpuJson, _version: u64, _limits: GenericGpuLimit) -> Self {
Self::system_default()
}
fn from_limits(_limits: GenericGpuLimit) -> Self {
Self::system_default()
}
}
impl Into<GpuJson> for Gpu {
#[inline]
fn into(self) -> GpuJson {

View file

@ -5,3 +5,11 @@ mod gpu;
pub use battery::Battery;
pub use cpu::{Cpu, Cpus};
pub use gpu::Gpu;
fn _impl_checker() {
fn impl_provider_builder<T: crate::settings::ProviderBuilder<J, L>, J, L>() {}
impl_provider_builder::<Battery, crate::persist::BatteryJson, limits_core::json_v2::GenericBatteryLimit>();
impl_provider_builder::<Cpus, Vec<crate::persist::CpuJson>, limits_core::json_v2::GenericCpusLimit>();
impl_provider_builder::<Gpu, crate::persist::GpuJson, limits_core::json_v2::GenericGpuLimit>();
}