From 64cb4193f9cf997e61075a2c24ba40d27bac37cb Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 6 Aug 2023 14:49:19 -0400 Subject: [PATCH] Improve sysfs calls from providers to intelligently find the correct entry to fix #114 --- backend/Cargo.lock | 5 + backend/Cargo.toml | 1 + backend/src/main.rs | 9 +- backend/src/persist/battery.rs | 3 + backend/src/persist/cpu.rs | 3 + backend/src/persist/gpu.rs | 3 + backend/src/settings/generic/battery.rs | 32 +++- backend/src/settings/generic/cpu.rs | 5 + backend/src/settings/generic/gpu.rs | 22 +++ backend/src/settings/steam_deck/battery.rs | 180 +++++++++++++----- backend/src/settings/steam_deck/cpu.rs | 67 +++++-- backend/src/settings/steam_deck/gpu.rs | 139 +++++++++----- backend/src/settings/steam_deck/mod.rs | 2 +- .../settings/steam_deck/power_dpm_force.rs | 34 ++-- backend/src/settings/steam_deck/util.rs | 3 + backend/src/settings/unknown/battery.rs | 1 + backend/src/settings/unknown/cpu.rs | 5 + backend/src/settings/unknown/gpu.rs | 1 + backend/src/settings/util.rs | 12 ++ backend/src/utility.rs | 3 + src/components/message.tsx | 4 +- 21 files changed, 387 insertions(+), 147 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 107ceca..4d5882f 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1053,6 +1053,7 @@ dependencies = [ "serde", "serde_json", "simplelog", + "sysfuss", "tokio", "ureq", "usdpl-back", @@ -1296,6 +1297,10 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysfuss" +version = "0.1.0" + [[package]] name = "termcolor" version = "1.1.3" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 042454a..43d4a7b 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -15,6 +15,7 @@ readme = "../README.md" usdpl-back = { version = "0.10.1", features = ["blocking"] }#, path = "../../usdpl-rs/usdpl-back"} serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +sysfuss = { version = "0.1", path = "../../sysfs-nav", features = ["derive"] } # async tokio = { version = "*", features = ["time"] } diff --git a/backend/src/main.rs b/backend/src/main.rs index 9a97973..41989d3 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -73,10 +73,6 @@ fn main() -> Result<(), ()> { } let _limits_handle = crate::settings::limits_worker_spawn(); - log::info!( - "Detected device automatically, starting with driver: {:?} (This can be overriden)", - crate::settings::auto_detect_provider() - ); let mut loaded_settings = persist::SettingsJson::open(utility::settings_dir().join(DEFAULT_SETTINGS_FILE)) @@ -88,6 +84,11 @@ fn main() -> Result<(), ()> { ) }); + log::info!( + "Detected device automatically {:?}, using driver: {:?} (This can be overriden)", + crate::settings::auto_detect_provider(), loaded_settings.cpus.provider() + ); + log::debug!("Settings: {:?}", loaded_settings); let (api_handler, api_sender) = crate::api::handler::ApiMessageHandler::new(); diff --git a/backend/src/persist/battery.rs b/backend/src/persist/battery.rs index 1e8ed57..57d33bb 100644 --- a/backend/src/persist/battery.rs +++ b/backend/src/persist/battery.rs @@ -9,6 +9,8 @@ pub struct BatteryJson { pub charge_mode: Option, #[serde(default)] pub events: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub root: Option, } #[derive(Serialize, Deserialize, Clone)] @@ -24,6 +26,7 @@ impl Default for BatteryJson { charge_rate: None, charge_mode: None, events: Vec::new(), + root: None, } } } diff --git a/backend/src/persist/cpu.rs b/backend/src/persist/cpu.rs index 442206a..e515311 100644 --- a/backend/src/persist/cpu.rs +++ b/backend/src/persist/cpu.rs @@ -12,6 +12,8 @@ pub struct CpuJson { pub online: bool, pub clock_limits: Option>, pub governor: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub root: Option, } impl Default for CpuJson { @@ -20,6 +22,7 @@ impl Default for CpuJson { online: true, clock_limits: None, governor: "schedutil".to_owned(), + root: None, } } } diff --git a/backend/src/persist/gpu.rs b/backend/src/persist/gpu.rs index 933f2c4..7755847 100644 --- a/backend/src/persist/gpu.rs +++ b/backend/src/persist/gpu.rs @@ -10,6 +10,8 @@ pub struct GpuJson { pub slow_ppt: Option, pub clock_limits: Option>, pub slow_memory: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub root: Option, } impl Default for GpuJson { @@ -19,6 +21,7 @@ impl Default for GpuJson { slow_ppt: None, clock_limits: None, slow_memory: false, + root: None, } } } diff --git a/backend/src/settings/generic/battery.rs b/backend/src/settings/generic/battery.rs index 3390f1f..81464dd 100644 --- a/backend/src/settings/generic/battery.rs +++ b/backend/src/settings/generic/battery.rs @@ -1,6 +1,7 @@ use std::convert::Into; use limits_core::json::GenericBatteryLimit; +use sysfuss::SysEntity; use crate::persist::BatteryJson; use crate::settings::TBattery; @@ -10,6 +11,7 @@ use crate::settings::{OnResume, OnSet, SettingError}; pub struct Battery { #[allow(dead_code)] limits: GenericBatteryLimit, + sysfs: sysfuss::PowerSupplyPath, } impl Into for Battery { @@ -19,6 +21,7 @@ impl Into for Battery { charge_rate: None, charge_mode: None, events: Vec::default(), + root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|s| s.to_owned())), } } } @@ -37,18 +40,41 @@ impl Battery { } } + fn find_psu_sysfs(root: Option>) -> sysfuss::PowerSupplyPath { + let root = crate::settings::util::root_or_default_sysfs(root); + match root.power_supply(crate::settings::util::always_satisfied) { + Ok(mut iter) => { + iter.next() + .unwrap_or_else(|| { + log::error!("Failed to find generic battery power_supply in sysfs (no results), using naive fallback"); + root.power_supply_by_name("BAT0") + }) + }, + Err(e) => { + log::error!("Failed to find generic battery power_supply in sysfs ({}), using naive fallback", e); + root.power_supply_by_name("BAT0") + } + } + } + pub fn from_limits(limits: limits_core::json::GenericBatteryLimit) -> Self { // TODO - Self { limits } + Self { + limits, + sysfs: Self::find_psu_sysfs(None::<&'static str>), + } } pub fn from_json_and_limits( - _other: BatteryJson, + other: BatteryJson, _version: u64, limits: limits_core::json::GenericBatteryLimit, ) -> Self { // TODO - Self { limits } + Self { + limits, + sysfs: Self::find_psu_sysfs(other.root) + } } } diff --git a/backend/src/settings/generic/cpu.rs b/backend/src/settings/generic/cpu.rs index b821053..00f7dbe 100644 --- a/backend/src/settings/generic/cpu.rs +++ b/backend/src/settings/generic/cpu.rs @@ -202,6 +202,7 @@ pub struct Cpu { limits: GenericCpuLimit, index: usize, state: crate::state::steam_deck::Cpu, + root: std::path::PathBuf, } /*impl Cpu { @@ -235,6 +236,7 @@ impl FromGenericCpuInfo for Cpu { limits, index: cpu_index, state: crate::state::steam_deck::Cpu::default(), + root: "/".into(), } } @@ -258,6 +260,7 @@ impl FromGenericCpuInfo for Cpu { limits, index: i, state: crate::state::steam_deck::Cpu::default(), + root: other.root.unwrap_or_else(|| "/".to_string()).into(), }, _ => Self { online: other.online, @@ -266,6 +269,7 @@ impl FromGenericCpuInfo for Cpu { limits, index: i, state: crate::state::steam_deck::Cpu::default(), + root: other.root.unwrap_or_else(|| "/".to_string()).into(), }, } } @@ -354,6 +358,7 @@ impl Into for Cpu { online: self.online, clock_limits: self.clock_limits.map(|x| x.into()), governor: self.governor, + root: self.root.to_str().map(|s| s.to_owned()), } } } diff --git a/backend/src/settings/generic/gpu.rs b/backend/src/settings/generic/gpu.rs index b388df1..b8fcb6c 100644 --- a/backend/src/settings/generic/gpu.rs +++ b/backend/src/settings/generic/gpu.rs @@ -1,6 +1,7 @@ use std::convert::Into; use limits_core::json::GenericGpuLimit; +use sysfuss::{BasicEntityPath, SysEntity}; use crate::api::RangeLimit; use crate::persist::GpuJson; @@ -15,6 +16,7 @@ pub struct Gpu { pub slow_ppt: Option, pub clock_limits: Option>, limits: GenericGpuLimit, + sysfs: BasicEntityPath, } impl Gpu { @@ -31,6 +33,23 @@ impl Gpu { } }*/ + fn find_card_sysfs(root: Option>) -> BasicEntityPath { + let root = crate::settings::util::root_or_default_sysfs(root); + match root.class("drm", crate::settings::util::always_satisfied) { + Ok(mut iter) => { + iter.next() + .unwrap_or_else(|| { + log::error!("Failed to find generic gpu drm in sysfs (no results), using naive fallback"); + BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0")) + }) + }, + Err(e) => { + log::error!("Failed to find generic gpu drm in sysfs ({}), using naive fallback", e); + BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0")) + } + } + } + pub fn from_limits(limits: limits_core::json::GenericGpuLimit) -> Self { Self { slow_memory: false, @@ -38,6 +57,7 @@ impl Gpu { slow_ppt: None, clock_limits: None, limits, + sysfs: Self::find_card_sysfs(None::<&'static str>), } } @@ -65,6 +85,7 @@ impl Gpu { }, clock_limits: clock_lims, limits, + sysfs: Self::find_card_sysfs(other.root) } } } @@ -77,6 +98,7 @@ impl Into for Gpu { slow_ppt: self.slow_ppt, clock_limits: self.clock_limits.map(|x| x.into()), slow_memory: false, + root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|s| s.to_owned())) } } } diff --git a/backend/src/settings/steam_deck/battery.rs b/backend/src/settings/steam_deck/battery.rs index 2405380..a4327fc 100644 --- a/backend/src/settings/steam_deck/battery.rs +++ b/backend/src/settings/steam_deck/battery.rs @@ -1,4 +1,8 @@ use std::convert::Into; +use std::sync::Arc; + +use sysfuss::{PowerSupplyAttribute, PowerSupplyPath, HwMonAttribute, HwMonAttributeItem, HwMonAttributeType, HwMonPath, SysEntity, SysEntityAttributesExt, SysAttribute}; +use sysfuss::capability::attributes; use super::oc_limits::{BatteryLimits, OverclockLimits}; use super::util::ChargeMode; @@ -15,6 +19,8 @@ pub struct Battery { limits: BatteryLimits, state: crate::state::steam_deck::Battery, driver_mode: crate::persist::DriverJson, + sysfs_bat: PowerSupplyPath, + sysfs_hwmon: Arc, } #[derive(Debug, Clone)] @@ -32,6 +38,7 @@ struct EventInstruction { charge_rate: Option, charge_mode: Option, is_triggered: bool, + sysfs_hwmon: Arc, } impl OnPowerEvent for EventInstruction { @@ -109,7 +116,7 @@ impl EventInstruction { } } - fn from_json(other: BatteryEventJson, _version: u64) -> Self { + fn from_json(other: BatteryEventJson, _version: u64, hwmon: Arc) -> Self { Self { trigger: Self::str_to_trigger(&other.trigger).unwrap_or(EventTrigger::Ignored), charge_rate: other.charge_rate, @@ -118,6 +125,7 @@ impl EventInstruction { .map(|x| Battery::str_to_charge_mode(&x)) .flatten(), is_triggered: false, + sysfs_hwmon: hwmon, } } @@ -136,12 +144,13 @@ impl EventInstruction { fn set_charge_rate(&self) -> Result<(), SettingError> { if let Some(charge_rate) = self.charge_rate { - usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, charge_rate) - .map_err(|e| SettingError { - msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e), + let attr = HwMonAttribute::custom("maximum_battery_charge_rate"); + self.sysfs_hwmon.set(attr, charge_rate).map_err( + |e| SettingError { + msg: format!("Failed to write to `{:?}`: {}", attr, e), setting: crate::settings::SettingVariant::Battery, - }) - .map(|_| ()) + }, + ) } else { Ok(()) } @@ -173,13 +182,32 @@ impl Into for EventInstruction { const BATTERY_VOLTAGE: f64 = 7.7; -const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only +/*const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only const BATTERY_CURRENT_NOW_PATH: &str = "/sys/class/power_supply/BAT1/current_now"; // read-only const BATTERY_CHARGE_NOW_PATH: &str = "/sys/class/power_supply/BAT1/charge_now"; // read-only const BATTERY_CHARGE_FULL_PATH: &str = "/sys/class/power_supply/BAT1/charge_full"; // read-only const BATTERY_CHARGE_DESIGN_PATH: &str = "/sys/class/power_supply/BAT1/charge_full_design"; // read-only const USB_PD_IN_MVOLTAGE_PATH: &str = "/sys/class/hwmon/hwmon5/in0_input"; // read-only -const USB_PD_IN_CURRENT_PATH: &str = "/sys/class/hwmon/hwmon5/curr1_input"; // read-only +const USB_PD_IN_CURRENT_PATH: &str = "/sys/class/hwmon/hwmon5/curr1_input"; // read-only*/ + + +const BATTERY_NEEDS: &[PowerSupplyAttribute] = &[ + PowerSupplyAttribute::Type, + PowerSupplyAttribute::CurrentNow, + PowerSupplyAttribute::ChargeNow, + PowerSupplyAttribute::ChargeFull, + PowerSupplyAttribute::ChargeFullDesign, + PowerSupplyAttribute::CycleCount, + PowerSupplyAttribute::Capacity, + PowerSupplyAttribute::CapacityLevel, +]; + +const HWMON_NEEDS: &[HwMonAttribute] = &[ + HwMonAttribute::name(), + HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Input), + HwMonAttribute::new(HwMonAttributeType::Curr, 1, HwMonAttributeItem::Input), + //HwMonAttribute::custom("maximum_battery_charge_rate"), // NOTE: Cannot filter by custom capabilities +]; impl Battery { #[inline] @@ -191,6 +219,7 @@ impl Battery { } else { crate::persist::DriverJson::SteamDeckAdvance }; + let hwmon_sys = Arc::new(Self::find_hwmon_sysfs(None::<&'static str>)); match version { 0 => Self { charge_rate: other.charge_rate, @@ -201,11 +230,13 @@ impl Battery { events: other .events .into_iter() - .map(|x| EventInstruction::from_json(x, version)) + .map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone())) .collect(), limits: oc_limits, state: crate::state::steam_deck::Battery::default(), driver_mode: driver, + sysfs_bat: Self::find_battery_sysfs(None::<&'static str>), + sysfs_hwmon: hwmon_sys, }, _ => Self { charge_rate: other.charge_rate, @@ -216,15 +247,52 @@ impl Battery { events: other .events .into_iter() - .map(|x| EventInstruction::from_json(x, version)) + .map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone())) .collect(), limits: oc_limits, state: crate::state::steam_deck::Battery::default(), driver_mode: driver, + sysfs_bat: Self::find_battery_sysfs(None::<&'static str>), + sysfs_hwmon: hwmon_sys, }, } } + fn find_battery_sysfs(root: Option>) -> PowerSupplyPath { + let root = crate::settings::util::root_or_default_sysfs(root); + match root.power_supply(attributes(BATTERY_NEEDS.into_iter().copied())) { + Ok(mut iter) => { + iter.next() + .unwrap_or_else(|| { + log::error!("Failed to find SteamDeck battery power_supply in sysfs (no results), using naive fallback"); + root.power_supply_by_name("BAT1") + }) + }, + Err(e) => { + log::error!("Failed to find SteamDeck battery power_supply in sysfs ({}), using naive fallback", e); + root.power_supply_by_name("BAT1") + } + } + } + + fn find_hwmon_sysfs(root: Option>) -> HwMonPath { + let root = crate::settings::util::root_or_default_sysfs(root); + match root.hwmon_by_name(super::util::JUPITER_HWMON_NAME) { + Ok(hwmon) => { + if hwmon.capable(attributes(HWMON_NEEDS.into_iter().copied())) { + hwmon + } else { + log::error!("Failed to find SteamDeck battery hwmon in sysfs (hwmon by name {} exists but missing attributes), using naive fallback", super::util::JUPITER_HWMON_NAME); + root.hwmon_by_index(5) + } + }, + Err(e) => { + log::error!("Failed to find SteamDeck battery hwmon in sysfs ({}), using naive fallback", e); + root.hwmon_by_index(5) + } + } + } + #[inline] fn charge_mode_to_str(mode: ChargeMode) -> String { match mode { @@ -248,22 +316,24 @@ impl Battery { fn set_charge_rate(&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( + let attr = HwMonAttribute::custom("maximum_battery_charge_rate"); + let path = attr.path(&*self.sysfs_hwmon); + self.sysfs_hwmon.set(attr, charge_rate).map_err( |e| SettingError { - msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e), + msg: format!("Failed to write to `{}`: {}", path.display(), 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.limits.charge_rate.max, + let attr = HwMonAttribute::custom("maximum_battery_charge_rate"); + let path = attr.path(&*self.sysfs_hwmon); + self.sysfs_hwmon.set(attr, self.limits.charge_rate.max,).map_err( + |e| SettingError { + msg: format!("Failed to write to `{}`: {}", path.display(), e), + setting: crate::settings::SettingVariant::Battery, + }, ) - .map_err(|e| SettingError { - msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e), - setting: crate::settings::SettingVariant::Battery, - }) } else { Ok(()) } @@ -309,10 +379,11 @@ impl Battery { } } - pub fn read_current_now() -> Result { - match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) { + pub fn read_current_now(&self) -> Result { + let attr = PowerSupplyAttribute::CurrentNow; + match self.sysfs_bat.attribute::(attr) { Err(e) => Err(SettingError { - msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e), + msg: format!("Failed to read from `{:?}`: {}", attr, e), setting: crate::settings::SettingVariant::Battery, }), // this value is in uA, while it's set in mA @@ -321,16 +392,17 @@ impl Battery { } } - pub fn read_charge_power() -> Result { - let current = Self::read_usb_current()?; - let voltage = Self::read_usb_voltage()?; + pub fn read_charge_power(&self) -> Result { + let current = self.read_usb_current()?; + let voltage = self.read_usb_voltage()?; Ok(current * voltage) } - pub fn read_charge_now() -> Result { - match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_NOW_PATH) { + pub fn read_charge_now(&self) -> Result { + let attr = PowerSupplyAttribute::ChargeNow; + match self.sysfs_bat.attribute::(attr) { Err(e) => Err(SettingError { - msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e), + msg: format!("Failed to read from `{:?}`: {}", attr, e), setting: crate::settings::SettingVariant::Battery, }), // convert to Wh @@ -338,10 +410,11 @@ impl Battery { } } - pub fn read_charge_full() -> Result { - match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_FULL_PATH) { + pub fn read_charge_full(&self) -> Result { + let attr = PowerSupplyAttribute::ChargeFull; + match self.sysfs_bat.attribute::(attr) { Err(e) => Err(SettingError { - msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e), + msg: format!("Failed to read from `{:?}`: {}", attr, e), setting: crate::settings::SettingVariant::Battery, }), // convert to Wh @@ -349,13 +422,11 @@ impl Battery { } } - pub fn read_charge_design() -> Result { - match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_DESIGN_PATH) { + pub fn read_charge_design(&self) -> Result { + let attr = PowerSupplyAttribute::ChargeFullDesign; + match self.sysfs_bat.attribute::(attr) { Err(e) => Err(SettingError { - msg: format!( - "Failed to read from `{}`: {}", - BATTERY_CHARGE_DESIGN_PATH, e - ), + msg: format!("Failed to read from `{:?}`: {}", attr, e), setting: crate::settings::SettingVariant::Battery, }), // convert to Wh @@ -363,10 +434,11 @@ impl Battery { } } - pub fn read_usb_voltage() -> Result { - match usdpl_back::api::files::read_single::<_, u64, _>(USB_PD_IN_MVOLTAGE_PATH) { + pub fn read_usb_voltage(&self) -> Result { + let attr = HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Input); + match self.sysfs_hwmon.attribute::(attr) { Err(e) => Err(SettingError { - msg: format!("Failed to read from `{}`: {}", USB_PD_IN_MVOLTAGE_PATH, e), + msg: format!("Failed to read from `{:?}`: {}", attr, e), setting: crate::settings::SettingVariant::Battery, }), // convert to V (from mV) @@ -374,10 +446,11 @@ impl Battery { } } - pub fn read_usb_current() -> Result { - match usdpl_back::api::files::read_single::<_, u64, _>(USB_PD_IN_CURRENT_PATH) { + pub fn read_usb_current(&self) -> Result { + let attr = HwMonAttribute::new(HwMonAttributeType::Curr, 1, HwMonAttributeItem::Input); + match self.sysfs_hwmon.attribute::(attr) { Err(e) => Err(SettingError { - msg: format!("Failed to read from `{}`: {}", USB_PD_IN_CURRENT_PATH, e), + msg: format!("Failed to read `{:?}`: {}", attr, e), setting: crate::settings::SettingVariant::Battery, }), Ok(val) => Ok((val as f64) / 1000.0), // mA -> A @@ -399,6 +472,8 @@ impl Battery { limits: oc_limits, state: crate::state::steam_deck::Battery::default(), driver_mode: driver, + sysfs_bat: Self::find_battery_sysfs(None::<&'static str>), + sysfs_hwmon: Arc::new(Self::find_hwmon_sysfs(None::<&'static str>)), } } @@ -438,6 +513,7 @@ impl Into for Battery { charge_rate: self.charge_rate, charge_mode: self.charge_mode.map(Self::charge_mode_to_str), events: self.events.into_iter().map(|x| x.into()).collect(), + root: self.sysfs_bat.root().or(self.sysfs_hwmon.root()).and_then(|p| p.as_ref().to_str().map(|x| x.to_owned())) } } } @@ -530,7 +606,7 @@ impl TBattery for Battery { } fn read_charge_full(&self) -> Option { - match Self::read_charge_full() { + match self.read_charge_full() { Ok(x) => Some(x), Err(e) => { log::warn!("read_charge_full err: {}", e.msg); @@ -540,7 +616,7 @@ impl TBattery for Battery { } fn read_charge_now(&self) -> Option { - match Self::read_charge_now() { + match self.read_charge_now() { Ok(x) => Some(x), Err(e) => { log::warn!("read_charge_now err: {}", e.msg); @@ -550,7 +626,7 @@ impl TBattery for Battery { } fn read_charge_design(&self) -> Option { - match Self::read_charge_design() { + match self.read_charge_design() { Ok(x) => Some(x), Err(e) => { log::warn!("read_charge_design err: {}", e.msg); @@ -561,7 +637,7 @@ impl TBattery for Battery { fn read_current_now(&self) -> Option { if self.limits.extra_readouts { - match Self::read_current_now() { + match self.read_current_now() { Ok(x) => Some(x as f64), Err(e) => { log::warn!("read_current_now err: {}", e.msg); @@ -575,7 +651,7 @@ impl TBattery for Battery { fn read_charge_power(&self) -> Option { if self.limits.extra_readouts { - match Self::read_charge_power() { + match self.read_charge_power() { Ok(x) => Some(x as f64), Err(e) => { log::warn!("read_current_now err: {}", e.msg); @@ -601,6 +677,7 @@ impl TBattery for Battery { charge_rate: None, charge_mode: Some(ChargeMode::Idle), is_triggered: false, + sysfs_hwmon: self.sysfs_hwmon.clone(), }; } else { self.events.remove(index); @@ -615,6 +692,7 @@ impl TBattery for Battery { charge_rate: None, charge_mode: Some(ChargeMode::Idle), is_triggered: false, + sysfs_hwmon: self.sysfs_hwmon.clone(), }); } // lower limit @@ -631,6 +709,7 @@ impl TBattery for Battery { charge_rate: None, charge_mode: Some(ChargeMode::Normal), is_triggered: false, + sysfs_hwmon: self.sysfs_hwmon.clone(), }; } else { self.events.remove(index); @@ -646,6 +725,7 @@ impl TBattery for Battery { charge_rate: None, charge_mode: Some(ChargeMode::Normal), is_triggered: false, + sysfs_hwmon: self.sysfs_hwmon.clone(), }); } } @@ -668,7 +748,7 @@ impl TBattery for Battery { log::debug!("Steam Deck power vibe check"); let mut errors = Vec::new(); let mut events = Vec::new(); - match (Self::read_charge_full(), Self::read_charge_now()) { + match (self.read_charge_full(), self.read_charge_now()) { (Ok(full), Ok(now)) => events.push(PowerMode::BatteryCharge(now / full)), (Err(e1), Err(e2)) => { errors.push(e1); @@ -677,7 +757,7 @@ impl TBattery for Battery { (Err(e), _) => errors.push(e), (_, Err(e)) => errors.push(e), } - match Self::read_usb_voltage() { + match self.read_usb_voltage() { Ok(voltage) => { if voltage > 0.0 && self.state.charger_state != crate::state::steam_deck::ChargeState::PluggedIn diff --git a/backend/src/settings/steam_deck/cpu.rs b/backend/src/settings/steam_deck/cpu.rs index cf4bd74..36f24c4 100644 --- a/backend/src/settings/steam_deck/cpu.rs +++ b/backend/src/settings/steam_deck/cpu.rs @@ -1,5 +1,7 @@ use std::convert::Into; +use sysfuss::{BasicEntityPath, SysEntity, SysEntityAttributesExt}; + use super::oc_limits::{CpuLimits, CpusLimits, OverclockLimits}; use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT; use crate::api::RangeLimit; @@ -11,6 +13,10 @@ use crate::settings::{TCpu, TCpus}; const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present"; const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control"; +const CARD_NEEDS: &[&'static str] = &[ + super::DPM_FORCE_LIMITS_ATTRIBUTE +]; + #[derive(Debug, Clone)] pub struct Cpus { pub cpus: Vec, @@ -230,9 +236,11 @@ pub struct Cpu { limits: CpuLimits, index: usize, state: crate::state::steam_deck::Cpu, + sysfs: BasicEntityPath, } -const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage"; +//const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage"; +const CPU_CLOCK_LIMITS_ATTRIBUTE: &str = "device/pp_od_clk_voltage"; enum ClockType { Min = 0, @@ -250,6 +258,7 @@ impl Cpu { limits: oc_limits, index: i, state: crate::state::steam_deck::Cpu::default(), + sysfs: Self::find_card_sysfs(other.root), }, _ => Self { online: other.online, @@ -258,17 +267,35 @@ impl Cpu { limits: oc_limits, index: i, state: crate::state::steam_deck::Cpu::default(), + sysfs: Self::find_card_sysfs(other.root), }, } } - fn set_clock_limit(index: usize, speed: u64, mode: ClockType) -> Result<(), SettingError> { + fn find_card_sysfs(root: Option>) -> BasicEntityPath { + let root = crate::settings::util::root_or_default_sysfs(root); + match root.class("drm", sysfuss::capability::attributes(CARD_NEEDS.into_iter().map(|s| s.to_string()))) { + Ok(mut iter) => { + iter.next() + .unwrap_or_else(|| { + log::error!("Failed to find SteamDeck drm in sysfs (no results), trying naive fallback"); + BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0")) + }) + }, + Err(e) => { + log::error!("Failed to find SteamDeck drm in sysfs ({}), using naive fallback", e); + BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0")) + } + } + } + + fn set_clock_limit(&self, index: usize, speed: u64, mode: ClockType) -> Result<(), SettingError> { let payload = format!("p {} {} {}\n", index / 2, mode as u8, speed); - usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload).map_err(|e| { + self.sysfs.set(CPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), &payload).map_err(|e| { SettingError { msg: format!( "Failed to write `{}` to `{}`: {}", - &payload, CPU_CLOCK_LIMITS_PATH, e + &payload, CPU_CLOCK_LIMITS_ATTRIBUTE, e ), setting: crate::settings::SettingVariant::Cpu, } @@ -279,7 +306,7 @@ impl Cpu { let mut errors = Vec::new(); if let Some(clock_limits) = &self.clock_limits { POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(true, self.index); - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs)?; log::debug!( "Setting CPU {} (min, max) clockspeed to ({:?}, {:?})", self.index, @@ -289,7 +316,7 @@ impl Cpu { self.state.clock_limits_set = true; // max clock if let Some(max) = clock_limits.max { - Self::set_clock_limit(self.index, max, ClockType::Max) + self.set_clock_limit(self.index, max, ClockType::Max) .unwrap_or_else(|e| errors.push(e)); } // min clock @@ -299,7 +326,7 @@ impl Cpu { } else { min }; - Self::set_clock_limit(self.index, valid_min, ClockType::Min) + self.set_clock_limit(self.index, valid_min, ClockType::Min) .unwrap_or_else(|e| errors.push(e)); } @@ -316,17 +343,17 @@ impl Cpu { self.state.clock_limits_set = false; POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(false, self.index); POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT - .enforce_level()?; + .enforce_level(&self.sysfs)?; if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { // always set clock speeds, since it doesn't reset correctly (kernel/hardware bug) - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs)?; // disable manual clock limits log::debug!("Setting CPU {} to default clockspeed", self.index); // max clock - Self::set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max) + self.set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max) .unwrap_or_else(|e| errors.push(e)); // min clock - Self::set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min) + self.set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min) .unwrap_or_else(|e| errors.push(e)); } // TODO remove this when it's no longer needed @@ -346,17 +373,17 @@ impl Cpu { let mut errors = Vec::new(); POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(true, self.index); // always set clock speeds, since it doesn't reset correctly (kernel/hardware bug) - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs)?; // disable manual clock limits log::debug!("Setting CPU {} to default clockspeed", self.index); // max clock - Self::set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max) + self.set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max) .unwrap_or_else(|e| errors.push(e)); // min clock - Self::set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min) + self.set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min) .unwrap_or_else(|e| errors.push(e)); - Self::set_confirm().unwrap_or_else(|e| errors.push(e)); + self.set_confirm().unwrap_or_else(|e| errors.push(e)); POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(false, self.index); if errors.is_empty() { Ok(()) @@ -365,10 +392,10 @@ impl Cpu { } } - fn set_confirm() -> Result<(), SettingError> { - usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| { + fn set_confirm(&self) -> Result<(), SettingError> { + self.sysfs.set(CPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), "c\n").map_err(|e| { SettingError { - msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e), + msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_ATTRIBUTE, e), setting: crate::settings::SettingVariant::Cpu, } }) @@ -385,7 +412,7 @@ impl Cpu { // commit changes (if no errors have already occured) if errors.is_empty() { if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { - Self::set_confirm().map_err(|e| vec![e]) + self.set_confirm().map_err(|e| vec![e]) } else { Ok(()) } @@ -470,6 +497,7 @@ impl Cpu { limits: oc_limits, index: cpu_index, state: crate::state::steam_deck::Cpu::default(), + sysfs: Self::find_card_sysfs(None::<&'static str>) } } @@ -509,6 +537,7 @@ impl Into for Cpu { online: self.online, clock_limits: self.clock_limits.map(|x| x.into()), governor: self.governor, + root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|r| r.to_owned())) } } } diff --git a/backend/src/settings/steam_deck/gpu.rs b/backend/src/settings/steam_deck/gpu.rs index c44f3af..5c36faf 100644 --- a/backend/src/settings/steam_deck/gpu.rs +++ b/backend/src/settings/steam_deck/gpu.rs @@ -1,5 +1,7 @@ use std::convert::Into; +use sysfuss::{BasicEntityPath, HwMonPath, SysEntity, capability::attributes, SysEntityAttributesExt, SysAttribute}; + use super::oc_limits::{GpuLimits, OverclockLimits}; use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT; use crate::api::RangeLimit; @@ -8,8 +10,9 @@ use crate::settings::TGpu; use crate::settings::{min_max_from_json, MinMax}; use crate::settings::{OnResume, OnSet, SettingError}; -const SLOW_PPT: u8 = 1; -const FAST_PPT: u8 = 2; +// usually in /sys/class/hwmon/hwmon4/ +const SLOW_PPT_ATTRIBUTE: sysfuss::HwMonAttribute = sysfuss::HwMonAttribute::custom("power1_cap"); +const FAST_PPT_ATTRIBUTE: sysfuss::HwMonAttribute = sysfuss::HwMonAttribute::custom("power2_cap"); #[derive(Debug, Clone)] pub struct Gpu { @@ -20,11 +23,22 @@ pub struct Gpu { limits: GpuLimits, state: crate::state::steam_deck::Gpu, driver_mode: crate::persist::DriverJson, + sysfs_card: BasicEntityPath, + sysfs_hwmon: HwMonPath } // same as CPU -const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage"; -const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk"; +//const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage"; +//const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk"; + +const GPU_CLOCK_LIMITS_ATTRIBUTE: &str = "device/pp_od_clk_voltage"; +const GPU_MEMORY_DOWNCLOCK_ATTRIBUTE: &str = "device/pp_dpm_fclk"; + +const CARD_NEEDS: &[&'static str] = &[ + GPU_CLOCK_LIMITS_ATTRIBUTE, + GPU_MEMORY_DOWNCLOCK_ATTRIBUTE, + super::DPM_FORCE_LIMITS_ATTRIBUTE, +]; enum ClockType { Min = 0, @@ -49,6 +63,8 @@ impl Gpu { limits: oc_limits.gpu, state: crate::state::steam_deck::Gpu::default(), driver_mode: driver, + sysfs_card: Self::find_card_sysfs(other.root.clone()), + sysfs_hwmon: Self::find_hwmon_sysfs(other.root), }, _ => Self { fast_ppt: other.fast_ppt, @@ -58,27 +74,53 @@ impl Gpu { limits: oc_limits.gpu, state: crate::state::steam_deck::Gpu::default(), driver_mode: driver, + sysfs_card: Self::find_card_sysfs(other.root.clone()), + sysfs_hwmon: Self::find_hwmon_sysfs(other.root), }, } } - fn set_clock_limit(speed: u64, mode: ClockType) -> Result<(), SettingError> { + fn find_card_sysfs(root: Option>) -> BasicEntityPath { + let root = crate::settings::util::root_or_default_sysfs(root); + match root.class("drm", attributes(CARD_NEEDS.into_iter().map(|s| s.to_string()))) { + Ok(mut iter) => { + iter.next() + .unwrap_or_else(|| { + log::error!("Failed to find SteamDeck gpu drm in sysfs (no results), trying naive fallback"); + BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0")) + }) + }, + Err(e) => { + log::error!("Failed to find SteamDeck gpu drm in sysfs ({}), using naive fallback", e); + BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0")) + } + } + } + + fn find_hwmon_sysfs(root: Option>) -> HwMonPath { + let root = crate::settings::util::root_or_default_sysfs(root); + root.hwmon_by_name(super::util::GPU_HWMON_NAME).unwrap_or_else(|e| { + log::error!("Failed to find SteamDeck gpu hwmon in sysfs ({}), using naive fallback", e); + root.hwmon_by_index(4) + }) + } + + fn set_clock_limit(&self, speed: u64, mode: ClockType) -> Result<(), SettingError> { let payload = format!("s {} {}\n", mode as u8, speed); - usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload).map_err(|e| { + let path = GPU_CLOCK_LIMITS_ATTRIBUTE.path(&self.sysfs_card); + self.sysfs_card.set(GPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), &payload).map_err(|e| { SettingError { - msg: format!( - "Failed to write `{}` to `{}`: {}", - &payload, GPU_CLOCK_LIMITS_PATH, e - ), + msg: format!("Failed to write `{}` to `{}`: {}", &payload, path.display(), e), setting: crate::settings::SettingVariant::Gpu, } }) } - fn set_confirm() -> Result<(), SettingError> { - usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| { + fn set_confirm(&self) -> Result<(), SettingError> { + let path = GPU_CLOCK_LIMITS_ATTRIBUTE.path(&self.sysfs_card); + self.sysfs_card.set(GPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), "c\n").map_err(|e| { SettingError { - msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e), + msg: format!("Failed to write `c` to `{}`: {}", path.display(), e), setting: crate::settings::SettingVariant::Gpu, } }) @@ -88,19 +130,19 @@ impl Gpu { let mut errors = Vec::new(); if let Some(clock_limits) = &self.clock_limits { POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true); - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?; // set clock limits self.state.clock_limits_set = true; // max clock if let Some(max) = clock_limits.max { - Self::set_clock_limit(max, ClockType::Max).unwrap_or_else(|e| errors.push(e)); + self.set_clock_limit(max, ClockType::Max).unwrap_or_else(|e| errors.push(e)); } // min clock if let Some(min) = clock_limits.min { - Self::set_clock_limit(min, ClockType::Min).unwrap_or_else(|e| errors.push(e)); + self.set_clock_limit(min, ClockType::Min).unwrap_or_else(|e| errors.push(e)); } - Self::set_confirm().unwrap_or_else(|e| errors.push(e)); + self.set_confirm().unwrap_or_else(|e| errors.push(e)); } else if self.state.clock_limits_set || (self.state.is_resuming && !self.limits.skip_resume_reclock) || POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() @@ -108,19 +150,19 @@ impl Gpu { self.state.clock_limits_set = false; POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.slow_memory); if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?; // disable manual clock limits // max clock - Self::set_clock_limit(self.limits.clock_max.max, ClockType::Max) + self.set_clock_limit(self.limits.clock_max.max, ClockType::Max) .unwrap_or_else(|e| errors.push(e)); // min clock - Self::set_clock_limit(self.limits.clock_min.min, ClockType::Min) + self.set_clock_limit(self.limits.clock_min.min, ClockType::Min) .unwrap_or_else(|e| errors.push(e)); - Self::set_confirm().unwrap_or_else(|e| errors.push(e)); + self.set_confirm().unwrap_or_else(|e| errors.push(e)); } else { POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT - .enforce_level() + .enforce_level(&self.sysfs_card) .unwrap_or_else(|mut e| errors.append(&mut e)); } } @@ -131,10 +173,11 @@ impl Gpu { } } - fn set_slow_memory(slow: bool) -> Result<(), SettingError> { - usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, slow as u8).map_err(|e| { + fn set_slow_memory(&self, slow: bool) -> Result<(), SettingError> { + let path = GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.path(&self.sysfs_card); + self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), slow as u8).map_err(|e| { SettingError { - msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e), + msg: format!("Failed to write to `{}`: {}", path.display(), e), setting: crate::settings::SettingVariant::Gpu, } }) @@ -146,14 +189,14 @@ impl Gpu { if self.slow_memory { POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true); POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT - .enforce_level() + .enforce_level(&self.sysfs_card) .unwrap_or_else(|mut e| errors.append(&mut e)); - Self::set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e)); + self.set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e)); } else if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { - Self::set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e)); + self.set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e)); POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.clock_limits.is_some()); POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT - .enforce_level() + .enforce_level(&self.sysfs_card) .unwrap_or_else(|mut e| errors.append(&mut e)); } self.set_clocks() @@ -161,7 +204,7 @@ impl Gpu { // commit changes (if no errors have already occured) if errors.is_empty() { if self.slow_memory || self.clock_limits.is_some() { - Self::set_confirm().map_err(|e| { + self.set_confirm().map_err(|e| { errors.push(e); errors }) @@ -178,12 +221,11 @@ impl Gpu { // set fast PPT if let Some(fast_ppt) = &self.fast_ppt { self.state.fast_ppt_set = true; - let fast_ppt_path = gpu_power_path(FAST_PPT); - usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt) + self.sysfs_hwmon.set(FAST_PPT_ATTRIBUTE, fast_ppt) .map_err(|e| SettingError { msg: format!( - "Failed to write `{}` to `{}`: {}", - fast_ppt, &fast_ppt_path, e + "Failed to write `{}` to `{:?}`: {}", + fast_ppt, FAST_PPT_ATTRIBUTE, e ), setting: crate::settings::SettingVariant::Gpu, }) @@ -193,12 +235,11 @@ impl Gpu { } else if self.state.fast_ppt_set { self.state.fast_ppt_set = false; let fast_ppt = self.limits.fast_ppt_default; - let fast_ppt_path = gpu_power_path(FAST_PPT); - usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt) + self.sysfs_hwmon.set(FAST_PPT_ATTRIBUTE, fast_ppt) .map_err(|e| SettingError { msg: format!( - "Failed to write `{}` to `{}`: {}", - fast_ppt, &fast_ppt_path, e + "Failed to write `{}` to `{:?}`: {}", + fast_ppt, FAST_PPT_ATTRIBUTE, e ), setting: crate::settings::SettingVariant::Gpu, }) @@ -209,12 +250,11 @@ impl Gpu { // set slow PPT if let Some(slow_ppt) = &self.slow_ppt { self.state.slow_ppt_set = true; - let slow_ppt_path = gpu_power_path(SLOW_PPT); - usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt) + self.sysfs_hwmon.set(SLOW_PPT_ATTRIBUTE, slow_ppt) .map_err(|e| SettingError { msg: format!( - "Failed to write `{}` to `{}`: {}", - slow_ppt, &slow_ppt_path, e + "Failed to write `{}` to `{:?}`: {}", + slow_ppt, SLOW_PPT_ATTRIBUTE, e ), setting: crate::settings::SettingVariant::Gpu, }) @@ -224,12 +264,11 @@ impl Gpu { } else if self.state.slow_ppt_set { self.state.slow_ppt_set = false; let slow_ppt = self.limits.slow_ppt_default; - let slow_ppt_path = gpu_power_path(SLOW_PPT); - usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt) + self.sysfs_hwmon.set(SLOW_PPT_ATTRIBUTE, slow_ppt) .map_err(|e| SettingError { msg: format!( - "Failed to write `{}` to `{}`: {}", - slow_ppt, &slow_ppt_path, e + "Failed to write `{}` to `{:?}`: {}", + slow_ppt, SLOW_PPT_ATTRIBUTE, e ), setting: crate::settings::SettingVariant::Gpu, }) @@ -279,6 +318,8 @@ impl Gpu { } else { crate::persist::DriverJson::SteamDeckAdvance }, + sysfs_card: Self::find_card_sysfs(None::<&'static str>), + sysfs_hwmon: Self::find_hwmon_sysfs(None::<&'static str>), } } } @@ -291,6 +332,7 @@ impl Into for Gpu { slow_ppt: self.slow_ppt, clock_limits: self.clock_limits.map(|x| x.into()), slow_memory: self.slow_memory, + root: self.sysfs_card.root().or(self.sysfs_hwmon.root()).and_then(|p| p.as_ref().to_str().map(|r| r.to_owned())) } } } @@ -372,8 +414,3 @@ impl TGpu for Gpu { self.driver_mode.clone() } } - -#[inline] -fn gpu_power_path(power_number: u8) -> String { - format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number) -} diff --git a/backend/src/settings/steam_deck/mod.rs b/backend/src/settings/steam_deck/mod.rs index 49481f7..268fa90 100644 --- a/backend/src/settings/steam_deck/mod.rs +++ b/backend/src/settings/steam_deck/mod.rs @@ -8,6 +8,6 @@ mod util; pub use battery::Battery; pub use cpu::{Cpu, Cpus}; pub use gpu::Gpu; -pub(self) use power_dpm_force::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT; +pub(self) use power_dpm_force::{POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT, DPM_FORCE_LIMITS_ATTRIBUTE}; pub use util::flash_led; diff --git a/backend/src/settings/steam_deck/power_dpm_force.rs b/backend/src/settings/steam_deck/power_dpm_force.rs index 06c820a..72404cc 100644 --- a/backend/src/settings/steam_deck/power_dpm_force.rs +++ b/backend/src/settings/steam_deck/power_dpm_force.rs @@ -5,6 +5,8 @@ use std::sync::atomic::{AtomicU64, Ordering}; +use sysfuss::{BasicEntityPath, SysEntityAttributesExt, SysAttribute}; + use crate::settings::SettingError; const DEFAULT_BITS: u64 = 0; @@ -19,7 +21,8 @@ pub struct PDFPLManager(AtomicU64); const GPU_BIT: usize = 1; const CPU_BITS_START: usize = 2; -const DPM_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level"; +//const DPM_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level"; +pub const DPM_FORCE_LIMITS_ATTRIBUTE: &str = "device/power_dpm_force_performance_level"; impl PDFPLManager { #[inline] @@ -56,49 +59,44 @@ impl PDFPLManager { self.set(DEFAULT_BITS); } - pub fn enforce_level(&self) -> Result<(), Vec> { + pub fn enforce_level(&self, entity: &BasicEntityPath) -> Result<(), Vec> { let needs = self.needs_manual(); let mut errors = Vec::new(); - let mode: String = usdpl_back::api::files::read_single(DPM_FORCE_LIMITS_PATH.to_owned()) + let path = DPM_FORCE_LIMITS_ATTRIBUTE.path(entity); + let mode: String = entity.attribute(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned()) .map_err(|e| { vec![SettingError { - msg: format!("Failed to read `{}`: {}", DPM_FORCE_LIMITS_PATH, e), + msg: format!("Failed to read `{}`: {}", path.display(), e), setting: crate::settings::SettingVariant::General, }] })?; if mode != "manual" && needs { - log::info!("Setting `{}` to manual", DPM_FORCE_LIMITS_PATH); + log::info!("Setting `{}` to manual", path.display()); // set manual control - usdpl_back::api::files::write_single(DPM_FORCE_LIMITS_PATH, "manual") + entity.set(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned(), "manual") .map_err(|e| { errors.push(SettingError { - msg: format!( - "Failed to write `manual` to `{}`: {}", - DPM_FORCE_LIMITS_PATH, e - ), + msg: format!("Failed to write `manual` to `{}`: {}", path.display(), e), setting: crate::settings::SettingVariant::General, }) }) .unwrap_or(()); } else if mode != "auto" && !needs { - log::info!("Setting `{}` to auto", DPM_FORCE_LIMITS_PATH); + log::info!("Setting `{}` to auto", path.display()); // unset manual control - usdpl_back::api::files::write_single(DPM_FORCE_LIMITS_PATH, "auto") + entity.set(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned(), "auto") .map_err(|e| { errors.push(SettingError { - msg: format!( - "Failed to write `auto` to `{}`: {}", - DPM_FORCE_LIMITS_PATH, e - ), + msg: format!("Failed to write `auto` to `{}`: {}", path.display(), e), setting: crate::settings::SettingVariant::General, }) }) .unwrap_or(()); } if let Ok(mode_now) = - usdpl_back::api::files::read_single::<_, String, _>(DPM_FORCE_LIMITS_PATH.to_owned()) + entity.attribute::(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned()) { - log::debug!("Mode for `{}` is now `{}`", DPM_FORCE_LIMITS_PATH, mode_now); + log::debug!("Mode for `{}` is now `{}`", path.display(), mode_now); } else { log::debug!("Error getting new mode for debugging purposes"); } diff --git a/backend/src/settings/steam_deck/util.rs b/backend/src/settings/steam_deck/util.rs index 525d414..84dfd9c 100644 --- a/backend/src/settings/steam_deck/util.rs +++ b/backend/src/settings/steam_deck/util.rs @@ -8,6 +8,9 @@ use std::fs::OpenOptions; use std::io::{Error, Read, Seek, SeekFrom, Write}; +pub const JUPITER_HWMON_NAME: &'static str = "jupiter"; +pub const GPU_HWMON_NAME: &'static str = "amdgpu"; + #[inline] fn write2(p0: u8, p1: u8) -> Result { write_to(0x6c, 0x81)?; diff --git a/backend/src/settings/unknown/battery.rs b/backend/src/settings/unknown/battery.rs index f0c67a3..ab76959 100644 --- a/backend/src/settings/unknown/battery.rs +++ b/backend/src/settings/unknown/battery.rs @@ -14,6 +14,7 @@ impl Into for Battery { charge_rate: None, charge_mode: None, events: Vec::default(), + root: None, } } } diff --git a/backend/src/settings/unknown/cpu.rs b/backend/src/settings/unknown/cpu.rs index bc5f1c4..c3985cf 100644 --- a/backend/src/settings/unknown/cpu.rs +++ b/backend/src/settings/unknown/cpu.rs @@ -180,6 +180,7 @@ pub struct Cpu { pub governor: String, index: usize, state: crate::state::steam_deck::Cpu, + root: std::path::PathBuf, } impl Cpu { @@ -191,12 +192,14 @@ impl Cpu { governor: other.governor, index: i, state: crate::state::steam_deck::Cpu::default(), + root: other.root.unwrap_or_else(|| "/".to_owned()).into(), }, _ => Self { online: other.online, governor: other.governor, index: i, state: crate::state::steam_deck::Cpu::default(), + root: other.root.unwrap_or_else(|| "/".to_owned()).into(), }, } } @@ -243,6 +246,7 @@ impl Cpu { .unwrap_or("schedutil".to_owned()), index: cpu_index, state: crate::state::steam_deck::Cpu::default(), + root: "/".into() } } @@ -263,6 +267,7 @@ impl Into for Cpu { online: self.online, clock_limits: None, governor: self.governor, + root: self.root.to_str().map(|s| s.to_owned()), } } } diff --git a/backend/src/settings/unknown/gpu.rs b/backend/src/settings/unknown/gpu.rs index 150fad7..0b4b73e 100644 --- a/backend/src/settings/unknown/gpu.rs +++ b/backend/src/settings/unknown/gpu.rs @@ -29,6 +29,7 @@ impl Into for Gpu { slow_ppt: None, clock_limits: None, slow_memory: false, + root: None, } } } diff --git a/backend/src/settings/util.rs b/backend/src/settings/util.rs index 725de0a..ec9189e 100644 --- a/backend/src/settings/util.rs +++ b/backend/src/settings/util.rs @@ -6,6 +6,18 @@ pub fn guess_smt(cpus: &Vec) -> bool { guess } +pub fn root_or_default_sysfs(root: Option>) -> sysfuss::SysPath { + if let Some(root) = root { + sysfuss::SysPath::path(root) + } else { + sysfuss::SysPath::default() + } +} + +pub fn always_satisfied<'a, X>(_: &'a X) -> bool { + true +} + #[cfg(test)] mod test { use super::*; diff --git a/backend/src/utility.rs b/backend/src/utility.rs index 75430f1..742cd32 100644 --- a/backend/src/utility.rs +++ b/backend/src/utility.rs @@ -76,6 +76,9 @@ fn version_filepath() -> std::path::PathBuf { pub fn save_version_file() -> std::io::Result { let path = version_filepath(); + if let Some(parent_dir) = path.parent() { + std::fs::create_dir_all(parent_dir)?; + } std::fs::File::create(path)?.write(crate::consts::PACKAGE_VERSION.as_bytes()) } diff --git a/src/components/message.tsx b/src/components/message.tsx index f343a41..c7954ee 100644 --- a/src/components/message.tsx +++ b/src/components/message.tsx @@ -6,6 +6,7 @@ import { staticClasses, PanelSectionRow, ButtonItem, + Navigation, } from "decky-frontend-lib"; import { MESSAGE_LIST } from "../consts"; @@ -30,7 +31,8 @@ export class DevMessages extends Component { {message.title} - + { if (message.url) { Navigation.NavigateToExternalWeb(message.url); } }}> {message.body}