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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -3,14 +3,14 @@ use std::sync::{Arc, Mutex};
|
|||
use usdpl_back::core::serdes::Primitive;
|
||||
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 super::handler::{ApiMessage, CpuMessage};
|
||||
|
||||
/// Available CPUs web method
|
||||
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||
super::utility::map_result(
|
||||
Cpus::cpu_count()
|
||||
crate::settings::steam_deck::Cpus::cpu_count()
|
||||
.map(|x| x as u64)
|
||||
.ok_or_else(
|
||||
|| 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 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::utility::unwrap_maybe_fatal;
|
||||
|
||||
|
@ -16,6 +16,7 @@ pub enum ApiMessage {
|
|||
LoadSettings(String, String), // (path, name)
|
||||
LoadMainSettings,
|
||||
LoadSystemSettings,
|
||||
GetLimits(Callback<super::SettingsLimits>),
|
||||
}
|
||||
|
||||
pub enum BatteryMessage {
|
||||
|
@ -24,10 +25,10 @@ pub enum BatteryMessage {
|
|||
}
|
||||
|
||||
impl BatteryMessage {
|
||||
fn process(self, settings: &mut Battery) {
|
||||
fn process(self, settings: &mut dyn TBattery) {
|
||||
match self {
|
||||
Self::SetChargeRate(rate) => settings.charge_rate = rate,
|
||||
Self::GetChargeRate(cb) => cb(settings.charge_rate),
|
||||
Self::SetChargeRate(rate) => settings.charge_rate(rate),
|
||||
Self::GetChargeRate(cb) => cb(settings.get_charge_rate()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,41 +46,41 @@ pub enum CpuMessage {
|
|||
}
|
||||
|
||||
impl CpuMessage {
|
||||
fn process(self, settings: &mut Cpus) {
|
||||
fn process(self, settings: &mut dyn TCpus) {
|
||||
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) => {
|
||||
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) => {
|
||||
let mut result = Vec::with_capacity(settings.cpus.len());
|
||||
for i in 0..settings.cpus.len() {
|
||||
settings.cpus[i].online = settings.cpus[i].online && (status || i % 2 == 0);
|
||||
result.push(settings.cpus[i].online);
|
||||
let mut result = Vec::with_capacity(settings.len());
|
||||
for i in 0..settings.len() {
|
||||
*settings.cpus()[i].online() = *settings.cpus()[i].online() && (status || i % 2 == 0);
|
||||
result.push(*settings.cpus()[i].online());
|
||||
}
|
||||
cb(result);
|
||||
}
|
||||
Self::GetCpusOnline(cb) => {
|
||||
let mut result = Vec::with_capacity(settings.cpus.len());
|
||||
for cpu in &settings.cpus {
|
||||
result.push(cpu.online);
|
||||
let mut result = Vec::with_capacity(settings.len());
|
||||
for cpu in settings.cpus() {
|
||||
result.push(*cpu.online());
|
||||
}
|
||||
cb(result);
|
||||
},
|
||||
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::SetCpuGovernor(index, gov) => {settings.cpus.get_mut(index).map(|c| c.governor = gov);},
|
||||
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.get_clock_limits().map(|x| x.to_owned())));},
|
||||
Self::SetCpuGovernor(index, gov) => {settings.cpus().get_mut(index).map(|c| c.governor(gov));},
|
||||
Self::SetCpusGovernor(govs) => {
|
||||
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) => {
|
||||
let mut result = Vec::with_capacity(settings.cpus.len());
|
||||
for cpu in &settings.cpus {
|
||||
result.push(cpu.governor.clone());
|
||||
let mut result = Vec::with_capacity(settings.len());
|
||||
for cpu in settings.cpus() {
|
||||
result.push(cpu.get_governor().to_owned());
|
||||
}
|
||||
cb(result);
|
||||
}
|
||||
|
@ -97,17 +98,14 @@ pub enum GpuMessage {
|
|||
}
|
||||
|
||||
impl GpuMessage {
|
||||
fn process(self, settings: &mut Gpu) {
|
||||
fn process(self, settings: &mut dyn TGpu) {
|
||||
match self {
|
||||
Self::SetPpt(fast, slow) => {
|
||||
settings.fast_ppt = fast;
|
||||
settings.slow_ppt = slow;
|
||||
},
|
||||
Self::GetPpt(cb) => cb((settings.fast_ppt, settings.slow_ppt)),
|
||||
Self::SetClockLimits(clocks) => settings.clock_limits = clocks,
|
||||
Self::GetClockLimits(cb) => cb(settings.clock_limits.clone()),
|
||||
Self::SetSlowMemory(val) => settings.slow_memory = val,
|
||||
Self::GetSlowMemory(cb) => cb(settings.slow_memory),
|
||||
Self::SetPpt(fast, slow) => settings.ppt(fast, slow),
|
||||
Self::GetPpt(cb) => cb(settings.get_ppt()),
|
||||
Self::SetClockLimits(clocks) => settings.clock_limits(clocks),
|
||||
Self::GetClockLimits(cb) => cb(settings.get_clock_limits().map(|x| x.to_owned())),
|
||||
Self::SetSlowMemory(val) => *settings.slow_memory() = val,
|
||||
Self::GetSlowMemory(cb) => cb(*settings.slow_memory()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,11 +117,11 @@ pub enum GeneralMessage {
|
|||
}
|
||||
|
||||
impl GeneralMessage {
|
||||
fn process(self, settings: &mut General) {
|
||||
fn process(self, settings: &mut dyn TGeneral) {
|
||||
match self {
|
||||
Self::SetPersistent(val) => settings.persistent = val,
|
||||
Self::GetPersistent(cb) => cb(settings.persistent),
|
||||
Self::GetCurrentProfileName(cb) => cb(settings.name.clone()),
|
||||
Self::SetPersistent(val) => *settings.persistent() = val,
|
||||
Self::GetPersistent(cb) => cb(*settings.persistent()),
|
||||
Self::GetCurrentProfileName(cb) => cb(settings.get_name().to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,11 +148,11 @@ impl ApiMessageHandler {
|
|||
}
|
||||
// save
|
||||
log::debug!("api_worker is saving...");
|
||||
let is_persistent = settings.general.persistent;
|
||||
let is_persistent = *settings.general.persistent();
|
||||
if is_persistent {
|
||||
let save_path = crate::utility::settings_dir()
|
||||
.join(settings.general.path.clone());
|
||||
let settings_clone = settings.clone();
|
||||
.join(settings.general.get_path().clone());
|
||||
let settings_clone = settings.json();
|
||||
let save_json: SettingsJson = settings_clone.into();
|
||||
unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings");
|
||||
log::debug!("Saved settings to {}", save_path.display());
|
||||
|
@ -166,10 +164,10 @@ impl ApiMessageHandler {
|
|||
|
||||
pub fn process(&mut self, settings: &mut Settings, message: ApiMessage) {
|
||||
match message {
|
||||
ApiMessage::Battery(x) => x.process(&mut settings.battery),
|
||||
ApiMessage::Cpu(x) => x.process(&mut settings.cpus),
|
||||
ApiMessage::Gpu(x) => x.process(&mut settings.gpu),
|
||||
ApiMessage::General(x) => x.process(&mut settings.general),
|
||||
ApiMessage::Battery(x) => x.process(settings.battery.as_mut()),
|
||||
ApiMessage::Cpu(x) => x.process(settings.cpus.as_mut()),
|
||||
ApiMessage::Gpu(x) => x.process(settings.gpu.as_mut()),
|
||||
ApiMessage::General(x) => x.process(settings.general.as_mut()),
|
||||
ApiMessage::OnResume => {
|
||||
if let Err(e) = settings.on_resume() {
|
||||
log::error!("Settings on_resume() err: {}", e);
|
||||
|
@ -194,6 +192,14 @@ impl ApiMessageHandler {
|
|||
}
|
||||
ApiMessage::LoadSystemSettings => {
|
||||
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 cpu;
|
||||
pub mod general;
|
||||
|
@ -7,3 +8,5 @@ mod async_utils;
|
|||
mod utility;
|
||||
|
||||
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]
|
||||
pub fn map_empty_result<T: Into<Primitive>>(
|
||||
result: Result<(), SettingError>,
|
||||
|
|
|
@ -44,6 +44,8 @@ fn main() -> Result<(), ()> {
|
|||
log::info!("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))
|
||||
.map(|settings| settings::Settings::from_json(settings, DEFAULT_SETTINGS_FILE.into()))
|
||||
.unwrap_or_else(|_| settings::Settings::system_default(DEFAULT_SETTINGS_FILE.into()));
|
||||
|
@ -183,6 +185,10 @@ fn main() -> Result<(), ()> {
|
|||
.register_async(
|
||||
"GENERAL_wait_for_unlocks",
|
||||
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);
|
||||
|
|
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 super::JsonError;
|
||||
use super::{BatteryJson, CpuJson, GpuJson};
|
||||
use super::{BatteryJson, CpuJson, GpuJson, DriverJson};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SettingsJson {
|
||||
|
@ -13,6 +13,7 @@ pub struct SettingsJson {
|
|||
pub cpus: Vec<CpuJson>,
|
||||
pub gpu: GpuJson,
|
||||
pub battery: BatteryJson,
|
||||
pub provider: Option<DriverJson>,
|
||||
}
|
||||
|
||||
impl Default for SettingsJson {
|
||||
|
@ -24,6 +25,7 @@ impl Default for SettingsJson {
|
|||
cpus: Vec::with_capacity(8),
|
||||
gpu: GpuJson::default(),
|
||||
battery: BatteryJson::default(),
|
||||
provider: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
mod battery;
|
||||
mod cpu;
|
||||
mod driver;
|
||||
mod error;
|
||||
mod general;
|
||||
mod gpu;
|
||||
|
||||
pub use battery::BatteryJson;
|
||||
pub use cpu::CpuJson;
|
||||
pub use driver::DriverJson;
|
||||
pub use general::{MinMaxJson, SettingsJson};
|
||||
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::sync::{Arc, Mutex};
|
||||
|
||||
use super::{Battery, Cpus, Gpu};
|
||||
//use super::{Battery, Cpus, Gpu};
|
||||
use super::{OnResume, OnSet, SettingError};
|
||||
use super::{TGeneral, TGpu, TCpus, TBattery};
|
||||
use crate::persist::SettingsJson;
|
||||
//use crate::utility::unwrap_lock;
|
||||
|
||||
|
@ -33,6 +33,7 @@ pub struct General {
|
|||
pub persistent: bool,
|
||||
pub path: PathBuf,
|
||||
pub name: String,
|
||||
pub driver: crate::persist::DriverJson,
|
||||
}
|
||||
|
||||
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 general: General,
|
||||
pub cpus: Cpus,
|
||||
pub gpu: Gpu,
|
||||
pub battery: Battery,
|
||||
pub general: Box<dyn TGeneral>,
|
||||
pub cpus: Box<dyn TCpus>,
|
||||
pub gpu: Box<dyn TGpu>,
|
||||
pub battery: Box<dyn TBattery>,
|
||||
}
|
||||
|
||||
impl OnSet for Settings {
|
||||
|
@ -62,47 +103,38 @@ impl OnSet for Settings {
|
|||
impl Settings {
|
||||
#[inline]
|
||||
pub fn from_json(other: SettingsJson, json_path: PathBuf) -> Self {
|
||||
match other.version {
|
||||
0 => 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),
|
||||
},
|
||||
_ => 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),
|
||||
match super::Driver::init(other, json_path.clone()) {
|
||||
Ok(x) => {
|
||||
log::info!("Loaded settings for driver {:?}", x.general.provider());
|
||||
Self {
|
||||
general: x.general,
|
||||
cpus: x.cpus,
|
||||
gpu: x.gpu,
|
||||
battery: x.battery,
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Driver init error: {}", e);
|
||||
Self::system_default(json_path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default(json_path: PathBuf) -> Self {
|
||||
let driver = super::Driver::system_default(json_path);
|
||||
Self {
|
||||
general: General {
|
||||
persistent: false,
|
||||
path: json_path,
|
||||
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
|
||||
},
|
||||
cpus: Cpus::system_default(),
|
||||
gpu: Gpu::system_default(),
|
||||
battery: Battery::system_default(),
|
||||
general: driver.general,
|
||||
cpus: driver.cpus,
|
||||
gpu: driver.gpu,
|
||||
battery: driver.battery,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_system_default(&mut self) {
|
||||
self.cpus = Cpus::system_default();
|
||||
self.gpu = Gpu::system_default();
|
||||
self.battery = Battery::system_default();
|
||||
let driver = super::Driver::system_default(self.general.get_path().to_owned());
|
||||
self.cpus = driver.cpus;
|
||||
self.gpu = driver.gpu;
|
||||
self.battery = driver.battery;
|
||||
}
|
||||
|
||||
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 {
|
||||
log::warn!("Loaded persistent config `{}` ({}) with persistent=false", &settings_json.name, json_path.display());
|
||||
self.general.persistent = false;
|
||||
self.general.name = name;
|
||||
*self.general.persistent() = false;
|
||||
self.general.name(name);
|
||||
} else {
|
||||
self.cpus = Cpus::from_json(settings_json.cpus, settings_json.version);
|
||||
self.gpu = Gpu::from_json(settings_json.gpu, settings_json.version);
|
||||
self.battery = Battery::from_json(settings_json.battery, settings_json.version);
|
||||
self.general.persistent = true;
|
||||
self.general.name = settings_json.name;
|
||||
self.cpus = Box::new(super::steam_deck::Cpus::from_json(settings_json.cpus, settings_json.version));
|
||||
self.gpu = Box::new(super::steam_deck::Gpu::from_json(settings_json.gpu, 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.name(settings_json.name);
|
||||
}
|
||||
} else {
|
||||
if system_defaults {
|
||||
self.load_system_default();
|
||||
}
|
||||
self.general.persistent = false;
|
||||
self.general.name = name;
|
||||
*self.general.persistent() = false;
|
||||
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]
|
||||
fn into(self) -> SettingsJson {
|
||||
log::debug!("Converting into json");
|
||||
SettingsJson {
|
||||
version: LATEST_VERSION,
|
||||
name: self.general.name.clone(),
|
||||
persistent: self.general.persistent,
|
||||
cpus: self.cpus.cpus
|
||||
.clone()
|
||||
.drain(..)
|
||||
.map(|cpu| cpu.into())
|
||||
.collect(),
|
||||
gpu: self.gpu.clone().into(),
|
||||
battery: self.battery.clone().into(),
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
mod battery;
|
||||
mod cpu;
|
||||
pub mod driver;
|
||||
mod error;
|
||||
mod general;
|
||||
mod gpu;
|
||||
mod min_max;
|
||||
mod traits;
|
||||
|
||||
pub use battery::Battery;
|
||||
pub use cpu::{Cpu, Cpus};
|
||||
pub mod steam_deck;
|
||||
pub mod steam_deck_adv;
|
||||
pub mod unknown;
|
||||
|
||||
pub use driver::Driver;
|
||||
pub use general::{SettingVariant, Settings, General};
|
||||
pub use gpu::Gpu;
|
||||
pub use min_max::MinMax;
|
||||
|
||||
pub use error::SettingError;
|
||||
pub use traits::{OnResume, OnSet, SettingsRange};
|
||||
pub use traits::{OnResume, OnSet, SettingsRange, TGeneral, TGpu, TCpus, TBattery, TCpu};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
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;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Battery {
|
||||
pub charge_rate: Option<u64>,
|
||||
state: crate::state::Battery,
|
||||
state: crate::state::steam_deck::Battery,
|
||||
}
|
||||
|
||||
const BATTERY_VOLTAGE: f64 = 7.7;
|
||||
|
@ -23,11 +25,11 @@ impl Battery {
|
|||
match version {
|
||||
0 => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
state: crate::state::Battery::default(),
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
},
|
||||
_ => Self {
|
||||
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(
|
||||
|e| SettingError {
|
||||
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 {
|
||||
|
@ -46,7 +48,7 @@ impl Battery {
|
|||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, Self::max().charge_rate.unwrap()).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
|
@ -66,11 +68,11 @@ impl Battery {
|
|||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
|
||||
Err((Some(e), None)) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
|
@ -86,11 +88,11 @@ impl Battery {
|
|||
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: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
|
@ -105,11 +107,11 @@ impl Battery {
|
|||
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: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
|
@ -124,11 +126,11 @@ impl Battery {
|
|||
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: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
||||
setting: super::SettingVariant::Battery,
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
|
@ -142,7 +144,7 @@ impl Battery {
|
|||
pub fn system_default() -> Self {
|
||||
Self {
|
||||
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 {
|
||||
Self {
|
||||
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 {
|
||||
Self {
|
||||
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 super::MinMax;
|
||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
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";
|
||||
|
@ -25,7 +27,7 @@ impl OnSet for Cpus {
|
|||
"Failed to write `on` to `{}`: {}",
|
||||
CPU_SMT_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
} else {
|
||||
|
@ -35,7 +37,7 @@ impl OnSet for Cpus {
|
|||
"Failed to write `off` to `{}`: {}",
|
||||
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)]
|
||||
pub struct Cpu {
|
||||
pub online: bool,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub governor: String,
|
||||
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";
|
||||
|
@ -150,14 +174,14 @@ impl Cpu {
|
|||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
governor: other.governor,
|
||||
index: i,
|
||||
state: crate::state::Cpu::default(),
|
||||
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::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| {
|
||||
SettingError {
|
||||
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 `{}`: {}",
|
||||
CPU_FORCE_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
@ -199,7 +223,7 @@ impl Cpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
|
@ -210,7 +234,7 @@ impl Cpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&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 {
|
||||
|
@ -225,7 +249,7 @@ impl Cpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Cpu,
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
|
@ -236,7 +260,7 @@ impl Cpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&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| {
|
||||
SettingError {
|
||||
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 `{}`: {}",
|
||||
&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 {
|
||||
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,
|
||||
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()),
|
||||
index: index,
|
||||
state: crate::state::Cpu::default(),
|
||||
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: 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 {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
|
@ -324,7 +390,7 @@ impl SettingsRange for Cpu {
|
|||
}),
|
||||
governor: "schedutil".to_owned(),
|
||||
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 }),
|
||||
governor: "schedutil".to_owned(),
|
||||
index: usize::MIN,
|
||||
state: crate::state::Cpu::default(),
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use super::MinMax;
|
||||
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
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;
|
||||
|
@ -13,7 +15,7 @@ pub struct Gpu {
|
|||
pub slow_ppt: Option<u64>,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub slow_memory: bool,
|
||||
state: crate::state::Gpu,
|
||||
state: crate::state::steam_deck::Gpu,
|
||||
}
|
||||
|
||||
// same as CPU
|
||||
|
@ -30,14 +32,14 @@ impl Gpu {
|
|||
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::Gpu::default(),
|
||||
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::Gpu::default(),
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +54,7 @@ impl Gpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
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 `{}`: {}",
|
||||
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 `{}`: {}",
|
||||
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)
|
||||
.map_err(|e| SettingError {
|
||||
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 {
|
||||
// set clock limits
|
||||
|
@ -100,7 +102,7 @@ impl Gpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
|
@ -111,7 +113,7 @@ impl Gpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&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 {
|
||||
|
@ -125,7 +127,7 @@ impl Gpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: super::SettingVariant::Gpu,
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
|
@ -136,7 +138,7 @@ impl Gpu {
|
|||
"Failed to write `{}` to `{}`: {}",
|
||||
&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| {
|
||||
SettingError {
|
||||
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,
|
||||
clock_limits: None,
|
||||
slow_memory: false,
|
||||
state: crate::state::Gpu::default(),
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,14 +218,14 @@ impl SettingsRange for Gpu {
|
|||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
fast_ppt: Some(30000000),
|
||||
slow_ppt: Some(29000000),
|
||||
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::Gpu::default(),
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,11 +236,70 @@ impl SettingsRange for Gpu {
|
|||
slow_ppt: Some(1000000),
|
||||
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
||||
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]
|
||||
fn gpu_power_path(power_number: u8) -> String {
|
||||
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::MinMax;
|
||||
|
||||
pub trait OnSet {
|
||||
fn on_set(&mut self) -> Result<(), SettingError>;
|
||||
|
@ -12,3 +14,69 @@ pub trait SettingsRange {
|
|||
fn max() -> 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 gpu;
|
||||
mod traits;
|
||||
|
||||
pub use battery::Battery;
|
||||
pub use cpu::Cpu;
|
||||
pub mod steam_deck;
|
||||
|
||||
pub use error::StateError;
|
||||
pub use gpu::Gpu;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
export async function getCpuCount(): Promise<number> {
|
||||
/*export async function getCpuCount(): Promise<number> {
|
||||
return (await call_backend("CPU_count", []))[0];
|
||||
}
|
||||
}*/
|
||||
|
||||
export async function setCpuOnline(index: number, online: boolean): Promise<boolean> {
|
||||
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> {
|
||||
return (await call_backend("GENERAL_wait_for_unlocks", []))[0];
|
||||
}
|
||||
|
||||
export async function getLimits(): Promise<SettingsLimits> {
|
||||
return (await call_backend("GENERAL_get_limits", []))[0];
|
||||
}
|
||||
|
|
344
src/index.tsx
344
src/index.tsx
|
@ -6,7 +6,7 @@ import {
|
|||
//MenuItem,
|
||||
PanelSection,
|
||||
PanelSectionRow,
|
||||
//Router,
|
||||
Router,
|
||||
ServerAPI,
|
||||
//showContextMenu,
|
||||
staticClasses,
|
||||
|
@ -17,8 +17,8 @@ import {
|
|||
//DropdownOption,
|
||||
SingleDropdownOption,
|
||||
//NotchLabel
|
||||
gamepadDialogClasses,
|
||||
joinClassNames,
|
||||
//gamepadDialogClasses,
|
||||
//joinClassNames,
|
||||
} from "decky-frontend-lib";
|
||||
import { VFC, useState } from "react";
|
||||
import { GiDrill } from "react-icons/gi";
|
||||
|
@ -32,7 +32,9 @@ var lifetimeHook: any = null;
|
|||
var startHook: any = null;
|
||||
var usdplReady = false;
|
||||
|
||||
var smtAllowed = true;
|
||||
var eggCount = 0;
|
||||
|
||||
//var smtAllowed = true;
|
||||
var advancedMode = false;
|
||||
var advancedCpu = 1;
|
||||
|
||||
|
@ -41,44 +43,19 @@ type MinMax = {
|
|||
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
|
||||
|
||||
const BACKEND_INFO = "VINFO";
|
||||
|
||||
const LIMITS_INFO = "LIMITS_all";
|
||||
|
||||
const CURRENT_BATT = "BATTERY_current_now";
|
||||
const CHARGE_RATE_BATT = "BATTERY_charge_rate";
|
||||
const CHARGE_NOW_BATT = "BATTERY_charge_now";
|
||||
const CHARGE_FULL_BATT = "BATTERY_charge_full";
|
||||
const CHARGE_DESIGN_BATT = "BATTERY_charge_design"
|
||||
|
||||
const TOTAL_CPUS = "CPUs_total";
|
||||
//const TOTAL_CPUS = "CPUs_total";
|
||||
const ONLINE_CPUS = "CPUs_online";
|
||||
const ONLINE_STATUS_CPUS = "CPUs_status_online";
|
||||
const SMT_CPU = "CPUs_SMT";
|
||||
|
@ -107,7 +84,7 @@ function countCpus(statii: boolean[]): number {
|
|||
}
|
||||
|
||||
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 maxClock = get_value(CLOCK_MAX_CPU);
|
||||
let clockArr = [];
|
||||
|
@ -123,18 +100,23 @@ function syncPlebClockToAdvanced() {
|
|||
const reload = function() {
|
||||
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.getBatteryChargeRate(), (rate: number) => { set_value(CHARGE_RATE_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.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[]) => {
|
||||
set_value(ONLINE_STATUS_CPUS, statii);
|
||||
const count = countCpus(statii);
|
||||
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[]) => {
|
||||
set_value(CLOCK_MIN_CPU, limits[0]);
|
||||
|
@ -144,7 +126,6 @@ const reload = function() {
|
|||
backend.resolve(backend.getCpusGovernor(), (governors: string[]) => {
|
||||
set_value(GOVERNOR_CPU, governors);
|
||||
console.log("POWERTOOLS: Governors from backend", governors);
|
||||
console.log("POWERTOOLS: Governors in dropdown", governorOptions);
|
||||
});
|
||||
|
||||
backend.resolve(backend.getGpuPpt(), (ppts: number[]) => {
|
||||
|
@ -226,10 +207,16 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
reloadGUI("periodic" + (new Date()).getTime().toString());
|
||||
}, 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 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 (
|
||||
<PanelSection>
|
||||
|
@ -280,7 +267,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
label="Threads"
|
||||
value={get_value(ONLINE_CPUS)}
|
||||
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}
|
||||
showValue={true}
|
||||
onChange={(cpus: number) => {
|
||||
|
@ -307,13 +294,17 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
</PanelSectionRow>}
|
||||
{!advancedMode && <PanelSectionRow>
|
||||
<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"
|
||||
description="Set bounds on clock speed"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(CLOCK_MIN_CPU, 1400);
|
||||
set_value(CLOCK_MAX_CPU, 3500);
|
||||
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits != null) {
|
||||
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();
|
||||
reloadGUI("CPUFreqToggle");
|
||||
} else {
|
||||
|
@ -330,13 +321,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}}
|
||||
/>
|
||||
</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
|
||||
label="Minimum (MHz)"
|
||||
value={get_value(CLOCK_MIN_CPU)}
|
||||
max={3500}
|
||||
min={1400}
|
||||
step={100}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_min_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MIN_CPU) == null}
|
||||
onChange={(freq: number) => {
|
||||
|
@ -360,13 +351,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}}
|
||||
/>}
|
||||
</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
|
||||
label="Maximum (MHz)"
|
||||
value={get_value(CLOCK_MAX_CPU)}
|
||||
max={3500}
|
||||
min={500}
|
||||
step={100}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_max_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_max_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[0].clock_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MAX_CPU) == null}
|
||||
onChange={(freq: number) => {
|
||||
|
@ -393,10 +384,10 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
{/* CPU advanced mode */}
|
||||
{advancedMode && <PanelSectionRow>
|
||||
<SliderField
|
||||
label="CPU to modify"
|
||||
label="Selected CPU"
|
||||
value={advancedCpu}
|
||||
step={1}
|
||||
max={8}
|
||||
max={total_cpus}
|
||||
min={1}
|
||||
showValue={true}
|
||||
onChange={(cpuNum: number) => {
|
||||
|
@ -406,9 +397,9 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
</PanelSectionRow>}
|
||||
{advancedMode && <PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(ONLINE_CPUS)[advancedCpuIndex]}
|
||||
checked={get_value(ONLINE_STATUS_CPUS)[advancedCpuIndex]}
|
||||
label="Online"
|
||||
description="Allow the CPU thread to do processing"
|
||||
description="Allow the CPU thread to do work"
|
||||
onChange={(status: boolean) => {
|
||||
console.debug("CPU " + advancedCpu.toString() + " is now " + status.toString());
|
||||
if (get_value(SMT_CPU)) {
|
||||
|
@ -426,14 +417,19 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
</PanelSectionRow>}
|
||||
{advancedMode && <PanelSectionRow>
|
||||
<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"
|
||||
description="Set bounds on clock speed"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
const clocks = get_value(CLOCK_MIN_MAX_CPU) as MinMax[];
|
||||
clocks[advancedCpuIndex].min = 1400;
|
||||
clocks[advancedCpuIndex].max = 3500;
|
||||
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits != null) {
|
||||
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);
|
||||
reloadGUI("CPUFreqToggle");
|
||||
} else {
|
||||
|
@ -448,13 +444,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}}
|
||||
/>
|
||||
</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
|
||||
label="Minimum (MHz)"
|
||||
value={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min}
|
||||
max={3500}
|
||||
min={1400}
|
||||
step={100}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_min_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min == null}
|
||||
onChange={(freq: number) => {
|
||||
|
@ -473,13 +469,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}}
|
||||
/>}
|
||||
</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
|
||||
label="Maximum (MHz)"
|
||||
value={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max}
|
||||
max={3500}
|
||||
min={500}
|
||||
step={100}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_max_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_max_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].clock_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max == null}
|
||||
onChange={(freq: number) => {
|
||||
|
@ -498,7 +494,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}}
|
||||
/>}
|
||||
</PanelSectionRow>}
|
||||
{advancedMode && <PanelSectionRow>
|
||||
{advancedMode && (get_value(LIMITS_INFO) as backend.SettingsLimits).cpu.cpus[advancedCpuIndex].governors.length != 0 && <PanelSectionRow>
|
||||
<Field
|
||||
label="Governor"
|
||||
>
|
||||
|
@ -527,15 +523,20 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
<div className={staticClasses.PanelSectionTitle}>
|
||||
GPU
|
||||
</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
|
||||
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"
|
||||
description="Override APU TDP settings"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(SLOW_PPT_GPU, 15000000);
|
||||
set_value(FAST_PPT_GPU, 15000000);
|
||||
if ((get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits != null) {
|
||||
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");
|
||||
} else {
|
||||
set_value(SLOW_PPT_GPU, null);
|
||||
|
@ -546,21 +547,22 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
{ get_value(SLOW_PPT_GPU) != null && <SliderField
|
||||
label="SlowPPT (uW)"
|
||||
label="SlowPPT (W)"
|
||||
value={get_value(SLOW_PPT_GPU)}
|
||||
max={29000000}
|
||||
min={1000000}
|
||||
step={1000000}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.slow_ppt_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.ppt_step}
|
||||
showValue={true}
|
||||
disabled={get_value(SLOW_PPT_GPU) == null}
|
||||
onChange={(ppt: number) => {
|
||||
console.debug("SlowPPT is now " + ppt.toString());
|
||||
const pptNow = get_value(SLOW_PPT_GPU);
|
||||
if (ppt != pptNow) {
|
||||
backend.resolve(backend.setGpuPpt(get_value(FAST_PPT_GPU), ppt),
|
||||
const realPpt = ppt;
|
||||
if (realPpt != pptNow) {
|
||||
backend.resolve(backend.setGpuPpt(get_value(FAST_PPT_GPU), realPpt),
|
||||
(limits: number[]) => {
|
||||
set_value(FAST_PPT_GPU, limits[0]);
|
||||
set_value(SLOW_PPT_GPU, limits[1]);
|
||||
|
@ -572,18 +574,19 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{get_value(FAST_PPT_GPU) != null && <SliderField
|
||||
label="FastPPT (uW)"
|
||||
label="FastPPT (W)"
|
||||
value={get_value(FAST_PPT_GPU)}
|
||||
max={29000000}
|
||||
min={1000000}
|
||||
step={1000000}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.fast_ppt_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.fast_ppt_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.ppt_step}
|
||||
showValue={true}
|
||||
disabled={get_value(FAST_PPT_GPU) == null}
|
||||
onChange={(ppt: number) => {
|
||||
console.debug("FastPPT is now " + ppt.toString());
|
||||
const pptNow = get_value(FAST_PPT_GPU);
|
||||
if (ppt != pptNow) {
|
||||
backend.resolve(backend.setGpuPpt(get_value(SLOW_PPT_GPU), ppt),
|
||||
const realPpt = ppt;
|
||||
if (realPpt != pptNow) {
|
||||
backend.resolve(backend.setGpuPpt(realPpt, get_value(SLOW_PPT_GPU)),
|
||||
(limits: number[]) => {
|
||||
set_value(FAST_PPT_GPU, limits[0]);
|
||||
set_value(SLOW_PPT_GPU, limits[1]);
|
||||
|
@ -593,15 +596,21 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}}
|
||||
/>}
|
||||
</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
|
||||
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"
|
||||
description="Override bounds on gpu clock"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(CLOCK_MIN_GPU, 200);
|
||||
set_value(CLOCK_MAX_GPU, 1600);
|
||||
let clock_min_limits = (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_min_limits;
|
||||
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");
|
||||
} else {
|
||||
set_value(CLOCK_MIN_GPU, null);
|
||||
|
@ -612,14 +621,14 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
{ get_value(CLOCK_MIN_GPU) != null && <SliderField
|
||||
label="Minimum (MHz)"
|
||||
value={get_value(CLOCK_MIN_GPU)}
|
||||
max={1600}
|
||||
min={200}
|
||||
step={100}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_min_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_min_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MIN_GPU) == null}
|
||||
onChange={(val: number) => {
|
||||
|
@ -640,9 +649,9 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
{get_value(CLOCK_MAX_GPU) != null && <SliderField
|
||||
label="Maximum (MHz)"
|
||||
value={get_value(CLOCK_MAX_GPU)}
|
||||
max={1600}
|
||||
min={200}
|
||||
step={100}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_max_limits!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_max_limits!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.clock_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MAX_GPU) == null}
|
||||
onChange={(val: number) => {
|
||||
|
@ -659,7 +668,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control_capable && <PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(SLOW_MEMORY_GPU)}
|
||||
label="Downclock Memory"
|
||||
|
@ -671,36 +680,28 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
})
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
</PanelSectionRow>}
|
||||
{/* Battery */}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
Battery
|
||||
</div>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Now (Charge)
|
||||
</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)}%)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<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)}%)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{get_value(CHARGE_NOW_BATT) != null && get_value(CHARGE_FULL_BATT) != null && <PanelSectionRow>
|
||||
<Field
|
||||
label="Now (Charge)"
|
||||
onClick={()=> eggCount++}
|
||||
focusable={false}>
|
||||
{get_value(CHARGE_NOW_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_NOW_BATT) / get_value(CHARGE_FULL_BATT)).toFixed(1)}%)
|
||||
</Field>
|
||||
</PanelSectionRow>}
|
||||
{get_value(CHARGE_FULL_BATT) != null && get_value(CHARGE_DESIGN_BATT) != null && <PanelSectionRow>
|
||||
<Field
|
||||
label="Max (Design)"
|
||||
onClick={()=> eggCount++}
|
||||
focusable={false}>
|
||||
{get_value(CHARGE_FULL_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_FULL_BATT) / get_value(CHARGE_DESIGN_BATT)).toFixed(1)}%)
|
||||
</Field>
|
||||
</PanelSectionRow>}
|
||||
{(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate != null && <PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(CHARGE_RATE_BATT) != null}
|
||||
label="Charge Current Limits"
|
||||
|
@ -720,9 +721,9 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
{ get_value(CHARGE_RATE_BATT) != null && <SliderField
|
||||
label="Maximum (mA)"
|
||||
value={get_value(CHARGE_RATE_BATT)}
|
||||
max={2500}
|
||||
min={250}
|
||||
step={50}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CHARGE_RATE_BATT) == null}
|
||||
onChange={(val: number) => {
|
||||
|
@ -737,18 +738,14 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Current
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{get_value(CURRENT_BATT)} mA
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Field
|
||||
label="Current"
|
||||
onClick={()=> eggCount++}
|
||||
focusable={false}>
|
||||
{get_value(CURRENT_BATT)} mA
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
{/* Persistence */}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
|
@ -769,56 +766,51 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Profile
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{get_value(NAME_GEN)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Field
|
||||
label="Profile"
|
||||
onClick={()=> eggCount++}
|
||||
focusable={false}>
|
||||
{get_value(NAME_GEN)}
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
{/* Version Info */}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
Debug
|
||||
{eggCount % 10 == 9 ? "Ha! Nerd" : "Debug"}
|
||||
</div>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Native
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{get_value(BACKEND_INFO)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Field
|
||||
label={eggCount % 10 == 9 ? "PowerTools" : "Native"}
|
||||
onClick={()=> {
|
||||
if (eggCount % 10 == 9) {
|
||||
// you know you're bored and/or conceited when you spend time adding an easter egg
|
||||
// that just sends people to your own project's repo
|
||||
Router.NavigateToExternalWeb("https://github.com/NGnius/PowerTools");
|
||||
}
|
||||
eggCount++;
|
||||
}}>
|
||||
{eggCount % 10 == 9 ? "by NGnius" : get_value(BACKEND_INFO)}
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Framework
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{target_usdpl()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Field
|
||||
label="Framework"
|
||||
onClick={()=> eggCount++}>
|
||||
{eggCount % 10 == 9 ? "<3 <3 <3" : target_usdpl()}
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
USDPL
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
v{version_usdpl()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Field
|
||||
label="USDPL"
|
||||
onClick={()=> {
|
||||
if (eggCount % 10 == 9) {
|
||||
// you know you're bored and/or conceited when you spend time adding an easter egg
|
||||
// that just sends people to your own project's repo
|
||||
Router.NavigateToExternalWeb("https://github.com/NGnius/usdpl-rs");
|
||||
}
|
||||
eggCount++;
|
||||
}}>
|
||||
v{version_usdpl()}
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ButtonItem
|
||||
|
|
Loading…
Reference in a new issue