Add and (mostly) test driver support
This commit is contained in:
parent
165a78e231
commit
31c72de11e
34 changed files with 2379 additions and 358 deletions
54
backend/src/api/api_types.rs
Normal file
54
backend/src/api/api_types.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct RangeLimit<T> {
|
||||||
|
pub min: T,
|
||||||
|
pub max: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SettingsLimits {
|
||||||
|
pub battery: BatteryLimits,
|
||||||
|
pub cpu: CpusLimits,
|
||||||
|
pub gpu: GpuLimits,
|
||||||
|
pub general: GeneralLimits,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct BatteryLimits {
|
||||||
|
pub charge_rate: Option<RangeLimit<u64>>,
|
||||||
|
pub charge_step: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct CpusLimits {
|
||||||
|
pub cpus: Vec<CpuLimits>,
|
||||||
|
pub count: usize,
|
||||||
|
pub smt_capable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct CpuLimits {
|
||||||
|
pub clock_min_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub clock_max_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub clock_step: u64,
|
||||||
|
pub governors: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct GeneralLimits {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct GpuLimits {
|
||||||
|
pub fast_ppt_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub slow_ppt_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub ppt_step: u64,
|
||||||
|
pub tdp_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub tdp_boost_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub tdp_step: u64,
|
||||||
|
pub clock_min_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub clock_max_limits: Option<RangeLimit<u64>>,
|
||||||
|
pub clock_step: u64,
|
||||||
|
pub memory_control_capable: bool,
|
||||||
|
}
|
|
@ -6,22 +6,22 @@ use super::handler::{ApiMessage, BatteryMessage};
|
||||||
|
|
||||||
/// Current current (ha!) web method
|
/// Current current (ha!) web method
|
||||||
pub fn current_now(_: super::ApiParameterType) -> super::ApiParameterType {
|
pub fn current_now(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||||
super::utility::map_result(crate::settings::Battery::read_current_now())
|
super::utility::map_optional_result(crate::settings::driver::read_current_now())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Charge now web method
|
/// Charge now web method
|
||||||
pub fn charge_now(_: super::ApiParameterType) -> super::ApiParameterType {
|
pub fn charge_now(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||||
super::utility::map_result(crate::settings::Battery::read_charge_now())
|
super::utility::map_optional_result(crate::settings::driver::read_charge_now())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Charge full web method
|
/// Charge full web method
|
||||||
pub fn charge_full(_: super::ApiParameterType) -> super::ApiParameterType {
|
pub fn charge_full(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||||
super::utility::map_result(crate::settings::Battery::read_charge_full())
|
super::utility::map_optional_result(crate::settings::driver::read_charge_full())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Charge design web method
|
/// Charge design web method
|
||||||
pub fn charge_design(_: super::ApiParameterType) -> super::ApiParameterType {
|
pub fn charge_design(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||||
super::utility::map_result(crate::settings::Battery::read_charge_design())
|
super::utility::map_optional_result(crate::settings::driver::read_charge_design())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate set battery charge rate web method
|
/// Generate set battery charge rate web method
|
||||||
|
|
|
@ -3,14 +3,14 @@ use std::sync::{Arc, Mutex};
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
use usdpl_back::AsyncCallable;
|
use usdpl_back::AsyncCallable;
|
||||||
|
|
||||||
use crate::settings::{Cpus, SettingError, SettingVariant, MinMax};
|
use crate::settings::{SettingError, SettingVariant, MinMax};
|
||||||
//use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
//use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||||
use super::handler::{ApiMessage, CpuMessage};
|
use super::handler::{ApiMessage, CpuMessage};
|
||||||
|
|
||||||
/// Available CPUs web method
|
/// Available CPUs web method
|
||||||
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
|
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||||
super::utility::map_result(
|
super::utility::map_result(
|
||||||
Cpus::cpu_count()
|
crate::settings::steam_deck::Cpus::cpu_count()
|
||||||
.map(|x| x as u64)
|
.map(|x| x as u64)
|
||||||
.ok_or_else(
|
.ok_or_else(
|
||||||
|| SettingError {
|
|| SettingError {
|
||||||
|
|
|
@ -161,3 +161,19 @@ pub fn lock_unlock_all(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate get limits web method
|
||||||
|
pub fn get_limits(
|
||||||
|
sender: Sender<ApiMessage>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety
|
||||||
|
let getter = move || {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let callback = move |value: super::SettingsLimits| tx.send(value).expect("get_limits callback send failed");
|
||||||
|
sender.lock().unwrap().send(ApiMessage::GetLimits(Box::new(callback))).expect("get_limits send failed");
|
||||||
|
rx.recv().expect("get_limits callback recv failed")
|
||||||
|
};
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
vec![Primitive::Json(serde_json::to_string(&getter()).unwrap())]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
|
||||||
use crate::settings::{Settings, Cpus, Gpu, Battery, General, OnSet, OnResume, MinMax};
|
use crate::settings::{Settings, TCpus, TGpu, TBattery, TGeneral, OnSet, OnResume, MinMax};
|
||||||
use crate::persist::SettingsJson;
|
use crate::persist::SettingsJson;
|
||||||
use crate::utility::unwrap_maybe_fatal;
|
use crate::utility::unwrap_maybe_fatal;
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ pub enum ApiMessage {
|
||||||
LoadSettings(String, String), // (path, name)
|
LoadSettings(String, String), // (path, name)
|
||||||
LoadMainSettings,
|
LoadMainSettings,
|
||||||
LoadSystemSettings,
|
LoadSystemSettings,
|
||||||
|
GetLimits(Callback<super::SettingsLimits>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BatteryMessage {
|
pub enum BatteryMessage {
|
||||||
|
@ -24,10 +25,10 @@ pub enum BatteryMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BatteryMessage {
|
impl BatteryMessage {
|
||||||
fn process(self, settings: &mut Battery) {
|
fn process(self, settings: &mut dyn TBattery) {
|
||||||
match self {
|
match self {
|
||||||
Self::SetChargeRate(rate) => settings.charge_rate = rate,
|
Self::SetChargeRate(rate) => settings.charge_rate(rate),
|
||||||
Self::GetChargeRate(cb) => cb(settings.charge_rate),
|
Self::GetChargeRate(cb) => cb(settings.get_charge_rate()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,41 +46,41 @@ pub enum CpuMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CpuMessage {
|
impl CpuMessage {
|
||||||
fn process(self, settings: &mut Cpus) {
|
fn process(self, settings: &mut dyn TCpus) {
|
||||||
match self {
|
match self {
|
||||||
Self::SetCpuOnline(index, status) => {settings.cpus.get_mut(index).map(|c| c.online = status);},
|
Self::SetCpuOnline(index, status) => {settings.cpus().get_mut(index).map(|c| *c.online() = status);},
|
||||||
Self::SetCpusOnline(cpus) => {
|
Self::SetCpusOnline(cpus) => {
|
||||||
for i in 0..cpus.len() {
|
for i in 0..cpus.len() {
|
||||||
settings.cpus.get_mut(i).map(|c| c.online = cpus[i]);
|
settings.cpus().get_mut(i).map(|c| *c.online() = cpus[i]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Self::SetSmt(status, cb) => {
|
Self::SetSmt(status, cb) => {
|
||||||
let mut result = Vec::with_capacity(settings.cpus.len());
|
let mut result = Vec::with_capacity(settings.len());
|
||||||
for i in 0..settings.cpus.len() {
|
for i in 0..settings.len() {
|
||||||
settings.cpus[i].online = settings.cpus[i].online && (status || i % 2 == 0);
|
*settings.cpus()[i].online() = *settings.cpus()[i].online() && (status || i % 2 == 0);
|
||||||
result.push(settings.cpus[i].online);
|
result.push(*settings.cpus()[i].online());
|
||||||
}
|
}
|
||||||
cb(result);
|
cb(result);
|
||||||
}
|
}
|
||||||
Self::GetCpusOnline(cb) => {
|
Self::GetCpusOnline(cb) => {
|
||||||
let mut result = Vec::with_capacity(settings.cpus.len());
|
let mut result = Vec::with_capacity(settings.len());
|
||||||
for cpu in &settings.cpus {
|
for cpu in settings.cpus() {
|
||||||
result.push(cpu.online);
|
result.push(*cpu.online());
|
||||||
}
|
}
|
||||||
cb(result);
|
cb(result);
|
||||||
},
|
},
|
||||||
Self::SetClockLimits(index, clocks) => {settings.cpus.get_mut(index).map(|c| c.clock_limits = clocks);},
|
Self::SetClockLimits(index, clocks) => {settings.cpus().get_mut(index).map(|c| c.clock_limits(clocks));},
|
||||||
Self::GetClockLimits(index, cb) => {settings.cpus.get(index).map(|c| cb(c.clock_limits.clone()));},
|
Self::GetClockLimits(index, cb) => {settings.cpus().get(index).map(|c| cb(c.get_clock_limits().map(|x| x.to_owned())));},
|
||||||
Self::SetCpuGovernor(index, gov) => {settings.cpus.get_mut(index).map(|c| c.governor = gov);},
|
Self::SetCpuGovernor(index, gov) => {settings.cpus().get_mut(index).map(|c| c.governor(gov));},
|
||||||
Self::SetCpusGovernor(govs) => {
|
Self::SetCpusGovernor(govs) => {
|
||||||
for i in 0..govs.len() {
|
for i in 0..govs.len() {
|
||||||
settings.cpus.get_mut(i).map(|c| c.governor = govs[i].clone());
|
settings.cpus().get_mut(i).map(|c| c.governor(govs[i].clone()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Self::GetCpusGovernor(cb) => {
|
Self::GetCpusGovernor(cb) => {
|
||||||
let mut result = Vec::with_capacity(settings.cpus.len());
|
let mut result = Vec::with_capacity(settings.len());
|
||||||
for cpu in &settings.cpus {
|
for cpu in settings.cpus() {
|
||||||
result.push(cpu.governor.clone());
|
result.push(cpu.get_governor().to_owned());
|
||||||
}
|
}
|
||||||
cb(result);
|
cb(result);
|
||||||
}
|
}
|
||||||
|
@ -97,17 +98,14 @@ pub enum GpuMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GpuMessage {
|
impl GpuMessage {
|
||||||
fn process(self, settings: &mut Gpu) {
|
fn process(self, settings: &mut dyn TGpu) {
|
||||||
match self {
|
match self {
|
||||||
Self::SetPpt(fast, slow) => {
|
Self::SetPpt(fast, slow) => settings.ppt(fast, slow),
|
||||||
settings.fast_ppt = fast;
|
Self::GetPpt(cb) => cb(settings.get_ppt()),
|
||||||
settings.slow_ppt = slow;
|
Self::SetClockLimits(clocks) => settings.clock_limits(clocks),
|
||||||
},
|
Self::GetClockLimits(cb) => cb(settings.get_clock_limits().map(|x| x.to_owned())),
|
||||||
Self::GetPpt(cb) => cb((settings.fast_ppt, settings.slow_ppt)),
|
Self::SetSlowMemory(val) => *settings.slow_memory() = val,
|
||||||
Self::SetClockLimits(clocks) => settings.clock_limits = clocks,
|
Self::GetSlowMemory(cb) => cb(*settings.slow_memory()),
|
||||||
Self::GetClockLimits(cb) => cb(settings.clock_limits.clone()),
|
|
||||||
Self::SetSlowMemory(val) => settings.slow_memory = val,
|
|
||||||
Self::GetSlowMemory(cb) => cb(settings.slow_memory),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,11 +117,11 @@ pub enum GeneralMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralMessage {
|
impl GeneralMessage {
|
||||||
fn process(self, settings: &mut General) {
|
fn process(self, settings: &mut dyn TGeneral) {
|
||||||
match self {
|
match self {
|
||||||
Self::SetPersistent(val) => settings.persistent = val,
|
Self::SetPersistent(val) => *settings.persistent() = val,
|
||||||
Self::GetPersistent(cb) => cb(settings.persistent),
|
Self::GetPersistent(cb) => cb(*settings.persistent()),
|
||||||
Self::GetCurrentProfileName(cb) => cb(settings.name.clone()),
|
Self::GetCurrentProfileName(cb) => cb(settings.get_name().to_owned()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,11 +148,11 @@ impl ApiMessageHandler {
|
||||||
}
|
}
|
||||||
// save
|
// save
|
||||||
log::debug!("api_worker is saving...");
|
log::debug!("api_worker is saving...");
|
||||||
let is_persistent = settings.general.persistent;
|
let is_persistent = *settings.general.persistent();
|
||||||
if is_persistent {
|
if is_persistent {
|
||||||
let save_path = crate::utility::settings_dir()
|
let save_path = crate::utility::settings_dir()
|
||||||
.join(settings.general.path.clone());
|
.join(settings.general.get_path().clone());
|
||||||
let settings_clone = settings.clone();
|
let settings_clone = settings.json();
|
||||||
let save_json: SettingsJson = settings_clone.into();
|
let save_json: SettingsJson = settings_clone.into();
|
||||||
unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings");
|
unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings");
|
||||||
log::debug!("Saved settings to {}", save_path.display());
|
log::debug!("Saved settings to {}", save_path.display());
|
||||||
|
@ -166,10 +164,10 @@ impl ApiMessageHandler {
|
||||||
|
|
||||||
pub fn process(&mut self, settings: &mut Settings, message: ApiMessage) {
|
pub fn process(&mut self, settings: &mut Settings, message: ApiMessage) {
|
||||||
match message {
|
match message {
|
||||||
ApiMessage::Battery(x) => x.process(&mut settings.battery),
|
ApiMessage::Battery(x) => x.process(settings.battery.as_mut()),
|
||||||
ApiMessage::Cpu(x) => x.process(&mut settings.cpus),
|
ApiMessage::Cpu(x) => x.process(settings.cpus.as_mut()),
|
||||||
ApiMessage::Gpu(x) => x.process(&mut settings.gpu),
|
ApiMessage::Gpu(x) => x.process(settings.gpu.as_mut()),
|
||||||
ApiMessage::General(x) => x.process(&mut settings.general),
|
ApiMessage::General(x) => x.process(settings.general.as_mut()),
|
||||||
ApiMessage::OnResume => {
|
ApiMessage::OnResume => {
|
||||||
if let Err(e) = settings.on_resume() {
|
if let Err(e) = settings.on_resume() {
|
||||||
log::error!("Settings on_resume() err: {}", e);
|
log::error!("Settings on_resume() err: {}", e);
|
||||||
|
@ -194,6 +192,14 @@ impl ApiMessageHandler {
|
||||||
}
|
}
|
||||||
ApiMessage::LoadSystemSettings => {
|
ApiMessage::LoadSystemSettings => {
|
||||||
settings.load_system_default();
|
settings.load_system_default();
|
||||||
|
},
|
||||||
|
ApiMessage::GetLimits(cb) => {
|
||||||
|
cb(super::SettingsLimits {
|
||||||
|
battery: settings.battery.limits(),
|
||||||
|
cpu: settings.cpus.limits(),
|
||||||
|
gpu: settings.gpu.limits(),
|
||||||
|
general: settings.general.limits(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod api_types;
|
||||||
pub mod battery;
|
pub mod battery;
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod general;
|
pub mod general;
|
||||||
|
@ -7,3 +8,5 @@ mod async_utils;
|
||||||
mod utility;
|
mod utility;
|
||||||
|
|
||||||
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;
|
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;
|
||||||
|
|
||||||
|
pub use api_types::*;
|
||||||
|
|
|
@ -14,6 +14,20 @@ pub fn map_result<T: Into<Primitive>>(result: Result<T, SettingError>) -> super:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn map_optional_result<T: Into<Primitive>>(result: Result<Option<T>, SettingError>) -> super::ApiParameterType {
|
||||||
|
match result {
|
||||||
|
Ok(val) => match val {
|
||||||
|
Some(val) => vec![val.into()],
|
||||||
|
None => vec![Primitive::Empty],
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!("Mapping error to primitive: {}", e);
|
||||||
|
vec![e.msg.into()]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*#[inline]
|
/*#[inline]
|
||||||
pub fn map_empty_result<T: Into<Primitive>>(
|
pub fn map_empty_result<T: Into<Primitive>>(
|
||||||
result: Result<(), SettingError>,
|
result: Result<(), SettingError>,
|
||||||
|
|
|
@ -44,6 +44,8 @@ fn main() -> Result<(), ()> {
|
||||||
log::info!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
log::info!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||||
println!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
println!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||||
|
|
||||||
|
crate::settings::driver::auto_detect_loud();
|
||||||
|
|
||||||
let mut loaded_settings = persist::SettingsJson::open(utility::settings_dir().join(DEFAULT_SETTINGS_FILE))
|
let mut loaded_settings = persist::SettingsJson::open(utility::settings_dir().join(DEFAULT_SETTINGS_FILE))
|
||||||
.map(|settings| settings::Settings::from_json(settings, DEFAULT_SETTINGS_FILE.into()))
|
.map(|settings| settings::Settings::from_json(settings, DEFAULT_SETTINGS_FILE.into()))
|
||||||
.unwrap_or_else(|_| settings::Settings::system_default(DEFAULT_SETTINGS_FILE.into()));
|
.unwrap_or_else(|_| settings::Settings::system_default(DEFAULT_SETTINGS_FILE.into()));
|
||||||
|
@ -183,6 +185,10 @@ fn main() -> Result<(), ()> {
|
||||||
.register_async(
|
.register_async(
|
||||||
"GENERAL_wait_for_unlocks",
|
"GENERAL_wait_for_unlocks",
|
||||||
api::general::lock_unlock_all(api_sender.clone())
|
api::general::lock_unlock_all(api_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GENERAL_get_limits",
|
||||||
|
api::general::get_limits(api_sender.clone())
|
||||||
);
|
);
|
||||||
|
|
||||||
api_worker::spawn(loaded_settings, api_handler);
|
api_worker::spawn(loaded_settings, api_handler);
|
||||||
|
|
14
backend/src/persist/driver.rs
Normal file
14
backend/src/persist/driver.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//use std::default::Default;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Debug, Clone)]
|
||||||
|
pub enum DriverJson {
|
||||||
|
#[default]
|
||||||
|
#[serde(rename = "steam-deck", alias = "gabe-boy")]
|
||||||
|
SteamDeck,
|
||||||
|
#[serde(rename = "steam-deck-oc", alias = "gabe-boy-advance")]
|
||||||
|
SteamDeckAdvance,
|
||||||
|
#[serde(rename = "unknown")]
|
||||||
|
Unknown,
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use std::default::Default;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::JsonError;
|
use super::JsonError;
|
||||||
use super::{BatteryJson, CpuJson, GpuJson};
|
use super::{BatteryJson, CpuJson, GpuJson, DriverJson};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct SettingsJson {
|
pub struct SettingsJson {
|
||||||
|
@ -13,6 +13,7 @@ pub struct SettingsJson {
|
||||||
pub cpus: Vec<CpuJson>,
|
pub cpus: Vec<CpuJson>,
|
||||||
pub gpu: GpuJson,
|
pub gpu: GpuJson,
|
||||||
pub battery: BatteryJson,
|
pub battery: BatteryJson,
|
||||||
|
pub provider: Option<DriverJson>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SettingsJson {
|
impl Default for SettingsJson {
|
||||||
|
@ -24,6 +25,7 @@ impl Default for SettingsJson {
|
||||||
cpus: Vec::with_capacity(8),
|
cpus: Vec::with_capacity(8),
|
||||||
gpu: GpuJson::default(),
|
gpu: GpuJson::default(),
|
||||||
battery: BatteryJson::default(),
|
battery: BatteryJson::default(),
|
||||||
|
provider: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
mod battery;
|
mod battery;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
|
mod driver;
|
||||||
mod error;
|
mod error;
|
||||||
mod general;
|
mod general;
|
||||||
mod gpu;
|
mod gpu;
|
||||||
|
|
||||||
pub use battery::BatteryJson;
|
pub use battery::BatteryJson;
|
||||||
pub use cpu::CpuJson;
|
pub use cpu::CpuJson;
|
||||||
|
pub use driver::DriverJson;
|
||||||
pub use general::{MinMaxJson, SettingsJson};
|
pub use general::{MinMaxJson, SettingsJson};
|
||||||
pub use gpu::GpuJson;
|
pub use gpu::GpuJson;
|
||||||
|
|
||||||
|
|
179
backend/src/settings/driver.rs
Normal file
179
backend/src/settings/driver.rs
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
use crate::persist::{DriverJson, SettingsJson};
|
||||||
|
use super::{TGeneral, TCpus, TGpu, TBattery, SettingError, General};
|
||||||
|
|
||||||
|
/// Device detection logic
|
||||||
|
fn auto_detect() -> DriverJson {
|
||||||
|
let lscpu: String = match usdpl_back::api::files::read_single("/proc/cpuinfo") {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return DriverJson::Unknown,
|
||||||
|
};
|
||||||
|
log::debug!("Read from /proc/cpuinfo:\n{}", lscpu);
|
||||||
|
let os_info: String = match usdpl_back::api::files::read_single("/etc/os-release") {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return DriverJson::Unknown,
|
||||||
|
};
|
||||||
|
log::debug!("Read from /etc/os-release:\n{}", os_info);
|
||||||
|
if let Some(_) = lscpu.find("model name\t: AMD Custom APU 0405\n") {
|
||||||
|
// definitely a Steam Deck, check if it's overclocked
|
||||||
|
let max_freq: u64 = match usdpl_back::api::files::read_single("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq") {
|
||||||
|
Ok(u) => u,
|
||||||
|
Err(_) => return DriverJson::SteamDeck,
|
||||||
|
};
|
||||||
|
if max_freq == 2800000 { // default clock speed
|
||||||
|
DriverJson::SteamDeck
|
||||||
|
} else {
|
||||||
|
DriverJson::SteamDeckAdvance
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DriverJson::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn auto_detect_loud() -> DriverJson {
|
||||||
|
let provider = auto_detect();
|
||||||
|
log::info!("Detected device automatically, compatible driver: {:?}", provider);
|
||||||
|
provider
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Driver {
|
||||||
|
pub general: Box<dyn TGeneral>,
|
||||||
|
pub cpus: Box<dyn TCpus>,
|
||||||
|
pub gpu: Box<dyn TGpu>,
|
||||||
|
pub battery: Box<dyn TBattery>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}),
|
||||||
|
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 provider = settings.provider.unwrap_or_else(auto_detect);
|
||||||
|
match provider {
|
||||||
|
DriverJson::SteamDeck => Ok(Self {
|
||||||
|
general: Box::new(General {
|
||||||
|
persistent: settings.persistent,
|
||||||
|
path: json_path,
|
||||||
|
name: settings.name,
|
||||||
|
driver: DriverJson::SteamDeck,
|
||||||
|
}),
|
||||||
|
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::SteamDeckAdvance => Ok(Self {
|
||||||
|
general: Box::new(General {
|
||||||
|
persistent: settings.persistent,
|
||||||
|
path: json_path,
|
||||||
|
name: settings.name,
|
||||||
|
driver: DriverJson::SteamDeckAdvance,
|
||||||
|
}),
|
||||||
|
cpus: Box::new(super::steam_deck_adv::Cpus::from_json(settings.cpus, settings.version)),
|
||||||
|
gpu: Box::new(super::steam_deck_adv::Gpu::from_json(settings.gpu, settings.version)),
|
||||||
|
battery: Box::new(super::steam_deck_adv::Battery::from_json(settings.battery, settings.version)),
|
||||||
|
}),
|
||||||
|
DriverJson::Unknown => Ok(Self {
|
||||||
|
general: Box::new(General {
|
||||||
|
persistent: settings.persistent,
|
||||||
|
path: json_path,
|
||||||
|
name: settings.name,
|
||||||
|
driver: DriverJson::Unknown,
|
||||||
|
}),
|
||||||
|
cpus: Box::new(super::unknown::Cpus::from_json(settings.cpus, settings.version)),
|
||||||
|
gpu: Box::new(super::unknown::Gpu::from_json(settings.gpu, settings.version)),
|
||||||
|
battery: Box::new(super::unknown::Battery),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default(json_path: std::path::PathBuf) -> Self {
|
||||||
|
let provider = auto_detect();
|
||||||
|
match provider {
|
||||||
|
DriverJson::SteamDeck => Self {
|
||||||
|
general: Box::new(General {
|
||||||
|
persistent: false,
|
||||||
|
path: json_path,
|
||||||
|
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
|
||||||
|
driver: DriverJson::SteamDeck,
|
||||||
|
}),
|
||||||
|
cpus: Box::new(super::steam_deck::Cpus::system_default()),
|
||||||
|
gpu: Box::new(super::steam_deck::Gpu::system_default()),
|
||||||
|
battery: Box::new(super::steam_deck::Battery::system_default()),
|
||||||
|
},
|
||||||
|
DriverJson::SteamDeckAdvance => Self {
|
||||||
|
general: Box::new(General {
|
||||||
|
persistent: false,
|
||||||
|
path: json_path,
|
||||||
|
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
|
||||||
|
driver: DriverJson::SteamDeck,
|
||||||
|
}),
|
||||||
|
cpus: Box::new(super::steam_deck_adv::Cpus::system_default()),
|
||||||
|
gpu: Box::new(super::steam_deck_adv::Gpu::system_default()),
|
||||||
|
battery: Box::new(super::steam_deck_adv::Battery::system_default()),
|
||||||
|
},
|
||||||
|
DriverJson::Unknown => Self {
|
||||||
|
general: Box::new(General {
|
||||||
|
persistent: false,
|
||||||
|
path: json_path,
|
||||||
|
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
|
||||||
|
driver: DriverJson::Unknown,
|
||||||
|
}),
|
||||||
|
cpus: Box::new(super::unknown::Cpus::system_default()),
|
||||||
|
gpu: Box::new(super::unknown::Gpu::system_default()),
|
||||||
|
battery: Box::new(super::unknown::Battery),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static battery calls
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_current_now() -> Result<Option<u64>, SettingError> {
|
||||||
|
match auto_detect() {
|
||||||
|
DriverJson::SteamDeck => super::steam_deck::Battery::read_current_now().map(|x| Some(x)),
|
||||||
|
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_current_now().map(|x| Some(x)),
|
||||||
|
DriverJson::Unknown => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_charge_now() -> Result<Option<f64>, SettingError> {
|
||||||
|
match auto_detect() {
|
||||||
|
DriverJson::SteamDeck => super::steam_deck::Battery::read_charge_now().map(|x| Some(x)),
|
||||||
|
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_charge_now().map(|x| Some(x)),
|
||||||
|
DriverJson::Unknown => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_charge_full() -> Result<Option<f64>, SettingError> {
|
||||||
|
match auto_detect() {
|
||||||
|
DriverJson::SteamDeck => super::steam_deck::Battery::read_charge_full().map(|x| Some(x)),
|
||||||
|
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_charge_full().map(|x| Some(x)),
|
||||||
|
DriverJson::Unknown => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_charge_design() -> Result<Option<f64>, SettingError> {
|
||||||
|
match auto_detect() {
|
||||||
|
DriverJson::SteamDeck => super::steam_deck::Battery::read_charge_design().map(|x| Some(x)),
|
||||||
|
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_charge_design().map(|x| Some(x)),
|
||||||
|
DriverJson::Unknown => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
use std::convert::Into;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
//use std::sync::{Arc, Mutex};
|
//use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use super::{Battery, Cpus, Gpu};
|
//use super::{Battery, Cpus, Gpu};
|
||||||
use super::{OnResume, OnSet, SettingError};
|
use super::{OnResume, OnSet, SettingError};
|
||||||
|
use super::{TGeneral, TGpu, TCpus, TBattery};
|
||||||
use crate::persist::SettingsJson;
|
use crate::persist::SettingsJson;
|
||||||
//use crate::utility::unwrap_lock;
|
//use crate::utility::unwrap_lock;
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ pub struct General {
|
||||||
pub persistent: bool,
|
pub persistent: bool,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub driver: crate::persist::DriverJson,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OnSet for General {
|
impl OnSet for General {
|
||||||
|
@ -41,12 +42,52 @@ impl OnSet for General {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl OnResume for General {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TGeneral for General {
|
||||||
|
fn limits(&self) -> crate::api::GeneralLimits {
|
||||||
|
crate::api::GeneralLimits { }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_persistent(&self) -> bool {
|
||||||
|
self.persistent
|
||||||
|
}
|
||||||
|
|
||||||
|
fn persistent(&mut self) -> &'_ mut bool {
|
||||||
|
&mut self.persistent
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path(&self) -> &'_ std::path::Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&mut self, path: std::path::PathBuf) {
|
||||||
|
self.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'_ str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&mut self, name: String) {
|
||||||
|
self.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn provider(&self) -> crate::persist::DriverJson {
|
||||||
|
self.driver.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub general: General,
|
pub general: Box<dyn TGeneral>,
|
||||||
pub cpus: Cpus,
|
pub cpus: Box<dyn TCpus>,
|
||||||
pub gpu: Gpu,
|
pub gpu: Box<dyn TGpu>,
|
||||||
pub battery: Battery,
|
pub battery: Box<dyn TBattery>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OnSet for Settings {
|
impl OnSet for Settings {
|
||||||
|
@ -62,47 +103,38 @@ impl OnSet for Settings {
|
||||||
impl Settings {
|
impl Settings {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_json(other: SettingsJson, json_path: PathBuf) -> Self {
|
pub fn from_json(other: SettingsJson, json_path: PathBuf) -> Self {
|
||||||
match other.version {
|
match super::Driver::init(other, json_path.clone()) {
|
||||||
0 => Self {
|
Ok(x) => {
|
||||||
general: General {
|
log::info!("Loaded settings for driver {:?}", x.general.provider());
|
||||||
persistent: other.persistent,
|
Self {
|
||||||
path: json_path,
|
general: x.general,
|
||||||
name: other.name,
|
cpus: x.cpus,
|
||||||
},
|
gpu: x.gpu,
|
||||||
cpus: Cpus::from_json(other.cpus, other.version),
|
battery: x.battery,
|
||||||
gpu: Gpu::from_json(other.gpu, other.version),
|
}
|
||||||
battery: Battery::from_json(other.battery, other.version),
|
|
||||||
},
|
|
||||||
_ => Self {
|
|
||||||
general: General {
|
|
||||||
persistent: other.persistent,
|
|
||||||
path: json_path,
|
|
||||||
name: other.name,
|
|
||||||
},
|
|
||||||
cpus: Cpus::from_json(other.cpus, other.version),
|
|
||||||
gpu: Gpu::from_json(other.gpu, other.version),
|
|
||||||
battery: Battery::from_json(other.battery, other.version),
|
|
||||||
},
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Driver init error: {}", e);
|
||||||
|
Self::system_default(json_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn system_default(json_path: PathBuf) -> Self {
|
pub fn system_default(json_path: PathBuf) -> Self {
|
||||||
|
let driver = super::Driver::system_default(json_path);
|
||||||
Self {
|
Self {
|
||||||
general: General {
|
general: driver.general,
|
||||||
persistent: false,
|
cpus: driver.cpus,
|
||||||
path: json_path,
|
gpu: driver.gpu,
|
||||||
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
|
battery: driver.battery,
|
||||||
},
|
|
||||||
cpus: Cpus::system_default(),
|
|
||||||
gpu: Gpu::system_default(),
|
|
||||||
battery: Battery::system_default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_system_default(&mut self) {
|
pub fn load_system_default(&mut self) {
|
||||||
self.cpus = Cpus::system_default();
|
let driver = super::Driver::system_default(self.general.get_path().to_owned());
|
||||||
self.gpu = Gpu::system_default();
|
self.cpus = driver.cpus;
|
||||||
self.battery = Battery::system_default();
|
self.gpu = driver.gpu;
|
||||||
|
self.battery = driver.battery;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_file(&mut self, filename: PathBuf, name: String, system_defaults: bool) -> Result<bool, SettingError> {
|
pub fn load_file(&mut self, filename: PathBuf, name: String, system_defaults: bool) -> Result<bool, SettingError> {
|
||||||
|
@ -115,24 +147,36 @@ impl Settings {
|
||||||
})?;
|
})?;
|
||||||
if !settings_json.persistent {
|
if !settings_json.persistent {
|
||||||
log::warn!("Loaded persistent config `{}` ({}) with persistent=false", &settings_json.name, json_path.display());
|
log::warn!("Loaded persistent config `{}` ({}) with persistent=false", &settings_json.name, json_path.display());
|
||||||
self.general.persistent = false;
|
*self.general.persistent() = false;
|
||||||
self.general.name = name;
|
self.general.name(name);
|
||||||
} else {
|
} else {
|
||||||
self.cpus = Cpus::from_json(settings_json.cpus, settings_json.version);
|
self.cpus = Box::new(super::steam_deck::Cpus::from_json(settings_json.cpus, settings_json.version));
|
||||||
self.gpu = Gpu::from_json(settings_json.gpu, settings_json.version);
|
self.gpu = Box::new(super::steam_deck::Gpu::from_json(settings_json.gpu, settings_json.version));
|
||||||
self.battery = Battery::from_json(settings_json.battery, settings_json.version);
|
self.battery = Box::new(super::steam_deck::Battery::from_json(settings_json.battery, settings_json.version));
|
||||||
self.general.persistent = true;
|
*self.general.persistent() = true;
|
||||||
self.general.name = settings_json.name;
|
self.general.name(settings_json.name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if system_defaults {
|
if system_defaults {
|
||||||
self.load_system_default();
|
self.load_system_default();
|
||||||
}
|
}
|
||||||
self.general.persistent = false;
|
*self.general.persistent() = false;
|
||||||
self.general.name = name;
|
self.general.name(name);
|
||||||
|
}
|
||||||
|
self.general.path(json_path);
|
||||||
|
Ok(*self.general.persistent())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn json(&self) -> SettingsJson {
|
||||||
|
SettingsJson {
|
||||||
|
version: LATEST_VERSION,
|
||||||
|
name: self.general.get_name().to_owned(),
|
||||||
|
persistent: self.general.get_persistent(),
|
||||||
|
cpus: self.cpus.json(),
|
||||||
|
gpu: self.gpu.json(),
|
||||||
|
battery: self.battery.json(),
|
||||||
|
provider: Some(self.general.provider()),
|
||||||
}
|
}
|
||||||
self.general.path = json_path;
|
|
||||||
Ok(self.general.persistent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,21 +193,18 @@ impl OnResume for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<SettingsJson> for Settings {
|
/*impl Into<SettingsJson> for Settings {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into(self) -> SettingsJson {
|
fn into(self) -> SettingsJson {
|
||||||
log::debug!("Converting into json");
|
log::debug!("Converting into json");
|
||||||
SettingsJson {
|
SettingsJson {
|
||||||
version: LATEST_VERSION,
|
version: LATEST_VERSION,
|
||||||
name: self.general.name.clone(),
|
name: self.general.get_name().to_owned(),
|
||||||
persistent: self.general.persistent,
|
persistent: self.general.get_persistent(),
|
||||||
cpus: self.cpus.cpus
|
cpus: self.cpus.json(),
|
||||||
.clone()
|
gpu: self.gpu.json(),
|
||||||
.drain(..)
|
battery: self.battery.json(),
|
||||||
.map(|cpu| cpu.into())
|
provider: Some(self.general.provider()),
|
||||||
.collect(),
|
|
||||||
gpu: self.gpu.clone().into(),
|
|
||||||
battery: self.battery.clone().into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
mod battery;
|
pub mod driver;
|
||||||
mod cpu;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod general;
|
mod general;
|
||||||
mod gpu;
|
|
||||||
mod min_max;
|
mod min_max;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
pub use battery::Battery;
|
pub mod steam_deck;
|
||||||
pub use cpu::{Cpu, Cpus};
|
pub mod steam_deck_adv;
|
||||||
|
pub mod unknown;
|
||||||
|
|
||||||
|
pub use driver::Driver;
|
||||||
pub use general::{SettingVariant, Settings, General};
|
pub use general::{SettingVariant, Settings, General};
|
||||||
pub use gpu::Gpu;
|
|
||||||
pub use min_max::MinMax;
|
pub use min_max::MinMax;
|
||||||
|
|
||||||
pub use error::SettingError;
|
pub use error::SettingError;
|
||||||
pub use traits::{OnResume, OnSet, SettingsRange};
|
pub use traits::{OnResume, OnSet, SettingsRange, TGeneral, TGpu, TCpus, TBattery, TCpu};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
|
|
||||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
use crate::api::RangeLimit;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
|
use crate::settings::TBattery;
|
||||||
use crate::persist::BatteryJson;
|
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,
|
state: crate::state::steam_deck::Battery,
|
||||||
}
|
}
|
||||||
|
|
||||||
const BATTERY_VOLTAGE: f64 = 7.7;
|
const BATTERY_VOLTAGE: f64 = 7.7;
|
||||||
|
@ -23,11 +25,11 @@ 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(),
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
},
|
},
|
||||||
_ => Self {
|
_ => Self {
|
||||||
charge_rate: other.charge_rate,
|
charge_rate: other.charge_rate,
|
||||||
state: crate::state::Battery::default(),
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +40,7 @@ impl Battery {
|
||||||
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: crate::settings::SettingVariant::Battery,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else if self.state.charge_rate_set {
|
} else if self.state.charge_rate_set {
|
||||||
|
@ -46,7 +48,7 @@ impl Battery {
|
||||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, Self::max().charge_rate.unwrap()).map_err(
|
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, Self::max().charge_rate.unwrap()).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: crate::settings::SettingVariant::Battery,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -66,11 +68,11 @@ impl Battery {
|
||||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
|
||||||
Err((Some(e), None)) => Err(SettingError {
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err((None, Some(e))) => Err(SettingError {
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err(_) => panic!(
|
Err(_) => panic!(
|
||||||
"Invalid error while reading from `{}`",
|
"Invalid error while reading from `{}`",
|
||||||
|
@ -86,11 +88,11 @@ impl Battery {
|
||||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_NOW_PATH) {
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_NOW_PATH) {
|
||||||
Err((Some(e), None)) => Err(SettingError {
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err((None, Some(e))) => Err(SettingError {
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err(_) => panic!(
|
Err(_) => panic!(
|
||||||
"Invalid error while reading from `{}`",
|
"Invalid error while reading from `{}`",
|
||||||
|
@ -105,11 +107,11 @@ impl Battery {
|
||||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_FULL_PATH) {
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_FULL_PATH) {
|
||||||
Err((Some(e), None)) => Err(SettingError {
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err((None, Some(e))) => Err(SettingError {
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err(_) => panic!(
|
Err(_) => panic!(
|
||||||
"Invalid error while reading from `{}`",
|
"Invalid error while reading from `{}`",
|
||||||
|
@ -124,11 +126,11 @@ impl Battery {
|
||||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_DESIGN_PATH) {
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_DESIGN_PATH) {
|
||||||
Err((Some(e), None)) => Err(SettingError {
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err((None, Some(e))) => Err(SettingError {
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
||||||
setting: super::SettingVariant::Battery,
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
}),
|
}),
|
||||||
Err(_) => panic!(
|
Err(_) => panic!(
|
||||||
"Invalid error while reading from `{}`",
|
"Invalid error while reading from `{}`",
|
||||||
|
@ -142,7 +144,7 @@ impl Battery {
|
||||||
pub fn system_default() -> Self {
|
pub fn system_default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
charge_rate: None,
|
charge_rate: None,
|
||||||
state: crate::state::Battery::default(),
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +176,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(),
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +184,33 @@ 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(),
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TBattery for Battery {
|
||||||
|
fn limits(&self) -> crate::api::BatteryLimits {
|
||||||
|
let max = Self::max();
|
||||||
|
let min = Self::min();
|
||||||
|
crate::api::BatteryLimits {
|
||||||
|
charge_rate: Some(RangeLimit{
|
||||||
|
min: min.charge_rate.unwrap(),
|
||||||
|
max: max.charge_rate.unwrap(),
|
||||||
|
}),
|
||||||
|
charge_step: 50,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::BatteryJson {
|
||||||
|
self.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn charge_rate(&mut self, rate: Option<u64>) {
|
||||||
|
self.charge_rate = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_charge_rate(&self) -> Option<u64> {
|
||||||
|
self.charge_rate
|
||||||
|
}
|
||||||
|
}
|
446
backend/src/settings/steam_deck/cpu.rs
Normal file
446
backend/src/settings/steam_deck/cpu.rs
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::api::RangeLimit;
|
||||||
|
use crate::settings::MinMax;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
|
use crate::settings::{TCpus, TCpu};
|
||||||
|
use crate::persist::CpuJson;
|
||||||
|
|
||||||
|
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
||||||
|
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Cpus {
|
||||||
|
pub cpus: Vec<Cpu>,
|
||||||
|
pub smt: bool,
|
||||||
|
pub smt_capable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Cpus {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
if self.smt_capable {
|
||||||
|
// toggle SMT
|
||||||
|
if self.smt {
|
||||||
|
usdpl_back::api::files::write_single(CPU_SMT_PATH, "on").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `on` to `{}`: {}",
|
||||||
|
CPU_SMT_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
usdpl_back::api::files::write_single(CPU_SMT_PATH, "off").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `off` to `{}`: {}",
|
||||||
|
CPU_SMT_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i, cpu) in self.cpus.as_mut_slice().iter_mut().enumerate() {
|
||||||
|
cpu.state.do_set_online = self.smt || i % 2 == 0;
|
||||||
|
cpu.on_set()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Cpus {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
for cpu in &self.cpus {
|
||||||
|
cpu.on_resume()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cpus {
|
||||||
|
pub fn cpu_count() -> Option<usize> {
|
||||||
|
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
|
||||||
|
.unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */);
|
||||||
|
if let Some(dash_index) = data.find('-') {
|
||||||
|
let data = data.split_off(dash_index + 1);
|
||||||
|
if let Ok(max_cpu) = data.parse::<usize>() {
|
||||||
|
return Some(max_cpu + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_smt_capabilities() -> (bool, bool) {
|
||||||
|
match usdpl_back::api::files::read_single::<_, String, _>(CPU_SMT_PATH) {
|
||||||
|
Ok(val) => (val.trim().to_lowercase() == "on", true),
|
||||||
|
Err(_) => (false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default() -> Self {
|
||||||
|
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::from_sys(i));
|
||||||
|
}
|
||||||
|
let (smt_status, can_smt) = Self::system_smt_capabilities();
|
||||||
|
Self {
|
||||||
|
cpus: sys_cpus,
|
||||||
|
smt: smt_status,
|
||||||
|
smt_capable: can_smt,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
cpus: vec![],
|
||||||
|
smt: false,
|
||||||
|
smt_capable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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());
|
||||||
|
let max_cpus = Self::cpu_count();
|
||||||
|
for (i, cpu) in other.drain(..).enumerate() {
|
||||||
|
// prevent having more CPUs than available
|
||||||
|
if let Some(max_cpus) = max_cpus {
|
||||||
|
if i == max_cpus {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(Cpu::from_json(cpu, version, i));
|
||||||
|
}
|
||||||
|
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: true,
|
||||||
|
smt_capable: can_smt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCpus for Cpus {
|
||||||
|
fn limits(&self) -> crate::api::CpusLimits {
|
||||||
|
crate::api::CpusLimits {
|
||||||
|
cpus: self.cpus.iter().map(|x| x.limits()).collect(),
|
||||||
|
count: self.cpus.len(),
|
||||||
|
smt_capable: self.smt_capable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> Vec<crate::persist::CpuJson> {
|
||||||
|
self.cpus.iter().map(|x| x.to_owned().into()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpus(&mut self) -> Vec<&mut dyn TCpu> {
|
||||||
|
self.cpus.iter_mut().map(|x| x as &mut dyn TCpu).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.cpus.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Cpu {
|
||||||
|
pub online: bool,
|
||||||
|
pub clock_limits: Option<MinMax<u64>>,
|
||||||
|
pub governor: String,
|
||||||
|
index: usize,
|
||||||
|
state: crate::state::steam_deck::Cpu,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||||
|
const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||||
|
|
||||||
|
impl Cpu {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
|
||||||
|
match version {
|
||||||
|
0 => Self {
|
||||||
|
online: other.online,
|
||||||
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
|
governor: other.governor,
|
||||||
|
index: i,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
online: other.online,
|
||||||
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
|
governor: other.governor,
|
||||||
|
index: i,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||||
|
// set cpu online/offline
|
||||||
|
if self.index != 0 && self.state.do_set_online { // cpu0 cannot be disabled
|
||||||
|
let online_path = cpu_online_path(self.index);
|
||||||
|
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!("Failed to write to `{}`: {}", &online_path, e),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// set 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
|
||||||
|
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `manual` to `{}`: {}",
|
||||||
|
CPU_FORCE_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::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
|
||||||
|
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(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
// min clock
|
||||||
|
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(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
} else if self.state.clock_limits_set || self.state.is_resuming {
|
||||||
|
self.state.clock_limits_set = false;
|
||||||
|
// disable manual clock limits
|
||||||
|
log::debug!("Setting CPU {} to default clockspeed", self.index);
|
||||||
|
// 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!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::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: crate::settings::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: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// set governor
|
||||||
|
if self.index == 0 || self.online {
|
||||||
|
let governor_path = cpu_governor_path(self.index);
|
||||||
|
usdpl_back::api::files::write_single(&governor_path, &self.governor).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&self.governor, &governor_path, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp_all(&mut self) {
|
||||||
|
let min = Self::min();
|
||||||
|
let max = Self::max();
|
||||||
|
if let Some(clock_limits) = &mut self.clock_limits {
|
||||||
|
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||||
|
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||||
|
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||||
|
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_sys(cpu_index: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
online: usdpl_back::api::files::read_single(cpu_online_path(cpu_index)).unwrap_or(1u8) != 0,
|
||||||
|
clock_limits: None,
|
||||||
|
governor: usdpl_back::api::files::read_single(cpu_governor_path(cpu_index))
|
||||||
|
.unwrap_or("schedutil".to_owned()),
|
||||||
|
index: cpu_index,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn limits(&self) -> crate::api::CpuLimits {
|
||||||
|
let max = Self::max();
|
||||||
|
let max_clocks = max.clock_limits.unwrap();
|
||||||
|
|
||||||
|
let min = Self::min();
|
||||||
|
let min_clocks = min.clock_limits.unwrap();
|
||||||
|
crate::api::CpuLimits {
|
||||||
|
clock_min_limits: Some(RangeLimit {
|
||||||
|
min: min_clocks.min,
|
||||||
|
max: max_clocks.min
|
||||||
|
}),
|
||||||
|
clock_max_limits: Some(RangeLimit {
|
||||||
|
min: min_clocks.max,
|
||||||
|
max: max_clocks.max
|
||||||
|
}),
|
||||||
|
clock_step: 100,
|
||||||
|
governors: self.governors(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn governors(&self) -> Vec<String> {
|
||||||
|
// NOTE: this eats errors
|
||||||
|
let gov_str: String = match usdpl_back::api::files::read_single(cpu_available_governors_path(self.index)) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err((Some(e), None)) => {
|
||||||
|
log::warn!("Error getting available CPU governors: {}", e);
|
||||||
|
return vec![];
|
||||||
|
},
|
||||||
|
Err((None, Some(e))) => {
|
||||||
|
log::warn!("Error getting available CPU governors: {}", e);
|
||||||
|
return vec![];
|
||||||
|
},
|
||||||
|
Err(_) => return vec![],
|
||||||
|
};
|
||||||
|
gov_str.split(' ').map(|s| s.to_owned()).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<CpuJson> for Cpu {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> CpuJson {
|
||||||
|
CpuJson {
|
||||||
|
online: self.online,
|
||||||
|
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||||
|
governor: self.governor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Cpu {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
self.clamp_all();
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Cpu {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
let mut copy = self.clone();
|
||||||
|
copy.state.is_resuming = true;
|
||||||
|
copy.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCpu for Cpu {
|
||||||
|
fn online(&mut self) -> &mut bool {
|
||||||
|
&mut self.online
|
||||||
|
}
|
||||||
|
|
||||||
|
fn governor(&mut self, governor: String) {
|
||||||
|
self.governor = governor;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_governor(&self) -> &'_ str {
|
||||||
|
&self.governor
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, limits: Option<MinMax<u64>>) {
|
||||||
|
self.clock_limits = limits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||||
|
self.clock_limits.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsRange for Cpu {
|
||||||
|
#[inline]
|
||||||
|
fn max() -> Self {
|
||||||
|
Self {
|
||||||
|
online: true,
|
||||||
|
clock_limits: Some(MinMax {
|
||||||
|
max: 3500,
|
||||||
|
min: 3500,
|
||||||
|
}),
|
||||||
|
governor: "schedutil".to_owned(),
|
||||||
|
index: usize::MAX,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min() -> Self {
|
||||||
|
Self {
|
||||||
|
online: false,
|
||||||
|
clock_limits: Some(MinMax { max: 500, min: 1400 }),
|
||||||
|
governor: "schedutil".to_owned(),
|
||||||
|
index: usize::MIN,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cpu_online_path(index: usize) -> String {
|
||||||
|
format!("/sys/devices/system/cpu/cpu{}/online", index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cpu_governor_path(index: usize) -> String {
|
||||||
|
format!(
|
||||||
|
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_governor",
|
||||||
|
index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cpu_available_governors_path(index: usize) -> String {
|
||||||
|
format!(
|
||||||
|
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_available_governors",
|
||||||
|
index
|
||||||
|
)
|
||||||
|
}
|
308
backend/src/settings/steam_deck/gpu.rs
Normal file
308
backend/src/settings/steam_deck/gpu.rs
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::api::RangeLimit;
|
||||||
|
use crate::settings::MinMax;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
|
use crate::settings::TGpu;
|
||||||
|
use crate::persist::GpuJson;
|
||||||
|
|
||||||
|
const SLOW_PPT: u8 = 1;
|
||||||
|
const FAST_PPT: u8 = 2;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Gpu {
|
||||||
|
pub fast_ppt: Option<u64>,
|
||||||
|
pub slow_ppt: Option<u64>,
|
||||||
|
pub clock_limits: Option<MinMax<u64>>,
|
||||||
|
pub slow_memory: bool,
|
||||||
|
state: crate::state::steam_deck::Gpu,
|
||||||
|
}
|
||||||
|
|
||||||
|
// same as CPU
|
||||||
|
const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||||
|
const GPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||||
|
const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk";
|
||||||
|
|
||||||
|
impl Gpu {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_json(other: GpuJson, version: u64) -> Self {
|
||||||
|
match version {
|
||||||
|
0 => Self {
|
||||||
|
fast_ppt: other.fast_ppt,
|
||||||
|
slow_ppt: other.slow_ppt,
|
||||||
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
|
slow_memory: other.slow_memory,
|
||||||
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
fast_ppt: other.fast_ppt,
|
||||||
|
slow_ppt: other.slow_ppt,
|
||||||
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
|
slow_memory: other.slow_memory,
|
||||||
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||||
|
// set fast PPT
|
||||||
|
if let Some(fast_ppt) = &self.fast_ppt {
|
||||||
|
let fast_ppt_path = gpu_power_path(FAST_PPT);
|
||||||
|
usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
fast_ppt, &fast_ppt_path, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// set slow PPT
|
||||||
|
if let Some(slow_ppt) = &self.slow_ppt {
|
||||||
|
let slow_ppt_path = gpu_power_path(SLOW_PPT);
|
||||||
|
usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
slow_ppt, &slow_ppt_path, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// settings using force_performance_level
|
||||||
|
let mode: String = usdpl_back::api::files::read_single(GPU_FORCE_LIMITS_PATH.to_owned()).unwrap();
|
||||||
|
if mode != "manual" {
|
||||||
|
// set manual control
|
||||||
|
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `manual` to `{}`: {}",
|
||||||
|
GPU_FORCE_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// enable/disable 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: crate::settings::SettingVariant::Gpu,
|
||||||
|
})?;
|
||||||
|
if let Some(clock_limits) = &self.clock_limits {
|
||||||
|
// set clock limits
|
||||||
|
self.state.clock_limits_set = true;
|
||||||
|
// 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!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::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: crate::settings::SettingVariant::Gpu,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
} else if self.state.clock_limits_set || self.state.is_resuming {
|
||||||
|
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: crate::settings::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: crate::settings::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: crate::settings::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp_all(&mut self) {
|
||||||
|
let min = Self::min();
|
||||||
|
let max = Self::max();
|
||||||
|
if let Some(fast_ppt) = &mut self.fast_ppt {
|
||||||
|
*fast_ppt = (*fast_ppt).clamp(
|
||||||
|
*min.fast_ppt.as_ref().unwrap(),
|
||||||
|
*max.fast_ppt.as_ref().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(slow_ppt) = &mut self.slow_ppt {
|
||||||
|
*slow_ppt = (*slow_ppt).clamp(
|
||||||
|
*min.slow_ppt.as_ref().unwrap(),
|
||||||
|
*max.slow_ppt.as_ref().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(clock_limits) = &mut self.clock_limits {
|
||||||
|
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||||
|
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||||
|
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||||
|
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default() -> Self {
|
||||||
|
Self {
|
||||||
|
fast_ppt: None,
|
||||||
|
slow_ppt: None,
|
||||||
|
clock_limits: None,
|
||||||
|
slow_memory: false,
|
||||||
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<GpuJson> for Gpu {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> GpuJson {
|
||||||
|
GpuJson {
|
||||||
|
fast_ppt: self.fast_ppt,
|
||||||
|
slow_ppt: self.slow_ppt,
|
||||||
|
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||||
|
slow_memory: self.slow_memory,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Gpu {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
self.clamp_all();
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Gpu {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
let mut copy = self.clone();
|
||||||
|
copy.state.is_resuming = true;
|
||||||
|
copy.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsRange for Gpu {
|
||||||
|
#[inline]
|
||||||
|
fn max() -> Self {
|
||||||
|
Self {
|
||||||
|
fast_ppt: Some(30_000_000),
|
||||||
|
slow_ppt: Some(29_000_000),
|
||||||
|
clock_limits: Some(MinMax {
|
||||||
|
min: 1600,
|
||||||
|
max: 1600,
|
||||||
|
}),
|
||||||
|
slow_memory: false,
|
||||||
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min() -> Self {
|
||||||
|
Self {
|
||||||
|
fast_ppt: Some(0),
|
||||||
|
slow_ppt: Some(1000000),
|
||||||
|
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
||||||
|
slow_memory: true,
|
||||||
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PPT_DIVISOR: u64 = 1_000_000;
|
||||||
|
|
||||||
|
impl TGpu for Gpu {
|
||||||
|
fn limits(&self) -> crate::api::GpuLimits {
|
||||||
|
let max = Self::max();
|
||||||
|
let max_clock_limits = max.clock_limits.unwrap();
|
||||||
|
|
||||||
|
let min = Self::min();
|
||||||
|
let min_clock_limits = min.clock_limits.unwrap();
|
||||||
|
crate::api::GpuLimits {
|
||||||
|
fast_ppt_limits: Some(RangeLimit {
|
||||||
|
min: min.fast_ppt.unwrap() / PPT_DIVISOR,
|
||||||
|
max: max.fast_ppt.unwrap() / PPT_DIVISOR,
|
||||||
|
}),
|
||||||
|
slow_ppt_limits: Some(RangeLimit {
|
||||||
|
min: min.slow_ppt.unwrap() / PPT_DIVISOR,
|
||||||
|
max: max.slow_ppt.unwrap() / PPT_DIVISOR,
|
||||||
|
}),
|
||||||
|
ppt_step: 1,
|
||||||
|
tdp_limits: None,
|
||||||
|
tdp_boost_limits: None,
|
||||||
|
tdp_step: 42,
|
||||||
|
clock_min_limits: Some(RangeLimit {
|
||||||
|
min: min_clock_limits.min,
|
||||||
|
max: max_clock_limits.max,
|
||||||
|
}),
|
||||||
|
clock_max_limits: Some(RangeLimit {
|
||||||
|
min: min_clock_limits.min,
|
||||||
|
max: max_clock_limits.max,
|
||||||
|
}),
|
||||||
|
clock_step: 100,
|
||||||
|
memory_control_capable: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::GpuJson {
|
||||||
|
self.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ppt(&mut self, fast: Option<u64>, slow: Option<u64>) {
|
||||||
|
self.fast_ppt = fast.map(|x| x * PPT_DIVISOR);
|
||||||
|
self.slow_ppt = slow.map(|x| x * PPT_DIVISOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ppt(&self) -> (Option<u64>, Option<u64>) {
|
||||||
|
(self.fast_ppt.map(|x| x / PPT_DIVISOR), self.slow_ppt.map(|x| x / PPT_DIVISOR))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, limits: Option<MinMax<u64>>) {
|
||||||
|
self.clock_limits = limits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||||
|
self.clock_limits.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slow_memory(&mut self) -> &mut bool {
|
||||||
|
&mut self.slow_memory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn gpu_power_path(power_number: u8) -> String {
|
||||||
|
format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number)
|
||||||
|
}
|
7
backend/src/settings/steam_deck/mod.rs
Normal file
7
backend/src/settings/steam_deck/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
mod battery;
|
||||||
|
mod cpu;
|
||||||
|
mod gpu;
|
||||||
|
|
||||||
|
pub use battery::Battery;
|
||||||
|
pub use cpu::{Cpu, Cpus};
|
||||||
|
pub use gpu::Gpu;
|
216
backend/src/settings/steam_deck_adv/battery.rs
Normal file
216
backend/src/settings/steam_deck_adv/battery.rs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::api::RangeLimit;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
|
use crate::settings::TBattery;
|
||||||
|
use crate::persist::BatteryJson;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Battery {
|
||||||
|
pub charge_rate: Option<u64>,
|
||||||
|
state: crate::state::steam_deck::Battery,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BATTERY_VOLTAGE: f64 = 7.7;
|
||||||
|
|
||||||
|
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
|
||||||
|
const BATTERY_CURRENT_NOW_PATH: &str = "/sys/class/power_supply/BAT1/current_now"; // read-only
|
||||||
|
const BATTERY_CHARGE_NOW_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_now"; // read-only
|
||||||
|
const BATTERY_CHARGE_FULL_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_full"; // read-only
|
||||||
|
const BATTERY_CHARGE_DESIGN_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_full_design"; // read-only
|
||||||
|
|
||||||
|
impl Battery {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_json(other: BatteryJson, version: u64) -> Self {
|
||||||
|
match version {
|
||||||
|
0 => Self {
|
||||||
|
charge_rate: other.charge_rate,
|
||||||
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
charge_rate: other.charge_rate,
|
||||||
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||||
|
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(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||||
|
setting: crate::settings::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: crate::settings::SettingVariant::Battery,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp_all(&mut self) {
|
||||||
|
let min = Self::min();
|
||||||
|
let max = Self::max();
|
||||||
|
if let Some(charge_rate) = &mut self.charge_rate {
|
||||||
|
*charge_rate = (*charge_rate).clamp(min.charge_rate.unwrap(), max.charge_rate.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_current_now() -> Result<u64, SettingError> {
|
||||||
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
|
||||||
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err(_) => panic!(
|
||||||
|
"Invalid error while reading from `{}`",
|
||||||
|
BATTERY_CURRENT_NOW_PATH
|
||||||
|
),
|
||||||
|
// this value is in uA, while it's set in mA
|
||||||
|
// so convert this to mA for consistency
|
||||||
|
Ok(val) => Ok(val / 1000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_charge_now() -> Result<f64, SettingError> {
|
||||||
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_NOW_PATH) {
|
||||||
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err(_) => panic!(
|
||||||
|
"Invalid error while reading from `{}`",
|
||||||
|
BATTERY_CHARGE_NOW_PATH
|
||||||
|
),
|
||||||
|
// convert to Wh
|
||||||
|
Ok(val) => Ok((val as f64) / 1000000.0 * BATTERY_VOLTAGE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_charge_full() -> Result<f64, SettingError> {
|
||||||
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_FULL_PATH) {
|
||||||
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err(_) => panic!(
|
||||||
|
"Invalid error while reading from `{}`",
|
||||||
|
BATTERY_CHARGE_NOW_PATH
|
||||||
|
),
|
||||||
|
// convert to Wh
|
||||||
|
Ok(val) => Ok((val as f64) / 1000000.0 * BATTERY_VOLTAGE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_charge_design() -> Result<f64, SettingError> {
|
||||||
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_DESIGN_PATH) {
|
||||||
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
||||||
|
setting: crate::settings::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err(_) => panic!(
|
||||||
|
"Invalid error while reading from `{}`",
|
||||||
|
BATTERY_CHARGE_NOW_PATH
|
||||||
|
),
|
||||||
|
// convert to Wh
|
||||||
|
Ok(val) => Ok((val as f64) / 1000000.0 * BATTERY_VOLTAGE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default() -> Self {
|
||||||
|
Self {
|
||||||
|
charge_rate: None,
|
||||||
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<BatteryJson> for Battery {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> BatteryJson {
|
||||||
|
BatteryJson {
|
||||||
|
charge_rate: self.charge_rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Battery {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
self.clamp_all();
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Battery {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
self.clone().set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsRange for Battery {
|
||||||
|
#[inline]
|
||||||
|
fn max() -> Self {
|
||||||
|
Self {
|
||||||
|
charge_rate: Some(2500),
|
||||||
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min() -> Self {
|
||||||
|
Self {
|
||||||
|
charge_rate: Some(250),
|
||||||
|
state: crate::state::steam_deck::Battery::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TBattery for Battery {
|
||||||
|
fn limits(&self) -> crate::api::BatteryLimits {
|
||||||
|
let max = Self::max();
|
||||||
|
let min = Self::min();
|
||||||
|
crate::api::BatteryLimits {
|
||||||
|
charge_rate: Some(RangeLimit{
|
||||||
|
min: min.charge_rate.unwrap(),
|
||||||
|
max: max.charge_rate.unwrap(),
|
||||||
|
}),
|
||||||
|
charge_step: 50,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::BatteryJson {
|
||||||
|
self.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn charge_rate(&mut self, rate: Option<u64>) {
|
||||||
|
self.charge_rate = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_charge_rate(&self) -> Option<u64> {
|
||||||
|
self.charge_rate
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
|
|
||||||
use super::MinMax;
|
use crate::api::RangeLimit;
|
||||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
use crate::settings::MinMax;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
|
use crate::settings::{TCpus, TCpu};
|
||||||
use crate::persist::CpuJson;
|
use crate::persist::CpuJson;
|
||||||
|
|
||||||
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
||||||
|
@ -25,7 +27,7 @@ impl OnSet for Cpus {
|
||||||
"Failed to write `on` to `{}`: {}",
|
"Failed to write `on` to `{}`: {}",
|
||||||
CPU_SMT_PATH, e
|
CPU_SMT_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,7 +37,7 @@ impl OnSet for Cpus {
|
||||||
"Failed to write `off` to `{}`: {}",
|
"Failed to write `off` to `{}`: {}",
|
||||||
CPU_SMT_PATH, e
|
CPU_SMT_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -129,13 +131,35 @@ impl Cpus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TCpus for Cpus {
|
||||||
|
fn limits(&self) -> crate::api::CpusLimits {
|
||||||
|
crate::api::CpusLimits {
|
||||||
|
cpus: self.cpus.iter().map(|x| x.limits()).collect(),
|
||||||
|
count: self.cpus.len(),
|
||||||
|
smt_capable: self.smt_capable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> Vec<crate::persist::CpuJson> {
|
||||||
|
self.cpus.iter().map(|x| x.to_owned().into()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpus(&mut self) -> Vec<&mut dyn TCpu> {
|
||||||
|
self.cpus.iter_mut().map(|x| x as &mut dyn TCpu).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.cpus.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cpu {
|
pub struct Cpu {
|
||||||
pub online: bool,
|
pub online: bool,
|
||||||
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,
|
state: crate::state::steam_deck::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";
|
||||||
|
@ -150,14 +174,14 @@ 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(),
|
state: crate::state::steam_deck::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(),
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +193,7 @@ impl Cpu {
|
||||||
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
|
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
|
||||||
SettingError {
|
SettingError {
|
||||||
msg: format!("Failed to write to `{}`: {}", &online_path, e),
|
msg: format!("Failed to write to `{}`: {}", &online_path, e),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -184,7 +208,7 @@ impl Cpu {
|
||||||
"Failed to write `manual` to `{}`: {}",
|
"Failed to write `manual` to `{}`: {}",
|
||||||
CPU_FORCE_LIMITS_PATH, e
|
CPU_FORCE_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -199,7 +223,7 @@ impl Cpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
// min clock
|
// min clock
|
||||||
|
@ -210,7 +234,7 @@ impl Cpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
} else if self.state.clock_limits_set || self.state.is_resuming {
|
} else if self.state.clock_limits_set || self.state.is_resuming {
|
||||||
|
@ -225,7 +249,7 @@ impl Cpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
// min clock
|
// min clock
|
||||||
|
@ -236,7 +260,7 @@ impl Cpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -244,7 +268,7 @@ impl Cpu {
|
||||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
|
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
|
||||||
SettingError {
|
SettingError {
|
||||||
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
|
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -257,7 +281,7 @@ impl Cpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&self.governor, &governor_path, e
|
&self.governor, &governor_path, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Cpu,
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -275,14 +299,34 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_sys(index: usize) -> Self {
|
fn from_sys(cpu_index: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
online: usdpl_back::api::files::read_single(cpu_online_path(index)).unwrap_or(1u8) != 0,
|
online: usdpl_back::api::files::read_single(cpu_online_path(cpu_index)).unwrap_or(1u8) != 0,
|
||||||
clock_limits: None,
|
clock_limits: None,
|
||||||
governor: usdpl_back::api::files::read_single(cpu_governor_path(index))
|
governor: usdpl_back::api::files::read_single(cpu_governor_path(cpu_index))
|
||||||
.unwrap_or("schedutil".to_owned()),
|
.unwrap_or("schedutil".to_owned()),
|
||||||
index: index,
|
index: cpu_index,
|
||||||
state: crate::state::Cpu::default(),
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn limits(&self) -> crate::api::CpuLimits {
|
||||||
|
let max = Self::max();
|
||||||
|
let max_clocks = max.clock_limits.unwrap();
|
||||||
|
|
||||||
|
let min = Self::min();
|
||||||
|
let min_clocks = min.clock_limits.unwrap();
|
||||||
|
crate::api::CpuLimits {
|
||||||
|
clock_min_limits: Some(RangeLimit {
|
||||||
|
min: min_clocks.min,
|
||||||
|
max: max_clocks.min
|
||||||
|
}),
|
||||||
|
clock_max_limits: Some(RangeLimit {
|
||||||
|
min: min_clocks.max,
|
||||||
|
max: max_clocks.max
|
||||||
|
}),
|
||||||
|
clock_step: 100,
|
||||||
|
governors: vec![], // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,6 +357,28 @@ impl OnResume for Cpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TCpu for Cpu {
|
||||||
|
fn online(&mut self) -> &mut bool {
|
||||||
|
&mut self.online
|
||||||
|
}
|
||||||
|
|
||||||
|
fn governor(&mut self, governor: String) {
|
||||||
|
self.governor = governor;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_governor(&self) -> &'_ str {
|
||||||
|
&self.governor
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, limits: Option<MinMax<u64>>) {
|
||||||
|
self.clock_limits = limits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||||
|
self.clock_limits.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SettingsRange for Cpu {
|
impl SettingsRange for Cpu {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn max() -> Self {
|
fn max() -> Self {
|
||||||
|
@ -324,7 +390,7 @@ impl SettingsRange for Cpu {
|
||||||
}),
|
}),
|
||||||
governor: "schedutil".to_owned(),
|
governor: "schedutil".to_owned(),
|
||||||
index: usize::MAX,
|
index: usize::MAX,
|
||||||
state: crate::state::Cpu::default(),
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +401,7 @@ impl SettingsRange for Cpu {
|
||||||
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: usize::MIN,
|
index: usize::MIN,
|
||||||
state: crate::state::Cpu::default(),
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
|
|
||||||
use super::MinMax;
|
use crate::api::RangeLimit;
|
||||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
use crate::settings::MinMax;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
|
use crate::settings::TGpu;
|
||||||
use crate::persist::GpuJson;
|
use crate::persist::GpuJson;
|
||||||
|
|
||||||
const SLOW_PPT: u8 = 1;
|
const SLOW_PPT: u8 = 1;
|
||||||
|
@ -13,7 +15,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,
|
state: crate::state::steam_deck::Gpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
// same as CPU
|
// same as CPU
|
||||||
|
@ -30,14 +32,14 @@ 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(),
|
state: crate::state::steam_deck::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(),
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +54,7 @@ impl Gpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
fast_ppt, &fast_ppt_path, e
|
fast_ppt, &fast_ppt_path, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +67,7 @@ impl Gpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
slow_ppt, &slow_ppt_path, e
|
slow_ppt, &slow_ppt_path, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +81,7 @@ impl Gpu {
|
||||||
"Failed to write `manual` to `{}`: {}",
|
"Failed to write `manual` to `{}`: {}",
|
||||||
GPU_FORCE_LIMITS_PATH, e
|
GPU_FORCE_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,7 @@ impl Gpu {
|
||||||
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
|
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
|
||||||
.map_err(|e| SettingError {
|
.map_err(|e| SettingError {
|
||||||
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
|
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
})?;
|
})?;
|
||||||
if let Some(clock_limits) = &self.clock_limits {
|
if let Some(clock_limits) = &self.clock_limits {
|
||||||
// set clock limits
|
// set clock limits
|
||||||
|
@ -100,7 +102,7 @@ impl Gpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
// min clock
|
// min clock
|
||||||
|
@ -111,7 +113,7 @@ impl Gpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
} else if self.state.clock_limits_set || self.state.is_resuming {
|
} else if self.state.clock_limits_set || self.state.is_resuming {
|
||||||
|
@ -125,7 +127,7 @@ impl Gpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
// min clock
|
// min clock
|
||||||
|
@ -136,7 +138,7 @@ impl Gpu {
|
||||||
"Failed to write `{}` to `{}`: {}",
|
"Failed to write `{}` to `{}`: {}",
|
||||||
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
||||||
),
|
),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +146,7 @@ impl Gpu {
|
||||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
|
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
|
||||||
SettingError {
|
SettingError {
|
||||||
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
|
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
|
||||||
setting: super::SettingVariant::Gpu,
|
setting: crate::settings::SettingVariant::Gpu,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -180,7 +182,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(),
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,14 +218,14 @@ impl SettingsRange for Gpu {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn max() -> Self {
|
fn max() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fast_ppt: Some(30000000),
|
fast_ppt: Some(30_000_000),
|
||||||
slow_ppt: Some(29000000),
|
slow_ppt: Some(29_000_000),
|
||||||
clock_limits: Some(MinMax {
|
clock_limits: Some(MinMax {
|
||||||
min: 1600,
|
min: 1600,
|
||||||
max: 1600,
|
max: 1600,
|
||||||
}),
|
}),
|
||||||
slow_memory: false,
|
slow_memory: false,
|
||||||
state: crate::state::Gpu::default(),
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,11 +236,70 @@ 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(),
|
state: crate::state::steam_deck::Gpu::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TGpu for Gpu {
|
||||||
|
fn limits(&self) -> crate::api::GpuLimits {
|
||||||
|
let max = Self::max();
|
||||||
|
let max_clock_limits = max.clock_limits.unwrap();
|
||||||
|
|
||||||
|
let min = Self::min();
|
||||||
|
let min_clock_limits = min.clock_limits.unwrap();
|
||||||
|
crate::api::GpuLimits {
|
||||||
|
fast_ppt_limits: Some(RangeLimit {
|
||||||
|
min: min.fast_ppt.unwrap(),
|
||||||
|
max: max.fast_ppt.unwrap(),
|
||||||
|
}),
|
||||||
|
slow_ppt_limits: Some(RangeLimit {
|
||||||
|
min: min.slow_ppt.unwrap(),
|
||||||
|
max: max.slow_ppt.unwrap(),
|
||||||
|
}),
|
||||||
|
ppt_step: 1_000_000,
|
||||||
|
tdp_limits: None,
|
||||||
|
tdp_boost_limits: None,
|
||||||
|
tdp_step: 42,
|
||||||
|
clock_min_limits: Some(RangeLimit {
|
||||||
|
min: min_clock_limits.min,
|
||||||
|
max: max_clock_limits.max,
|
||||||
|
}),
|
||||||
|
clock_max_limits: Some(RangeLimit {
|
||||||
|
min: min_clock_limits.min,
|
||||||
|
max: max_clock_limits.max,
|
||||||
|
}),
|
||||||
|
clock_step: 100,
|
||||||
|
memory_control_capable: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::GpuJson {
|
||||||
|
self.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ppt(&mut self, fast: Option<u64>, slow: Option<u64>) {
|
||||||
|
self.fast_ppt = fast;
|
||||||
|
self.slow_ppt = slow;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ppt(&self) -> (Option<u64>, Option<u64>) {
|
||||||
|
(self.fast_ppt, self.slow_ppt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, limits: Option<MinMax<u64>>) {
|
||||||
|
self.clock_limits = limits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||||
|
self.clock_limits.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slow_memory(&mut self) -> &mut bool {
|
||||||
|
&mut self.slow_memory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn gpu_power_path(power_number: u8) -> String {
|
fn gpu_power_path(power_number: u8) -> String {
|
||||||
format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number)
|
format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number)
|
7
backend/src/settings/steam_deck_adv/mod.rs
Normal file
7
backend/src/settings/steam_deck_adv/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
mod battery;
|
||||||
|
mod cpu;
|
||||||
|
mod gpu;
|
||||||
|
|
||||||
|
pub use battery::Battery;
|
||||||
|
pub use cpu::{Cpu, Cpus};
|
||||||
|
pub use gpu::Gpu;
|
|
@ -1,4 +1,6 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
use super::SettingError;
|
use super::SettingError;
|
||||||
|
use super::MinMax;
|
||||||
|
|
||||||
pub trait OnSet {
|
pub trait OnSet {
|
||||||
fn on_set(&mut self) -> Result<(), SettingError>;
|
fn on_set(&mut self) -> Result<(), SettingError>;
|
||||||
|
@ -12,3 +14,69 @@ pub trait SettingsRange {
|
||||||
fn max() -> Self;
|
fn max() -> Self;
|
||||||
fn min() -> Self;
|
fn min() -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TGpu: OnResume + OnSet + Debug + Send {
|
||||||
|
fn limits(&self) -> crate::api::GpuLimits;
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::GpuJson;
|
||||||
|
|
||||||
|
fn ppt(&mut self, fast: Option<u64>, slow: Option<u64>);
|
||||||
|
|
||||||
|
fn get_ppt(&self) -> (Option<u64>, Option<u64>);
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, limits: Option<MinMax<u64>>);
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>>;
|
||||||
|
|
||||||
|
fn slow_memory(&mut self) -> &mut bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TCpus: OnResume + OnSet + Debug + Send {
|
||||||
|
fn limits(&self) -> crate::api::CpusLimits;
|
||||||
|
|
||||||
|
fn json(&self) -> Vec<crate::persist::CpuJson>;
|
||||||
|
|
||||||
|
fn cpus(&mut self) -> Vec<&mut dyn TCpu>;
|
||||||
|
|
||||||
|
fn len(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TCpu: Debug + Send {
|
||||||
|
fn online(&mut self) -> &mut bool;
|
||||||
|
|
||||||
|
fn governor(&mut self, governor: String);
|
||||||
|
|
||||||
|
fn get_governor(&self) -> &'_ str;
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, limits: Option<MinMax<u64>>);
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TGeneral: OnResume + OnSet + Debug + Send {
|
||||||
|
fn limits(&self) -> crate::api::GeneralLimits;
|
||||||
|
|
||||||
|
fn get_persistent(&self) -> bool;
|
||||||
|
|
||||||
|
fn persistent(&mut self) -> &'_ mut bool;
|
||||||
|
|
||||||
|
fn get_path(&self) -> &'_ std::path::Path;
|
||||||
|
|
||||||
|
fn path(&mut self, path: std::path::PathBuf);
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'_ str;
|
||||||
|
|
||||||
|
fn name(&mut self, name: String);
|
||||||
|
|
||||||
|
fn provider(&self) -> crate::persist::DriverJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TBattery: OnResume + OnSet + Debug + Send {
|
||||||
|
fn limits(&self) -> crate::api::BatteryLimits;
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::BatteryJson;
|
||||||
|
|
||||||
|
fn charge_rate(&mut self, rate: Option<u64>);
|
||||||
|
|
||||||
|
fn get_charge_rate(&self) -> Option<u64>;
|
||||||
|
}
|
||||||
|
|
49
backend/src/settings/unknown/battery.rs
Normal file
49
backend/src/settings/unknown/battery.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError};
|
||||||
|
use crate::settings::TBattery;
|
||||||
|
use crate::persist::BatteryJson;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Battery;
|
||||||
|
|
||||||
|
impl Into<BatteryJson> for Battery {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> BatteryJson {
|
||||||
|
BatteryJson {
|
||||||
|
charge_rate: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Battery {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Battery {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TBattery for Battery {
|
||||||
|
fn limits(&self) -> crate::api::BatteryLimits {
|
||||||
|
crate::api::BatteryLimits {
|
||||||
|
charge_rate: None,
|
||||||
|
charge_step: 50,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::BatteryJson {
|
||||||
|
self.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn charge_rate(&mut self, _rate: Option<u64>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_charge_rate(&self) -> Option<u64> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
289
backend/src/settings/unknown/cpu.rs
Normal file
289
backend/src/settings/unknown/cpu.rs
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::settings::MinMax;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError};
|
||||||
|
use crate::settings::{TCpus, TCpu};
|
||||||
|
use crate::persist::CpuJson;
|
||||||
|
|
||||||
|
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
||||||
|
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Cpus {
|
||||||
|
pub cpus: Vec<Cpu>,
|
||||||
|
pub smt: bool,
|
||||||
|
pub smt_capable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Cpus {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
if self.smt_capable {
|
||||||
|
// toggle SMT
|
||||||
|
if self.smt {
|
||||||
|
usdpl_back::api::files::write_single(CPU_SMT_PATH, "on").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `on` to `{}`: {}",
|
||||||
|
CPU_SMT_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
usdpl_back::api::files::write_single(CPU_SMT_PATH, "off").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `off` to `{}`: {}",
|
||||||
|
CPU_SMT_PATH, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i, cpu) in self.cpus.as_mut_slice().iter_mut().enumerate() {
|
||||||
|
cpu.state.do_set_online = self.smt || i % 2 == 0;
|
||||||
|
cpu.on_set()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Cpus {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
for cpu in &self.cpus {
|
||||||
|
cpu.on_resume()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cpus {
|
||||||
|
pub fn cpu_count() -> Option<usize> {
|
||||||
|
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
|
||||||
|
.unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */);
|
||||||
|
if let Some(dash_index) = data.find('-') {
|
||||||
|
let data = data.split_off(dash_index + 1);
|
||||||
|
if let Ok(max_cpu) = data.parse::<usize>() {
|
||||||
|
return Some(max_cpu + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_smt_capabilities() -> (bool, bool) {
|
||||||
|
match usdpl_back::api::files::read_single::<_, String, _>(CPU_SMT_PATH) {
|
||||||
|
Ok(val) => (val.trim().to_lowercase() == "on", true),
|
||||||
|
Err(_) => (false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default() -> Self {
|
||||||
|
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::from_sys(i));
|
||||||
|
}
|
||||||
|
let (smt_status, can_smt) = Self::system_smt_capabilities();
|
||||||
|
Self {
|
||||||
|
cpus: sys_cpus,
|
||||||
|
smt: smt_status,
|
||||||
|
smt_capable: can_smt,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
cpus: vec![],
|
||||||
|
smt: false,
|
||||||
|
smt_capable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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());
|
||||||
|
let max_cpus = Self::cpu_count();
|
||||||
|
for (i, cpu) in other.drain(..).enumerate() {
|
||||||
|
// prevent having more CPUs than available
|
||||||
|
if let Some(max_cpus) = max_cpus {
|
||||||
|
if i == max_cpus {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(Cpu::from_json(cpu, version, i));
|
||||||
|
}
|
||||||
|
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: true,
|
||||||
|
smt_capable: can_smt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCpus for Cpus {
|
||||||
|
fn limits(&self) -> crate::api::CpusLimits {
|
||||||
|
crate::api::CpusLimits {
|
||||||
|
cpus: self.cpus.iter().map(|x| x.limits()).collect(),
|
||||||
|
count: self.cpus.len(),
|
||||||
|
smt_capable: self.smt_capable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> Vec<crate::persist::CpuJson> {
|
||||||
|
self.cpus.iter().map(|x| x.to_owned().into()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpus(&mut self) -> Vec<&mut dyn TCpu> {
|
||||||
|
self.cpus.iter_mut().map(|x| x as &mut dyn TCpu).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.cpus.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Cpu {
|
||||||
|
pub online: bool,
|
||||||
|
pub governor: String,
|
||||||
|
index: usize,
|
||||||
|
state: crate::state::steam_deck::Cpu,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Cpu {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
|
||||||
|
match version {
|
||||||
|
0 => Self {
|
||||||
|
online: other.online,
|
||||||
|
governor: other.governor,
|
||||||
|
index: i,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
online: other.online,
|
||||||
|
governor: other.governor,
|
||||||
|
index: i,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||||
|
// set cpu online/offline
|
||||||
|
if self.index != 0 && self.state.do_set_online { // cpu0 cannot be disabled
|
||||||
|
let online_path = cpu_online_path(self.index);
|
||||||
|
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!("Failed to write to `{}`: {}", &online_path, e),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set governor
|
||||||
|
if self.index == 0 || self.online {
|
||||||
|
let governor_path = cpu_governor_path(self.index);
|
||||||
|
usdpl_back::api::files::write_single(&governor_path, &self.governor).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&self.governor, &governor_path, e
|
||||||
|
),
|
||||||
|
setting: crate::settings::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_sys(cpu_index: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
online: usdpl_back::api::files::read_single(cpu_online_path(cpu_index)).unwrap_or(1u8) != 0,
|
||||||
|
governor: usdpl_back::api::files::read_single(cpu_governor_path(cpu_index))
|
||||||
|
.unwrap_or("schedutil".to_owned()),
|
||||||
|
index: cpu_index,
|
||||||
|
state: crate::state::steam_deck::Cpu::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn limits(&self) -> crate::api::CpuLimits {
|
||||||
|
crate::api::CpuLimits {
|
||||||
|
clock_min_limits: None,
|
||||||
|
clock_max_limits: None,
|
||||||
|
clock_step: 100,
|
||||||
|
governors: vec![], // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<CpuJson> for Cpu {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> CpuJson {
|
||||||
|
CpuJson {
|
||||||
|
online: self.online,
|
||||||
|
clock_limits: None,
|
||||||
|
governor: self.governor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Cpu {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
//self.clamp_all();
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Cpu {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
let mut copy = self.clone();
|
||||||
|
copy.state.is_resuming = true;
|
||||||
|
copy.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCpu for Cpu {
|
||||||
|
fn online(&mut self) -> &mut bool {
|
||||||
|
&mut self.online
|
||||||
|
}
|
||||||
|
|
||||||
|
fn governor(&mut self, governor: String) {
|
||||||
|
self.governor = governor;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_governor(&self) -> &'_ str {
|
||||||
|
&self.governor
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, _limits: Option<MinMax<u64>>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cpu_online_path(index: usize) -> String {
|
||||||
|
format!("/sys/devices/system/cpu/cpu{}/online", index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cpu_governor_path(index: usize) -> String {
|
||||||
|
format!(
|
||||||
|
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_governor",
|
||||||
|
index
|
||||||
|
)
|
||||||
|
}
|
89
backend/src/settings/unknown/gpu.rs
Normal file
89
backend/src/settings/unknown/gpu.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::settings::MinMax;
|
||||||
|
use crate::settings::{OnResume, OnSet, SettingError};
|
||||||
|
use crate::settings::TGpu;
|
||||||
|
use crate::persist::GpuJson;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Gpu {
|
||||||
|
slow_memory: bool, // ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Into<GpuJson> for Gpu {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> GpuJson {
|
||||||
|
GpuJson {
|
||||||
|
fast_ppt: None,
|
||||||
|
slow_ppt: None,
|
||||||
|
clock_limits: None,
|
||||||
|
slow_memory: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Gpu {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Gpu {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TGpu for Gpu {
|
||||||
|
fn limits(&self) -> crate::api::GpuLimits {
|
||||||
|
crate::api::GpuLimits {
|
||||||
|
fast_ppt_limits: None,
|
||||||
|
slow_ppt_limits: None,
|
||||||
|
ppt_step: 1_000_000,
|
||||||
|
tdp_limits: None,
|
||||||
|
tdp_boost_limits: None,
|
||||||
|
tdp_step: 42,
|
||||||
|
clock_min_limits: None,
|
||||||
|
clock_max_limits: None,
|
||||||
|
clock_step: 100,
|
||||||
|
memory_control_capable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json(&self) -> crate::persist::GpuJson {
|
||||||
|
self.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ppt(&mut self, _fast: Option<u64>, _slow: Option<u64>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ppt(&self) -> (Option<u64>, Option<u64>) {
|
||||||
|
(None, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_limits(&mut self, _limits: Option<MinMax<u64>>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slow_memory(&mut self) -> &mut bool {
|
||||||
|
&mut self.slow_memory
|
||||||
|
}
|
||||||
|
}
|
7
backend/src/settings/unknown/mod.rs
Normal file
7
backend/src/settings/unknown/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
mod battery;
|
||||||
|
mod cpu;
|
||||||
|
mod gpu;
|
||||||
|
|
||||||
|
pub use battery::Battery;
|
||||||
|
pub use cpu::{Cpu, Cpus};
|
||||||
|
pub use gpu::Gpu;
|
|
@ -1,11 +1,7 @@
|
||||||
mod battery;
|
|
||||||
mod cpu;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod gpu;
|
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
pub use battery::Battery;
|
pub mod steam_deck;
|
||||||
pub use cpu::Cpu;
|
|
||||||
pub use error::StateError;
|
pub use error::StateError;
|
||||||
pub use gpu::Gpu;
|
|
||||||
pub use traits::OnPoll;
|
pub use traits::OnPoll;
|
||||||
|
|
7
backend/src/state/steam_deck/mod.rs
Normal file
7
backend/src/state/steam_deck/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
mod battery;
|
||||||
|
mod cpu;
|
||||||
|
mod gpu;
|
||||||
|
|
||||||
|
pub use battery::Battery;
|
||||||
|
pub use cpu::Cpu;
|
||||||
|
pub use gpu::Gpu;
|
|
@ -24,6 +24,50 @@ export async function initBackend() {
|
||||||
//setReady(true);
|
//setReady(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API limit types
|
||||||
|
|
||||||
|
export type RangeLimit = {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SettingsLimits = {
|
||||||
|
battery: BatteryLimits;
|
||||||
|
cpu: CpusLimits;
|
||||||
|
gpu: GpuLimits;
|
||||||
|
general: GeneralLimits;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BatteryLimits = {
|
||||||
|
charge_rate: RangeLimit | null;
|
||||||
|
charge_step: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CpuLimits = {
|
||||||
|
clock_min_limits: RangeLimit | null;
|
||||||
|
clock_max_limits: RangeLimit | null;
|
||||||
|
clock_step: number;
|
||||||
|
governors: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CpusLimits = {
|
||||||
|
cpus: CpuLimits[];
|
||||||
|
count: number;
|
||||||
|
smt_capable: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GeneralLimits = {};
|
||||||
|
|
||||||
|
export type GpuLimits = {
|
||||||
|
fast_ppt_limits: RangeLimit | null;
|
||||||
|
slow_ppt_limits: RangeLimit | null;
|
||||||
|
ppt_step: number;
|
||||||
|
clock_min_limits: RangeLimit | null;
|
||||||
|
clock_max_limits: RangeLimit | null;
|
||||||
|
clock_step: number;
|
||||||
|
memory_control_capable: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
export async function getInfo(): Promise<string> {
|
export async function getInfo(): Promise<string> {
|
||||||
|
@ -66,9 +110,9 @@ export async function setCpuSmt(status: boolean): Promise<boolean> {
|
||||||
return (await call_backend("CPU_set_smt", [status]))[0];
|
return (await call_backend("CPU_set_smt", [status]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCpuCount(): Promise<number> {
|
/*export async function getCpuCount(): Promise<number> {
|
||||||
return (await call_backend("CPU_count", []))[0];
|
return (await call_backend("CPU_count", []))[0];
|
||||||
}
|
}*/
|
||||||
|
|
||||||
export async function setCpuOnline(index: number, online: boolean): Promise<boolean> {
|
export async function setCpuOnline(index: number, online: boolean): Promise<boolean> {
|
||||||
return (await call_backend("CPU_set_online", [index, online]))[0];
|
return (await call_backend("CPU_set_online", [index, online]))[0];
|
||||||
|
@ -165,3 +209,7 @@ export async function getGeneralSettingsName(): Promise<string> {
|
||||||
export async function waitForComplete(): Promise<boolean> {
|
export async function waitForComplete(): Promise<boolean> {
|
||||||
return (await call_backend("GENERAL_wait_for_unlocks", []))[0];
|
return (await call_backend("GENERAL_wait_for_unlocks", []))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getLimits(): Promise<SettingsLimits> {
|
||||||
|
return (await call_backend("GENERAL_get_limits", []))[0];
|
||||||
|
}
|
||||||
|
|
334
src/index.tsx
334
src/index.tsx
|
@ -6,7 +6,7 @@ import {
|
||||||
//MenuItem,
|
//MenuItem,
|
||||||
PanelSection,
|
PanelSection,
|
||||||
PanelSectionRow,
|
PanelSectionRow,
|
||||||
//Router,
|
Router,
|
||||||
ServerAPI,
|
ServerAPI,
|
||||||
//showContextMenu,
|
//showContextMenu,
|
||||||
staticClasses,
|
staticClasses,
|
||||||
|
@ -17,8 +17,8 @@ import {
|
||||||
//DropdownOption,
|
//DropdownOption,
|
||||||
SingleDropdownOption,
|
SingleDropdownOption,
|
||||||
//NotchLabel
|
//NotchLabel
|
||||||
gamepadDialogClasses,
|
//gamepadDialogClasses,
|
||||||
joinClassNames,
|
//joinClassNames,
|
||||||
} from "decky-frontend-lib";
|
} from "decky-frontend-lib";
|
||||||
import { VFC, useState } from "react";
|
import { VFC, useState } from "react";
|
||||||
import { GiDrill } from "react-icons/gi";
|
import { GiDrill } from "react-icons/gi";
|
||||||
|
@ -32,7 +32,9 @@ var lifetimeHook: any = null;
|
||||||
var startHook: any = null;
|
var startHook: any = null;
|
||||||
var usdplReady = false;
|
var usdplReady = false;
|
||||||
|
|
||||||
var smtAllowed = true;
|
var eggCount = 0;
|
||||||
|
|
||||||
|
//var smtAllowed = true;
|
||||||
var advancedMode = false;
|
var advancedMode = false;
|
||||||
var advancedCpu = 1;
|
var advancedCpu = 1;
|
||||||
|
|
||||||
|
@ -41,44 +43,19 @@ type MinMax = {
|
||||||
max: number | null;
|
max: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const governorOptions: SingleDropdownOption[] = [
|
|
||||||
{
|
|
||||||
data: "conservative",
|
|
||||||
label: <span>conservative</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: "ondemand",
|
|
||||||
label: <span>ondemand</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: "userspace",
|
|
||||||
label: <span>userspace</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: "powersave",
|
|
||||||
label: <span>powersave</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: "performance",
|
|
||||||
label: <span>performance</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: "schedutil",
|
|
||||||
label: <span>schedutil</span>,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// usdpl persistent store keys
|
// usdpl persistent store keys
|
||||||
|
|
||||||
const BACKEND_INFO = "VINFO";
|
const BACKEND_INFO = "VINFO";
|
||||||
|
|
||||||
|
const LIMITS_INFO = "LIMITS_all";
|
||||||
|
|
||||||
const CURRENT_BATT = "BATTERY_current_now";
|
const CURRENT_BATT = "BATTERY_current_now";
|
||||||
const CHARGE_RATE_BATT = "BATTERY_charge_rate";
|
const CHARGE_RATE_BATT = "BATTERY_charge_rate";
|
||||||
const CHARGE_NOW_BATT = "BATTERY_charge_now";
|
const CHARGE_NOW_BATT = "BATTERY_charge_now";
|
||||||
const CHARGE_FULL_BATT = "BATTERY_charge_full";
|
const CHARGE_FULL_BATT = "BATTERY_charge_full";
|
||||||
const CHARGE_DESIGN_BATT = "BATTERY_charge_design"
|
const CHARGE_DESIGN_BATT = "BATTERY_charge_design"
|
||||||
|
|
||||||
const TOTAL_CPUS = "CPUs_total";
|
//const TOTAL_CPUS = "CPUs_total";
|
||||||
const ONLINE_CPUS = "CPUs_online";
|
const ONLINE_CPUS = "CPUs_online";
|
||||||
const ONLINE_STATUS_CPUS = "CPUs_status_online";
|
const ONLINE_STATUS_CPUS = "CPUs_status_online";
|
||||||
const SMT_CPU = "CPUs_SMT";
|
const SMT_CPU = "CPUs_SMT";
|
||||||
|
@ -107,7 +84,7 @@ function countCpus(statii: boolean[]): number {
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncPlebClockToAdvanced() {
|
function syncPlebClockToAdvanced() {
|
||||||
const cpuCount = get_value(TOTAL_CPUS);
|
const cpuCount = (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.count;
|
||||||
const minClock = get_value(CLOCK_MIN_CPU);
|
const minClock = get_value(CLOCK_MIN_CPU);
|
||||||
const maxClock = get_value(CLOCK_MAX_CPU);
|
const maxClock = get_value(CLOCK_MAX_CPU);
|
||||||
let clockArr = [];
|
let clockArr = [];
|
||||||
|
@ -123,18 +100,23 @@ function syncPlebClockToAdvanced() {
|
||||||
const reload = function() {
|
const reload = function() {
|
||||||
if (!usdplReady) {return;}
|
if (!usdplReady) {return;}
|
||||||
|
|
||||||
|
backend.resolve(backend.getLimits(), (limits) => {
|
||||||
|
set_value(LIMITS_INFO, limits);
|
||||||
|
console.debug("POWERTOOLS: got limits", limits);
|
||||||
|
});
|
||||||
|
|
||||||
backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) });
|
backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) });
|
||||||
backend.resolve(backend.getBatteryChargeRate(), (rate: number) => { set_value(CHARGE_RATE_BATT, rate) });
|
backend.resolve(backend.getBatteryChargeRate(), (rate: number) => { set_value(CHARGE_RATE_BATT, rate) });
|
||||||
backend.resolve(backend.getBatteryChargeNow(), (rate: number) => { set_value(CHARGE_NOW_BATT, rate) });
|
backend.resolve(backend.getBatteryChargeNow(), (rate: number) => { set_value(CHARGE_NOW_BATT, rate) });
|
||||||
backend.resolve(backend.getBatteryChargeFull(), (rate: number) => { set_value(CHARGE_FULL_BATT, rate) });
|
backend.resolve(backend.getBatteryChargeFull(), (rate: number) => { set_value(CHARGE_FULL_BATT, rate) });
|
||||||
backend.resolve(backend.getBatteryChargeDesign(), (rate: number) => { set_value(CHARGE_DESIGN_BATT, rate) });
|
backend.resolve(backend.getBatteryChargeDesign(), (rate: number) => { set_value(CHARGE_DESIGN_BATT, rate) });
|
||||||
|
|
||||||
backend.resolve(backend.getCpuCount(), (count: number) => { set_value(TOTAL_CPUS, count)});
|
//backend.resolve(backend.getCpuCount(), (count: number) => { set_value(TOTAL_CPUS, count)});
|
||||||
backend.resolve(backend.getCpusOnline(), (statii: boolean[]) => {
|
backend.resolve(backend.getCpusOnline(), (statii: boolean[]) => {
|
||||||
set_value(ONLINE_STATUS_CPUS, statii);
|
set_value(ONLINE_STATUS_CPUS, statii);
|
||||||
const count = countCpus(statii);
|
const count = countCpus(statii);
|
||||||
set_value(ONLINE_CPUS, count);
|
set_value(ONLINE_CPUS, count);
|
||||||
set_value(SMT_CPU, statii.length > 3 && statii[0] == statii[1] && statii[2] == statii[3] && smtAllowed);
|
set_value(SMT_CPU, statii.length > 3 && statii[0] == statii[1] && statii[2] == statii[3]);
|
||||||
});
|
});
|
||||||
backend.resolve(backend.getCpuClockLimits(0), (limits: number[]) => {
|
backend.resolve(backend.getCpuClockLimits(0), (limits: number[]) => {
|
||||||
set_value(CLOCK_MIN_CPU, limits[0]);
|
set_value(CLOCK_MIN_CPU, limits[0]);
|
||||||
|
@ -144,7 +126,6 @@ const reload = function() {
|
||||||
backend.resolve(backend.getCpusGovernor(), (governors: string[]) => {
|
backend.resolve(backend.getCpusGovernor(), (governors: string[]) => {
|
||||||
set_value(GOVERNOR_CPU, governors);
|
set_value(GOVERNOR_CPU, governors);
|
||||||
console.log("POWERTOOLS: Governors from backend", governors);
|
console.log("POWERTOOLS: Governors from backend", governors);
|
||||||
console.log("POWERTOOLS: Governors in dropdown", governorOptions);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
backend.resolve(backend.getGpuPpt(), (ppts: number[]) => {
|
backend.resolve(backend.getGpuPpt(), (ppts: number[]) => {
|
||||||
|
@ -226,10 +207,16 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
reloadGUI("periodic" + (new Date()).getTime().toString());
|
reloadGUI("periodic" + (new Date()).getTime().toString());
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
const FieldWithSeparator = joinClassNames(gamepadDialogClasses.Field, gamepadDialogClasses.WithBottomSeparatorStandard);
|
//const FieldWithSeparator = joinClassNames(gamepadDialogClasses.Field, gamepadDialogClasses.WithBottomSeparatorStandard);
|
||||||
|
|
||||||
const total_cpus = get_value(TOTAL_CPUS);
|
const total_cpus = (get_value(LIMITS_INFO) as backend.SettingsLimits | null)?.cpu.count ?? 8;
|
||||||
const advancedCpuIndex = advancedCpu - 1;
|
const advancedCpuIndex = advancedCpu - 1;
|
||||||
|
const smtAllowed = (get_value(LIMITS_INFO) as backend.SettingsLimits | null)?.cpu.smt_capable ?? true;
|
||||||
|
|
||||||
|
const governorOptions: SingleDropdownOption[] = (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].governors.map((elem) => {return {
|
||||||
|
data: elem,
|
||||||
|
label: <span>{elem}</span>,
|
||||||
|
};});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelSection>
|
<PanelSection>
|
||||||
|
@ -280,7 +267,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
label="Threads"
|
label="Threads"
|
||||||
value={get_value(ONLINE_CPUS)}
|
value={get_value(ONLINE_CPUS)}
|
||||||
step={1}
|
step={1}
|
||||||
max={get_value(SMT_CPU)? total_cpus : total_cpus/2}
|
max={get_value(SMT_CPU) || !smtAllowed ? total_cpus : total_cpus/2}
|
||||||
min={1}
|
min={1}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
onChange={(cpus: number) => {
|
onChange={(cpus: number) => {
|
||||||
|
@ -307,13 +294,17 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{!advancedMode && <PanelSectionRow>
|
{!advancedMode && <PanelSectionRow>
|
||||||
<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"
|
description="Set bounds on clock speed"
|
||||||
onChange={(value: boolean) => {
|
onChange={(value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
set_value(CLOCK_MIN_CPU, 1400);
|
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits != null) {
|
||||||
set_value(CLOCK_MAX_CPU, 3500);
|
set_value(CLOCK_MIN_CPU, (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits!.min);
|
||||||
|
}
|
||||||
|
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_max_limits != null) {
|
||||||
|
set_value(CLOCK_MAX_CPU, (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_max_limits!.max);
|
||||||
|
}
|
||||||
syncPlebClockToAdvanced();
|
syncPlebClockToAdvanced();
|
||||||
reloadGUI("CPUFreqToggle");
|
reloadGUI("CPUFreqToggle");
|
||||||
} else {
|
} else {
|
||||||
|
@ -330,13 +321,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{!advancedMode && <PanelSectionRow>
|
{!advancedMode && (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits != null && <PanelSectionRow>
|
||||||
{get_value(CLOCK_MIN_CPU) != null && <SliderField
|
{get_value(CLOCK_MIN_CPU) != null && <SliderField
|
||||||
label="Minimum (MHz)"
|
label="Minimum (MHz)"
|
||||||
value={get_value(CLOCK_MIN_CPU)}
|
value={get_value(CLOCK_MIN_CPU)}
|
||||||
max={3500}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits!.max}
|
||||||
min={1400}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits!.min}
|
||||||
step={100}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(CLOCK_MIN_CPU) == null}
|
disabled={get_value(CLOCK_MIN_CPU) == null}
|
||||||
onChange={(freq: number) => {
|
onChange={(freq: number) => {
|
||||||
|
@ -360,13 +351,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{!advancedMode && <PanelSectionRow>
|
{!advancedMode && (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_max_limits != null && <PanelSectionRow>
|
||||||
{get_value(CLOCK_MAX_CPU) != null && <SliderField
|
{get_value(CLOCK_MAX_CPU) != null && <SliderField
|
||||||
label="Maximum (MHz)"
|
label="Maximum (MHz)"
|
||||||
value={get_value(CLOCK_MAX_CPU)}
|
value={get_value(CLOCK_MAX_CPU)}
|
||||||
max={3500}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_max_limits!.max}
|
||||||
min={500}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_max_limits!.min}
|
||||||
step={100}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(CLOCK_MAX_CPU) == null}
|
disabled={get_value(CLOCK_MAX_CPU) == null}
|
||||||
onChange={(freq: number) => {
|
onChange={(freq: number) => {
|
||||||
|
@ -393,10 +384,10 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
{/* CPU advanced mode */}
|
{/* CPU advanced mode */}
|
||||||
{advancedMode && <PanelSectionRow>
|
{advancedMode && <PanelSectionRow>
|
||||||
<SliderField
|
<SliderField
|
||||||
label="CPU to modify"
|
label="Selected CPU"
|
||||||
value={advancedCpu}
|
value={advancedCpu}
|
||||||
step={1}
|
step={1}
|
||||||
max={8}
|
max={total_cpus}
|
||||||
min={1}
|
min={1}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
onChange={(cpuNum: number) => {
|
onChange={(cpuNum: number) => {
|
||||||
|
@ -406,9 +397,9 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{advancedMode && <PanelSectionRow>
|
{advancedMode && <PanelSectionRow>
|
||||||
<ToggleField
|
<ToggleField
|
||||||
checked={get_value(ONLINE_CPUS)[advancedCpuIndex]}
|
checked={get_value(ONLINE_STATUS_CPUS)[advancedCpuIndex]}
|
||||||
label="Online"
|
label="Online"
|
||||||
description="Allow the CPU thread to do processing"
|
description="Allow the CPU thread to do work"
|
||||||
onChange={(status: boolean) => {
|
onChange={(status: boolean) => {
|
||||||
console.debug("CPU " + advancedCpu.toString() + " is now " + status.toString());
|
console.debug("CPU " + advancedCpu.toString() + " is now " + status.toString());
|
||||||
if (get_value(SMT_CPU)) {
|
if (get_value(SMT_CPU)) {
|
||||||
|
@ -426,14 +417,19 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{advancedMode && <PanelSectionRow>
|
{advancedMode && <PanelSectionRow>
|
||||||
<ToggleField
|
<ToggleField
|
||||||
checked={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min != null && get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max}
|
checked={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min != null || get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max != null}
|
||||||
label="Frequency Limits"
|
label="Frequency Limits"
|
||||||
description="Set bounds on clock speed"
|
description="Set bounds on clock speed"
|
||||||
onChange={(value: boolean) => {
|
onChange={(value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
const clocks = get_value(CLOCK_MIN_MAX_CPU) as MinMax[];
|
const clocks = get_value(CLOCK_MIN_MAX_CPU) as MinMax[];
|
||||||
clocks[advancedCpuIndex].min = 1400;
|
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits != null) {
|
||||||
clocks[advancedCpuIndex].max = 3500;
|
clocks[advancedCpuIndex].min = (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits!.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_max_limits != null) {
|
||||||
|
clocks[advancedCpuIndex].max = (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_max_limits!.max;
|
||||||
|
}
|
||||||
set_value(CLOCK_MIN_MAX_CPU, clocks);
|
set_value(CLOCK_MIN_MAX_CPU, clocks);
|
||||||
reloadGUI("CPUFreqToggle");
|
reloadGUI("CPUFreqToggle");
|
||||||
} else {
|
} else {
|
||||||
|
@ -448,13 +444,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{advancedMode && <PanelSectionRow>
|
{advancedMode && (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits != null && <PanelSectionRow>
|
||||||
{get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min != null && <SliderField
|
{get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min != null && <SliderField
|
||||||
label="Minimum (MHz)"
|
label="Minimum (MHz)"
|
||||||
value={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min}
|
value={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min}
|
||||||
max={3500}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits!.max}
|
||||||
min={1400}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits!.min}
|
||||||
step={100}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min == null}
|
disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min == null}
|
||||||
onChange={(freq: number) => {
|
onChange={(freq: number) => {
|
||||||
|
@ -473,13 +469,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{advancedMode && <PanelSectionRow>
|
{advancedMode && (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_max_limits != null && <PanelSectionRow>
|
||||||
{get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max != null && <SliderField
|
{get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max != null && <SliderField
|
||||||
label="Maximum (MHz)"
|
label="Maximum (MHz)"
|
||||||
value={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max}
|
value={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max}
|
||||||
max={3500}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_max_limits!.max}
|
||||||
min={500}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_max_limits!.min}
|
||||||
step={100}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max == null}
|
disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max == null}
|
||||||
onChange={(freq: number) => {
|
onChange={(freq: number) => {
|
||||||
|
@ -498,7 +494,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
</PanelSectionRow>}
|
</PanelSectionRow>}
|
||||||
{advancedMode && <PanelSectionRow>
|
{advancedMode && (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].governors.length != 0 && <PanelSectionRow>
|
||||||
<Field
|
<Field
|
||||||
label="Governor"
|
label="Governor"
|
||||||
>
|
>
|
||||||
|
@ -527,15 +523,20 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
<div className={staticClasses.PanelSectionTitle}>
|
<div className={staticClasses.PanelSectionTitle}>
|
||||||
GPU
|
GPU
|
||||||
</div>
|
</div>
|
||||||
<PanelSectionRow>
|
{ ((get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.fast_ppt_limits != null ||(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits != null) && <PanelSectionRow>
|
||||||
<ToggleField
|
<ToggleField
|
||||||
checked={get_value(SLOW_PPT_GPU) != null && get_value(FAST_PPT_GPU) != null}
|
checked={get_value(SLOW_PPT_GPU) != null || get_value(FAST_PPT_GPU) != null}
|
||||||
label="PowerPlay Limits"
|
label="PowerPlay Limits"
|
||||||
description="Override APU TDP settings"
|
description="Override APU TDP settings"
|
||||||
onChange={(value: boolean) => {
|
onChange={(value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
set_value(SLOW_PPT_GPU, 15000000);
|
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits != null) {
|
||||||
set_value(FAST_PPT_GPU, 15000000);
|
set_value(SLOW_PPT_GPU, (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits!.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.fast_ppt_limits != null) {
|
||||||
|
set_value(FAST_PPT_GPU, (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.fast_ppt_limits!.max);
|
||||||
|
}
|
||||||
reloadGUI("GPUPPTToggle");
|
reloadGUI("GPUPPTToggle");
|
||||||
} else {
|
} else {
|
||||||
set_value(SLOW_PPT_GPU, null);
|
set_value(SLOW_PPT_GPU, null);
|
||||||
|
@ -546,21 +547,22 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>}
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
{ get_value(SLOW_PPT_GPU) != null && <SliderField
|
{ get_value(SLOW_PPT_GPU) != null && <SliderField
|
||||||
label="SlowPPT (uW)"
|
label="SlowPPT (W)"
|
||||||
value={get_value(SLOW_PPT_GPU)}
|
value={get_value(SLOW_PPT_GPU)}
|
||||||
max={29000000}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits!.max}
|
||||||
min={1000000}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits!.min}
|
||||||
step={1000000}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.ppt_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(SLOW_PPT_GPU) == null}
|
disabled={get_value(SLOW_PPT_GPU) == null}
|
||||||
onChange={(ppt: number) => {
|
onChange={(ppt: number) => {
|
||||||
console.debug("SlowPPT is now " + ppt.toString());
|
console.debug("SlowPPT is now " + ppt.toString());
|
||||||
const pptNow = get_value(SLOW_PPT_GPU);
|
const pptNow = get_value(SLOW_PPT_GPU);
|
||||||
if (ppt != pptNow) {
|
const realPpt = ppt;
|
||||||
backend.resolve(backend.setGpuPpt(get_value(FAST_PPT_GPU), ppt),
|
if (realPpt != pptNow) {
|
||||||
|
backend.resolve(backend.setGpuPpt(get_value(FAST_PPT_GPU), realPpt),
|
||||||
(limits: number[]) => {
|
(limits: number[]) => {
|
||||||
set_value(FAST_PPT_GPU, limits[0]);
|
set_value(FAST_PPT_GPU, limits[0]);
|
||||||
set_value(SLOW_PPT_GPU, limits[1]);
|
set_value(SLOW_PPT_GPU, limits[1]);
|
||||||
|
@ -572,18 +574,19 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
{get_value(FAST_PPT_GPU) != null && <SliderField
|
{get_value(FAST_PPT_GPU) != null && <SliderField
|
||||||
label="FastPPT (uW)"
|
label="FastPPT (W)"
|
||||||
value={get_value(FAST_PPT_GPU)}
|
value={get_value(FAST_PPT_GPU)}
|
||||||
max={29000000}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.fast_ppt_limits!.max}
|
||||||
min={1000000}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.fast_ppt_limits!.min}
|
||||||
step={1000000}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.ppt_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(FAST_PPT_GPU) == null}
|
disabled={get_value(FAST_PPT_GPU) == null}
|
||||||
onChange={(ppt: number) => {
|
onChange={(ppt: number) => {
|
||||||
console.debug("FastPPT is now " + ppt.toString());
|
console.debug("FastPPT is now " + ppt.toString());
|
||||||
const pptNow = get_value(FAST_PPT_GPU);
|
const pptNow = get_value(FAST_PPT_GPU);
|
||||||
if (ppt != pptNow) {
|
const realPpt = ppt;
|
||||||
backend.resolve(backend.setGpuPpt(get_value(SLOW_PPT_GPU), ppt),
|
if (realPpt != pptNow) {
|
||||||
|
backend.resolve(backend.setGpuPpt(realPpt, get_value(SLOW_PPT_GPU)),
|
||||||
(limits: number[]) => {
|
(limits: number[]) => {
|
||||||
set_value(FAST_PPT_GPU, limits[0]);
|
set_value(FAST_PPT_GPU, limits[0]);
|
||||||
set_value(SLOW_PPT_GPU, limits[1]);
|
set_value(SLOW_PPT_GPU, limits[1]);
|
||||||
|
@ -593,15 +596,21 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
<PanelSectionRow>
|
{((get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_min_limits != null || (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_max_limits != null) && <PanelSectionRow>
|
||||||
<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"
|
description="Override bounds on gpu clock"
|
||||||
onChange={(value: boolean) => {
|
onChange={(value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
set_value(CLOCK_MIN_GPU, 200);
|
let clock_min_limits = (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_min_limits;
|
||||||
set_value(CLOCK_MAX_GPU, 1600);
|
let clock_max_limits = (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_max_limits;
|
||||||
|
if (clock_min_limits != null) {
|
||||||
|
set_value(CLOCK_MIN_GPU, clock_min_limits.min);
|
||||||
|
}
|
||||||
|
if (clock_max_limits != null) {
|
||||||
|
set_value(CLOCK_MAX_GPU, clock_max_limits.max);
|
||||||
|
}
|
||||||
reloadGUI("GPUFreqToggle");
|
reloadGUI("GPUFreqToggle");
|
||||||
} else {
|
} else {
|
||||||
set_value(CLOCK_MIN_GPU, null);
|
set_value(CLOCK_MIN_GPU, null);
|
||||||
|
@ -612,14 +621,14 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>}
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
{ get_value(CLOCK_MIN_GPU) != null && <SliderField
|
{ get_value(CLOCK_MIN_GPU) != null && <SliderField
|
||||||
label="Minimum (MHz)"
|
label="Minimum (MHz)"
|
||||||
value={get_value(CLOCK_MIN_GPU)}
|
value={get_value(CLOCK_MIN_GPU)}
|
||||||
max={1600}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_min_limits!.max}
|
||||||
min={200}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_min_limits!.min}
|
||||||
step={100}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(CLOCK_MIN_GPU) == null}
|
disabled={get_value(CLOCK_MIN_GPU) == null}
|
||||||
onChange={(val: number) => {
|
onChange={(val: number) => {
|
||||||
|
@ -640,9 +649,9 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
{get_value(CLOCK_MAX_GPU) != null && <SliderField
|
{get_value(CLOCK_MAX_GPU) != null && <SliderField
|
||||||
label="Maximum (MHz)"
|
label="Maximum (MHz)"
|
||||||
value={get_value(CLOCK_MAX_GPU)}
|
value={get_value(CLOCK_MAX_GPU)}
|
||||||
max={1600}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_max_limits!.max}
|
||||||
min={200}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_max_limits!.min}
|
||||||
step={100}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(CLOCK_MAX_GPU) == null}
|
disabled={get_value(CLOCK_MAX_GPU) == null}
|
||||||
onChange={(val: number) => {
|
onChange={(val: number) => {
|
||||||
|
@ -659,7 +668,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
<PanelSectionRow>
|
{(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control_capable && <PanelSectionRow>
|
||||||
<ToggleField
|
<ToggleField
|
||||||
checked={get_value(SLOW_MEMORY_GPU)}
|
checked={get_value(SLOW_MEMORY_GPU)}
|
||||||
label="Downclock Memory"
|
label="Downclock Memory"
|
||||||
|
@ -671,36 +680,28 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>}
|
||||||
{/* Battery */}
|
{/* Battery */}
|
||||||
<div className={staticClasses.PanelSectionTitle}>
|
<div className={staticClasses.PanelSectionTitle}>
|
||||||
Battery
|
Battery
|
||||||
</div>
|
</div>
|
||||||
<PanelSectionRow>
|
{get_value(CHARGE_NOW_BATT) != null && get_value(CHARGE_FULL_BATT) != null && <PanelSectionRow>
|
||||||
<div className={FieldWithSeparator}>
|
<Field
|
||||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
label="Now (Charge)"
|
||||||
<div className={gamepadDialogClasses.FieldLabel}>
|
onClick={()=> eggCount++}
|
||||||
Now (Charge)
|
focusable={false}>
|
||||||
</div>
|
|
||||||
<div className={gamepadDialogClasses.FieldChildren}>
|
|
||||||
{get_value(CHARGE_NOW_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_NOW_BATT) / get_value(CHARGE_FULL_BATT)).toFixed(1)}%)
|
{get_value(CHARGE_NOW_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_NOW_BATT) / get_value(CHARGE_FULL_BATT)).toFixed(1)}%)
|
||||||
</div>
|
</Field>
|
||||||
</div>
|
</PanelSectionRow>}
|
||||||
</div>
|
{get_value(CHARGE_FULL_BATT) != null && get_value(CHARGE_DESIGN_BATT) != null && <PanelSectionRow>
|
||||||
</PanelSectionRow>
|
<Field
|
||||||
<PanelSectionRow>
|
label="Max (Design)"
|
||||||
<div className={FieldWithSeparator}>
|
onClick={()=> eggCount++}
|
||||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
focusable={false}>
|
||||||
<div className={gamepadDialogClasses.FieldLabel}>
|
|
||||||
Max (Design)
|
|
||||||
</div>
|
|
||||||
<div className={gamepadDialogClasses.FieldChildren}>
|
|
||||||
{get_value(CHARGE_FULL_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_FULL_BATT) / get_value(CHARGE_DESIGN_BATT)).toFixed(1)}%)
|
{get_value(CHARGE_FULL_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_FULL_BATT) / get_value(CHARGE_DESIGN_BATT)).toFixed(1)}%)
|
||||||
</div>
|
</Field>
|
||||||
</div>
|
</PanelSectionRow>}
|
||||||
</div>
|
{(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate != null && <PanelSectionRow>
|
||||||
</PanelSectionRow>
|
|
||||||
<PanelSectionRow>
|
|
||||||
<ToggleField
|
<ToggleField
|
||||||
checked={get_value(CHARGE_RATE_BATT) != null}
|
checked={get_value(CHARGE_RATE_BATT) != null}
|
||||||
label="Charge Current Limits"
|
label="Charge Current Limits"
|
||||||
|
@ -720,9 +721,9 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
{ get_value(CHARGE_RATE_BATT) != null && <SliderField
|
{ get_value(CHARGE_RATE_BATT) != null && <SliderField
|
||||||
label="Maximum (mA)"
|
label="Maximum (mA)"
|
||||||
value={get_value(CHARGE_RATE_BATT)}
|
value={get_value(CHARGE_RATE_BATT)}
|
||||||
max={2500}
|
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate!.max}
|
||||||
min={250}
|
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate!.min}
|
||||||
step={50}
|
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_step}
|
||||||
showValue={true}
|
showValue={true}
|
||||||
disabled={get_value(CHARGE_RATE_BATT) == null}
|
disabled={get_value(CHARGE_RATE_BATT) == null}
|
||||||
onChange={(val: number) => {
|
onChange={(val: number) => {
|
||||||
|
@ -737,18 +738,14 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>}
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<div className={FieldWithSeparator}>
|
<Field
|
||||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
label="Current"
|
||||||
<div className={gamepadDialogClasses.FieldLabel}>
|
onClick={()=> eggCount++}
|
||||||
Current
|
focusable={false}>
|
||||||
</div>
|
|
||||||
<div className={gamepadDialogClasses.FieldChildren}>
|
|
||||||
{get_value(CURRENT_BATT)} mA
|
{get_value(CURRENT_BATT)} mA
|
||||||
</div>
|
</Field>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
{/* Persistence */}
|
{/* Persistence */}
|
||||||
<div className={staticClasses.PanelSectionTitle}>
|
<div className={staticClasses.PanelSectionTitle}>
|
||||||
|
@ -769,56 +766,51 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
/>
|
/>
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<div className={FieldWithSeparator}>
|
<Field
|
||||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
label="Profile"
|
||||||
<div className={gamepadDialogClasses.FieldLabel}>
|
onClick={()=> eggCount++}
|
||||||
Profile
|
focusable={false}>
|
||||||
</div>
|
|
||||||
<div className={gamepadDialogClasses.FieldChildren}>
|
|
||||||
{get_value(NAME_GEN)}
|
{get_value(NAME_GEN)}
|
||||||
</div>
|
</Field>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
{/* Version Info */}
|
{/* Version Info */}
|
||||||
<div className={staticClasses.PanelSectionTitle}>
|
<div className={staticClasses.PanelSectionTitle}>
|
||||||
Debug
|
{eggCount % 10 == 9 ? "Ha! Nerd" : "Debug"}
|
||||||
</div>
|
</div>
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<div className={FieldWithSeparator}>
|
<Field
|
||||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
label={eggCount % 10 == 9 ? "PowerTools" : "Native"}
|
||||||
<div className={gamepadDialogClasses.FieldLabel}>
|
onClick={()=> {
|
||||||
Native
|
if (eggCount % 10 == 9) {
|
||||||
</div>
|
// you know you're bored and/or conceited when you spend time adding an easter egg
|
||||||
<div className={gamepadDialogClasses.FieldChildren}>
|
// that just sends people to your own project's repo
|
||||||
{get_value(BACKEND_INFO)}
|
Router.NavigateToExternalWeb("https://github.com/NGnius/PowerTools");
|
||||||
</div>
|
}
|
||||||
</div>
|
eggCount++;
|
||||||
</div>
|
}}>
|
||||||
|
{eggCount % 10 == 9 ? "by NGnius" : get_value(BACKEND_INFO)}
|
||||||
|
</Field>
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<div className={FieldWithSeparator}>
|
<Field
|
||||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
label="Framework"
|
||||||
<div className={gamepadDialogClasses.FieldLabel}>
|
onClick={()=> eggCount++}>
|
||||||
Framework
|
{eggCount % 10 == 9 ? "<3 <3 <3" : target_usdpl()}
|
||||||
</div>
|
</Field>
|
||||||
<div className={gamepadDialogClasses.FieldChildren}>
|
|
||||||
{target_usdpl()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<div className={FieldWithSeparator}>
|
<Field
|
||||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
label="USDPL"
|
||||||
<div className={gamepadDialogClasses.FieldLabel}>
|
onClick={()=> {
|
||||||
USDPL
|
if (eggCount % 10 == 9) {
|
||||||
</div>
|
// you know you're bored and/or conceited when you spend time adding an easter egg
|
||||||
<div className={gamepadDialogClasses.FieldChildren}>
|
// that just sends people to your own project's repo
|
||||||
|
Router.NavigateToExternalWeb("https://github.com/NGnius/usdpl-rs");
|
||||||
|
}
|
||||||
|
eggCount++;
|
||||||
|
}}>
|
||||||
v{version_usdpl()}
|
v{version_usdpl()}
|
||||||
</div>
|
</Field>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<ButtonItem
|
<ButtonItem
|
||||||
|
|
Loading…
Reference in a new issue