From dd9b000e4b6b5e4dc09860191bd990f671aef084 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 Jun 2025 18:44:30 -0700 Subject: [PATCH] hardware: Move DeviceConfig and allow for auto-matching based on file contents --- data/devices/jupiter.toml | 12 + data/devices/legion-go-series.toml | 30 ++ data/devices/rog-ally-series.toml | 12 + data/devices/zotac-gaming-zone.toml | 14 +- src/hardware.rs | 422 ++++++++++++++++++---------- src/manager/root.rs | 5 +- src/manager/user.rs | 26 +- src/platform.rs | 104 +------ src/power.rs | 10 +- src/testing.rs | 3 +- 10 files changed, 375 insertions(+), 263 deletions(-) diff --git a/data/devices/jupiter.toml b/data/devices/jupiter.toml index 6e7d81d..e380bae 100644 --- a/data/devices/jupiter.toml +++ b/data/devices/jupiter.toml @@ -1,3 +1,15 @@ +[[device]] +dmi.sys_vendor = "Valve" +dmi.board_name = "Jupiter" +device = "steam_deck" +variant = "Jupiter" + +[[device]] +dmi.sys_vendor = "Valve" +dmi.board_name = "Galileo" +device = "steam_deck" +variant = "Galileo" + [tdp_limit] method = "gpu_hwmon" download_mode_limit = 6 diff --git a/data/devices/legion-go-series.toml b/data/devices/legion-go-series.toml index 7d5b7ae..c2b57af 100644 --- a/data/devices/legion-go-series.toml +++ b/data/devices/legion-go-series.toml @@ -1,3 +1,33 @@ +[[device]] +dmi.sys_vendor = "LENOVO" +dmi.product_name = "83E1" +device = "legion_go" +variant = "83E1" + +[[device]] +dmi.sys_vendor = "LENOVO" +dmi.product_name = "83L3" +device = "legion_go_s" +variant = "83L3" + +[[device]] +dmi.sys_vendor = "LENOVO" +dmi.product_name = "83N6" +device = "legion_go_s" +variant = "83N6" + +[[device]] +dmi.sys_vendor = "LENOVO" +dmi.product_name = "83Q2" +device = "legion_go_s" +variant = "83Q2" + +[[device]] +dmi.sys_vendor = "LENOVO" +dmi.product_name = "83Q3" +device = "legion_go_s" +variant = "83Q3" + [performance_profile] platform_profile_name = "lenovo-wmi-gamezone" suggested_default = "custom" diff --git a/data/devices/rog-ally-series.toml b/data/devices/rog-ally-series.toml index 07d2c5f..c106ded 100644 --- a/data/devices/rog-ally-series.toml +++ b/data/devices/rog-ally-series.toml @@ -1,3 +1,15 @@ +[[device]] +dmi.sys_vendor = "ASUSTeK COMPUTER INC." +dmi.board_name = "RC71L" +device = "rog_ally" +variant = "RC71L" + +[[device]] +dmi.sys_vendor = "ASUSTeK COMPUTER INC." +dmi.board_name = "RC72LA" +device = "rog_ally_x" +variant = "RC72LA" + [performance_profile] platform_profile_name = "asus-wmi" # until custom mode is added diff --git a/data/devices/zotac-gaming-zone.toml b/data/devices/zotac-gaming-zone.toml index c78ec54..360a559 100644 --- a/data/devices/zotac-gaming-zone.toml +++ b/data/devices/zotac-gaming-zone.toml @@ -1,3 +1,15 @@ +[[device]] +dmi.sys_vendor = "ZOTAC" +dmi.board_name = "G0A1W" +device = "zotac_gaming_zone" +variant = "G0A1W" + +[[device]] +dmi.sys_vendor = "ZOTAC" +dmi.board_name = "G1A1W" +device = "zotac_gaming_zone" +variant = "G1A1W" + [performance_profile] platform_profile_name = "zotac_zone_platform" suggested_default = "custom" @@ -7,4 +19,4 @@ method = "firmware_attribute" [tdp_limit.firmware_attribute] attribute = "zotac_zone_platform" -performance_profile = "custom" \ No newline at end of file +performance_profile = "custom" diff --git a/src/hardware.rs b/src/hardware.rs index e6fb8dd..661984a 100644 --- a/src/hardware.rs +++ b/src/hardware.rs @@ -7,19 +7,33 @@ use anyhow::{bail, ensure, Result}; use num_enum::TryFromPrimitive; +use serde::de::Error; +use serde::{Deserialize, Deserializer}; +use std::num::NonZeroU32; use std::str::FromStr; -use strum::{Display, EnumString}; -use tokio::fs; +use strum::{Display, EnumString, VariantNames}; +use tokio::fs::{read_dir, read_to_string}; +#[cfg(not(test))] +use tokio::sync::OnceCell; +use tracing::error; use zbus::Connection; use crate::path; use crate::platform::{platform_config, ServiceConfig}; +use crate::power::TdpLimitingMethod; use crate::process::{run_script, script_exit_code}; use crate::systemd::SystemdUnit; +#[cfg(not(test))] +static DEVICE_CONFIG: OnceCell> = OnceCell::const_new(); + const SYS_VENDOR_PATH: &str = "/sys/class/dmi/id/sys_vendor"; const BOARD_NAME_PATH: &str = "/sys/class/dmi/id/board_name"; const PRODUCT_NAME_PATH: &str = "/sys/class/dmi/id/product_name"; +#[cfg(not(test))] +const DEVICE_CONFIG_PATH: &str = "/usr/share/steamos-manager/devices"; +#[cfg(test)] +const DEVICE_CONFIG_PATH: &str = "data/devices"; #[derive(Display, EnumString, PartialEq, Debug, Default, Copy, Clone)] #[strum(serialize_all = "snake_case", ascii_case_insensitive)] @@ -30,19 +44,6 @@ pub(crate) enum SteamDeckVariant { Galileo, } -#[derive(Display, EnumString, PartialEq, Debug, Default, Copy, Clone)] -#[strum(serialize_all = "snake_case", ascii_case_insensitive)] -pub(crate) enum DeviceType { - #[default] - Unknown, - SteamDeck, - LegionGo, - LegionGoS, - RogAlly, - RogAllyX, - ZotacGamingZone, -} - #[derive(Display, EnumString, PartialEq, Debug, Copy, Clone, TryFromPrimitive)] #[strum(ascii_case_insensitive)] #[repr(u32)] @@ -62,36 +63,174 @@ pub enum FactoryResetKind { All = 3, } +#[derive(Clone, Default, Deserialize, Debug)] +#[serde(default)] +pub(crate) struct DeviceConfig { + pub device: Vec, + pub tdp_limit: Option, + pub gpu_clocks: Option>, + pub battery_charge_limit: Option, + pub performance_profile: Option, +} + +#[derive(Clone, Deserialize, Debug)] +pub(crate) struct BatteryChargeLimitConfig { + pub suggested_minimum_limit: Option, + pub hwmon_name: String, + pub attribute: String, +} + +#[derive(Clone, Deserialize, Debug)] +pub(crate) struct DeviceMatch { + pub dmi: Option, + pub device: String, + pub variant: String, +} + +#[derive(Clone, Deserialize, Debug)] +pub(crate) struct DmiMatch { + pub sys_vendor: String, + pub board_name: Option, + pub product_name: Option, +} + +#[derive(Clone, Deserialize, Debug)] +pub(crate) struct FirmwareAttributeConfig { + pub attribute: String, + pub performance_profile: Option, +} + +#[derive(Clone, Deserialize, Debug)] +pub(crate) struct PerformanceProfileConfig { + pub suggested_default: String, + pub platform_profile_name: String, +} + +#[derive(Clone, Deserialize, Debug)] +pub(crate) struct RangeConfig { + pub min: T, + pub max: T, +} + +impl Copy for RangeConfig where T: Copy {} + +impl RangeConfig { + #[allow(unused)] + pub(crate) fn new(min: T, max: T) -> RangeConfig { + RangeConfig { min, max } + } +} + +#[derive(Clone, Deserialize, Debug)] +pub(crate) struct TdpLimitConfig { + #[serde(deserialize_with = "de_tdp_limiter_method")] + pub method: TdpLimitingMethod, + pub range: Option>, + pub download_mode_limit: Option, + pub firmware_attribute: Option, +} + +impl DeviceConfig { + pub(crate) async fn device_match(&self) -> Result> { + let sys_vendor = read_to_string(path(SYS_VENDOR_PATH)).await?; + let sys_vendor = sys_vendor.trim_end(); + let board_name = read_to_string(path(BOARD_NAME_PATH)).await?; + let board_name = board_name.trim_end(); + let product_name = read_to_string(path(PRODUCT_NAME_PATH)).await?; + let product_name = product_name.trim_end(); + + for device in self.device.iter() { + if let Some(dmi) = &device.dmi { + if dmi.sys_vendor != sys_vendor { + continue; + } + if Some(board_name) == dmi.board_name.as_deref() { + return Ok(Some(device)); + } + if Some(product_name) == dmi.product_name.as_deref() { + return Ok(Some(device)); + } + } + } + Ok(None) + } + + async fn load() -> Result> { + let mut dir = read_dir(DEVICE_CONFIG_PATH).await?; + while let Some(config) = dir.next_entry().await? { + let path = config.path(); + if let Some(ext) = path.extension() { + if ext != "toml" { + continue; + } + } else { + continue; + } + let config = match read_to_string(&path).await { + Ok(config) => config, + Err(e) => { + error!("Failed to read config file {}: {e}", path.display()); + continue; + } + }; + let config: DeviceConfig = match toml::from_str(config.as_ref()) { + Ok(config) => config, + Err(e) => { + error!("Failed to parse config file {}: {e}", path.display()); + continue; + } + }; + if config.device_match().await?.is_some() { + return Ok(Some(config)); + } + } + Ok(None) + } +} + +fn de_tdp_limiter_method<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, + D::Error: Error, +{ + let string = String::deserialize(deserializer)?; + TdpLimitingMethod::try_from(string.as_str()) + .map_err(|_| D::Error::unknown_variant(string.as_str(), TdpLimitingMethod::VARIANTS)) +} + +#[cfg(not(test))] +pub(crate) async fn device_config() -> Result<&'static Option> { + DEVICE_CONFIG.get_or_try_init(DeviceConfig::load).await +} + +#[cfg(test)] +pub(crate) async fn device_config() -> Result> { + let test = crate::testing::current(); + let config = test.device_config.borrow().clone(); + Ok(config) +} + pub(crate) async fn steam_deck_variant() -> Result { - let sys_vendor = fs::read_to_string(path(SYS_VENDOR_PATH)).await?; + let sys_vendor = read_to_string(path(SYS_VENDOR_PATH)).await?; if sys_vendor.trim_end() != "Valve" { return Ok(SteamDeckVariant::Unknown); } - let board_name = fs::read_to_string(path(BOARD_NAME_PATH)).await?; + let board_name = read_to_string(path(BOARD_NAME_PATH)).await?; Ok(SteamDeckVariant::from_str(board_name.trim_end()).unwrap_or_default()) } -pub(crate) async fn device_type() -> Result { +pub(crate) async fn device_type() -> Result { Ok(device_variant().await?.0) } -pub(crate) async fn device_variant() -> Result<(DeviceType, String)> { - let sys_vendor = fs::read_to_string(path(SYS_VENDOR_PATH)).await?; - let product_name = fs::read_to_string(path(PRODUCT_NAME_PATH)).await?; - let product_name = product_name.trim_end(); - let board_name = fs::read_to_string(path(BOARD_NAME_PATH)).await?; - let board_name = board_name.trim_end(); - Ok(match (sys_vendor.trim_end(), product_name, board_name) { - ("ASUSTeK COMPUTER INC.", _, "RC71L") => (DeviceType::RogAlly, board_name.to_string()), - ("ASUSTeK COMPUTER INC.", _, "RC72LA") => (DeviceType::RogAllyX, board_name.to_string()), - ("LENOVO", "83E1", _) => (DeviceType::LegionGo, product_name.to_string()), - ("LENOVO", "83L3" | "83N6" | "83Q2" | "83Q3", _) => { - (DeviceType::LegionGoS, product_name.to_string()) - } - ("Valve", _, "Jupiter" | "Galileo") => (DeviceType::SteamDeck, board_name.to_string()), - ("ZOTAC", _, "G0A1W" | "G1A1W") => (DeviceType::ZotacGamingZone, board_name.to_string()), - _ => (DeviceType::Unknown, String::from("unknown")), - }) +pub(crate) async fn device_variant() -> Result<(String, String)> { + let Some(device) = device_config().await? else { + return Ok((String::from("unknown"), String::from("unknown"))); + }; + let Some(device) = device.device_match().await? else { + return Ok((String::from("unknown"), String::from("unknown"))); + }; + Ok((device.device.to_string(), device.variant.to_string())) } pub(crate) struct FanControl { @@ -173,238 +312,237 @@ pub mod test { use zbus::zvariant::{ObjectPath, OwnedObjectPath}; pub(crate) async fn fake_model(model: SteamDeckVariant) -> Result<()> { - create_dir_all(crate::path("/sys/class/dmi/id")).await?; + create_dir_all(path("/sys/class/dmi/id")).await?; match model { - SteamDeckVariant::Unknown => write(crate::path(SYS_VENDOR_PATH), "LENOVO\n").await?, + SteamDeckVariant::Unknown => { + write(path(SYS_VENDOR_PATH), "LENOVO\n").await?; + write(path(BOARD_NAME_PATH), "INVALID\n").await?; + write(path(PRODUCT_NAME_PATH), "INVALID\n").await?; + } SteamDeckVariant::Jupiter => { - write(crate::path(SYS_VENDOR_PATH), "Valve\n").await?; - write(crate::path(BOARD_NAME_PATH), "Jupiter\n").await?; - write(crate::path(PRODUCT_NAME_PATH), "Jupiter\n").await?; + write(path(SYS_VENDOR_PATH), "Valve\n").await?; + write(path(BOARD_NAME_PATH), "Jupiter\n").await?; + write(path(PRODUCT_NAME_PATH), "Jupiter\n").await?; } SteamDeckVariant::Galileo => { - write(crate::path(SYS_VENDOR_PATH), "Valve\n").await?; - write(crate::path(BOARD_NAME_PATH), "Galileo\n").await?; - write(crate::path(PRODUCT_NAME_PATH), "Galileo\n").await?; + write(path(SYS_VENDOR_PATH), "Valve\n").await?; + write(path(BOARD_NAME_PATH), "Galileo\n").await?; + write(path(PRODUCT_NAME_PATH), "Galileo\n").await?; } } + testing::current() + .device_config + .replace(DeviceConfig::load().await?); Ok(()) } + async fn setup_board( + sys_vendor: &str, + board_name: &str, + product_name: &str, + ) -> Result { + let h = testing::start(); + + create_dir_all(path("/sys/class/dmi/id")).await?; + write(path(SYS_VENDOR_PATH), sys_vendor).await?; + write(path(BOARD_NAME_PATH), board_name).await?; + write(path(PRODUCT_NAME_PATH), product_name).await?; + h.test.device_config.replace(DeviceConfig::load().await?); + Ok(h) + } + #[tokio::test] - async fn board_lookup() { - let _h = testing::start(); - - create_dir_all(crate::path("/sys/class/dmi/id")) + async fn board_lookup_invalid() { + let _h = setup_board("ASUSTeK COMPUTER INC.\n", "INVALID\n", "INVALID\n") .await - .expect("create_dir_all"); - assert!(steam_deck_variant().await.is_err()); - assert!(device_variant().await.is_err()); - - write(crate::path(SYS_VENDOR_PATH), "ASUSTeK COMPUTER INC.\n") - .await - .expect("write"); - write(crate::path(BOARD_NAME_PATH), "INVALID\n") - .await - .expect("write"); - write(crate::path(PRODUCT_NAME_PATH), "INVALID\n") - .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::Unknown, String::from("unknown")) + (String::from("unknown"), String::from("unknown")) ); + } - write(crate::path(BOARD_NAME_PATH), "RC71L\n") + #[tokio::test] + async fn board_lookup_rog_ally() { + let _h = setup_board("ASUSTeK COMPUTER INC.\n", "RC71L\n", "INVALID\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::RogAlly, String::from("RC71L")) + (String::from("rog_ally"), String::from("RC71L")) ); + } - write(crate::path(BOARD_NAME_PATH), "RC72LA\n") + #[tokio::test] + async fn board_lookup_rog_ally_x() { + let _h = setup_board("ASUSTeK COMPUTER INC.\n", "RC72LA\n", "INVALID\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::RogAllyX, String::from("RC72LA")) + (String::from("rog_ally_x"), String::from("RC72LA")) ); + } - write(crate::path(SYS_VENDOR_PATH), "LENOVO\n") + #[tokio::test] + async fn board_lookup_legion_go() { + let _h = setup_board("LENOVO\n", "INVALID\n", "83E1\n") .await - .expect("write"); - write(crate::path(BOARD_NAME_PATH), "INVALID\n") - .await - .expect("write"); - write(crate::path(PRODUCT_NAME_PATH), "INVALID\n") - .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::Unknown, String::from("unknown")) + (String::from("legion_go"), String::from("83E1")) ); + } - write(crate::path(PRODUCT_NAME_PATH), "83E1\n") + #[tokio::test] + async fn board_lookup_legion_go_s_83l3() { + let _h = setup_board("LENOVO\n", "INVALID\n", "83L3\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::LegionGo, String::from("83E1")) + (String::from("legion_go_s"), String::from("83L3")) ); + } - write(crate::path(PRODUCT_NAME_PATH), "83L3\n") + #[tokio::test] + async fn board_lookup_legion_go_s_83n6() { + let _h = setup_board("LENOVO\n", "INVALID\n", "83N6\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::LegionGoS, String::from("83L3")) + (String::from("legion_go_s"), String::from("83N6")) ); + } - write(crate::path(PRODUCT_NAME_PATH), "83N6\n") + #[tokio::test] + async fn board_lookup_legion_go_s_83q2() { + let _h = setup_board("LENOVO\n", "INVALID\n", "83Q2\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::LegionGoS, String::from("83N6")) + (String::from("legion_go_s"), String::from("83Q2")) ); + } - write(crate::path(PRODUCT_NAME_PATH), "83Q2\n") + #[tokio::test] + async fn board_lookup_legion_go_s_83q3() { + let _h = setup_board("LENOVO\n", "INVALID\n", "83Q3\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::LegionGoS, String::from("83Q2")) + (String::from("legion_go_s"), String::from("83Q3")) ); + } - write(crate::path(PRODUCT_NAME_PATH), "83Q3\n") + #[tokio::test] + async fn board_lookup_steam_deck_jupiter() { + let _h = setup_board("Valve\n", "Jupiter\n", "Jupiter\n") .await - .expect("write"); - assert_eq!( - steam_deck_variant().await.unwrap(), - SteamDeckVariant::Unknown - ); - assert_eq!( - device_variant().await.unwrap(), - (DeviceType::LegionGoS, String::from("83Q3")) - ); - - write(crate::path(SYS_VENDOR_PATH), "Valve\n") - .await - .expect("write"); - write(crate::path(BOARD_NAME_PATH), "Jupiter\n") - .await - .expect("write"); - write(crate::path(PRODUCT_NAME_PATH), "Jupiter\n") - .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Jupiter ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::SteamDeck, String::from("Jupiter")) + (String::from("steam_deck"), String::from("Jupiter")) ); + } - write(crate::path(BOARD_NAME_PATH), "Galileo\n") + #[tokio::test] + async fn board_lookup_steam_deck_galileo() { + let _h = setup_board("Valve\n", "Galileo\n", "Galileo\n") .await - .expect("write"); - write(crate::path(PRODUCT_NAME_PATH), "Galileo\n") - .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Galileo ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::SteamDeck, String::from("Galileo")) + (String::from("steam_deck"), String::from("Galileo")) ); + } - write(crate::path(BOARD_NAME_PATH), "Neptune\n") + #[tokio::test] + async fn board_lookup_invalid_valve() { + let _h = setup_board("Valve\n", "Neptune\n", "Neptune\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::Unknown, String::from("unknown")) + (String::from("unknown"), String::from("unknown")) ); + } - write(crate::path(SYS_VENDOR_PATH), "ZOTAC\n") + #[tokio::test] + async fn board_lookup_zotac_gaming_zone_g0a1w() { + let _h = setup_board("ZOTAC\n", "G0A1W\n", "INVALID\n") .await - .expect("write"); - write(crate::path(BOARD_NAME_PATH), "INVALID\n") - .await - .expect("write"); - write(crate::path(PRODUCT_NAME_PATH), "INVALID\n") - .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::Unknown, String::from("unknown")) + (String::from("zotac_gaming_zone"), String::from("G0A1W")) ); + } - write(crate::path(BOARD_NAME_PATH), "G0A1W\n") + #[tokio::test] + async fn board_lookup_zotac_gaming_zone_g1a1w() { + let _h = setup_board("ZOTAC\n", "G1A1W\n", "INVALID\n") .await - .expect("write"); + .unwrap(); assert_eq!( steam_deck_variant().await.unwrap(), SteamDeckVariant::Unknown ); assert_eq!( device_variant().await.unwrap(), - (DeviceType::ZotacGamingZone, String::from("G0A1W")) - ); - - write(crate::path(BOARD_NAME_PATH), "G1A1W\n") - .await - .expect("write"); - assert_eq!( - steam_deck_variant().await.unwrap(), - SteamDeckVariant::Unknown - ); - assert_eq!( - device_variant().await.unwrap(), - (DeviceType::ZotacGamingZone, String::from("G1A1W")) + (String::from("zotac_gaming_zone"), String::from("G1A1W")) ); } diff --git a/src/manager/root.rs b/src/manager/root.rs index 171ec07..88ad1c7 100644 --- a/src/manager/root.rs +++ b/src/manager/root.rs @@ -21,10 +21,11 @@ use crate::daemon::root::{Command, RootCommand}; use crate::daemon::DaemonCommand; use crate::error::{to_zbus_error, to_zbus_fdo_error}; use crate::hardware::{ - steam_deck_variant, FactoryResetKind, FanControl, FanControlState, SteamDeckVariant, + device_config, steam_deck_variant, FactoryResetKind, FanControl, FanControlState, + SteamDeckVariant, }; use crate::job::JobManager; -use crate::platform::{device_config, platform_config}; +use crate::platform::platform_config; use crate::power::{ set_cpu_scaling_governor, set_gpu_clocks, set_gpu_performance_level, set_gpu_power_profile, set_max_charge_level, set_platform_profile, tdp_limit_manager, CPUScalingGovernor, diff --git a/src/manager/user.rs b/src/manager/user.rs index f4d5654..d113336 100644 --- a/src/manager/user.rs +++ b/src/manager/user.rs @@ -21,10 +21,10 @@ use crate::daemon::user::Command; use crate::daemon::DaemonCommand; use crate::error::{to_zbus_error, to_zbus_fdo_error, zbus_to_zbus_fdo}; use crate::hardware::{ - device_type, device_variant, steam_deck_variant, DeviceType, SteamDeckVariant, + device_config, device_type, device_variant, steam_deck_variant, SteamDeckVariant, }; use crate::job::JobManagerCommand; -use crate::platform::{device_config, platform_config}; +use crate::platform::platform_config; use crate::power::{ get_available_cpu_scaling_governors, get_available_gpu_performance_levels, get_available_gpu_power_profiles, get_available_platform_profiles, get_cpu_scaling_governor, @@ -1040,7 +1040,7 @@ pub(crate) async fn create_interfaces( create_device_interfaces(&proxy, object_server, tdp_manager).await?; create_platform_interfaces(&proxy, object_server, &system, &job_manager).await?; - if device_type().await.unwrap_or_default() == DeviceType::SteamDeck { + if device_type().await.unwrap_or_default() == "steam_deck" { object_server.at(MANAGER_PATH, als).await?; } if steam_deck_variant().await.unwrap_or_default() == SteamDeckVariant::Galileo { @@ -1098,11 +1098,12 @@ mod test { use crate::daemon::channel; use crate::daemon::user::UserContext; use crate::hardware::test::fake_model; - use crate::hardware::SteamDeckVariant; + use crate::hardware::{ + BatteryChargeLimitConfig, DeviceConfig, DeviceMatch, DmiMatch, PerformanceProfileConfig, + RangeConfig, SteamDeckVariant, TdpLimitConfig, + }; use crate::platform::{ - BatteryChargeLimitConfig, DeviceConfig, FormatDeviceConfig, PerformanceProfileConfig, - PlatformConfig, RangeConfig, ResetConfig, ScriptConfig, ServiceConfig, StorageConfig, - TdpLimitConfig, + FormatDeviceConfig, PlatformConfig, ResetConfig, ScriptConfig, ServiceConfig, StorageConfig, }; use crate::power::TdpLimitingMethod; use crate::systemd::test::{MockManager, MockUnit}; @@ -1139,6 +1140,15 @@ mod test { fn all_device_config() -> Option { Some(DeviceConfig { + device: vec![DeviceMatch { + dmi: Some(DmiMatch { + sys_vendor: String::from("Valve"), + board_name: Some(String::from("Galileo")), + product_name: None, + }), + device: String::from("steam_deck"), + variant: String::from("Galileo"), + }], tdp_limit: Some(TdpLimitConfig { method: TdpLimitingMethod::GpuHwmon, range: Some(RangeConfig::new(3, 15)), @@ -1182,6 +1192,7 @@ mod test { config.set_test_paths(); } + fake_model(SteamDeckVariant::Galileo).await?; handle.test.platform_config.replace(platform_config); handle.test.device_config.replace(device_config); let connection = handle.new_dbus().await?; @@ -1207,7 +1218,6 @@ mod test { write(&exe_path, "").await?; set_permissions(&exe_path, PermissionsExt::from_mode(0o700)).await?; - fake_model(SteamDeckVariant::Galileo).await?; handle .test .process_cb diff --git a/src/platform.rs b/src/platform.rs index f094d89..cc4f897 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -8,30 +8,22 @@ use anyhow::Result; use nix::errno::Errno; use nix::unistd::{access, AccessFlags}; -use serde::de::Error; -use serde::{Deserialize, Deserializer}; +use serde::Deserialize; use std::io::ErrorKind; -use std::num::NonZeroU32; use std::os::unix::fs::MetadataExt; use std::path::PathBuf; -use strum::VariantNames; use tokio::fs::{metadata, read_to_string}; #[cfg(not(test))] use tokio::sync::OnceCell; use tokio::task::spawn_blocking; use zbus::Connection; -#[cfg(not(test))] -use crate::hardware::{device_type, DeviceType}; #[cfg(test)] use crate::path; -use crate::power::TdpLimitingMethod; use crate::systemd::SystemdUnit; #[cfg(not(test))] static PLATFORM_CONFIG: OnceCell> = OnceCell::const_new(); -#[cfg(not(test))] -static DEVICE_CONFIG: OnceCell> = OnceCell::const_new(); #[derive(Clone, Default, Deserialize, Debug)] #[serde(default)] @@ -43,23 +35,6 @@ pub(crate) struct PlatformConfig { pub fan_control: Option, } -#[derive(Clone, Default, Deserialize, Debug)] -#[serde(default)] -pub(crate) struct DeviceConfig { - pub tdp_limit: Option, - pub gpu_clocks: Option>, - pub battery_charge_limit: Option, - pub performance_profile: Option, -} - -#[derive(Clone, Deserialize, Debug)] -pub(crate) struct RangeConfig { - pub min: T, - pub max: T, -} - -impl Copy for RangeConfig where T: Copy {} - #[derive(Clone, Default, Deserialize, Debug)] pub(crate) struct ScriptConfig { pub script: PathBuf, @@ -151,34 +126,6 @@ impl StorageConfig { } } -#[derive(Clone, Deserialize, Debug)] -pub(crate) struct BatteryChargeLimitConfig { - pub suggested_minimum_limit: Option, - pub hwmon_name: String, - pub attribute: String, -} - -#[derive(Clone, Deserialize, Debug)] -pub(crate) struct FirmwareAttributeConfig { - pub attribute: String, - pub performance_profile: Option, -} - -#[derive(Clone, Deserialize, Debug)] -pub(crate) struct PerformanceProfileConfig { - pub suggested_default: String, - pub platform_profile_name: String, -} - -#[derive(Clone, Deserialize, Debug)] -pub(crate) struct TdpLimitConfig { - #[serde(deserialize_with = "de_tdp_limiter_method")] - pub method: TdpLimitingMethod, - pub range: Option>, - pub download_mode_limit: Option, - pub firmware_attribute: Option, -} - #[derive(Clone, Default, Deserialize, Debug)] pub(crate) struct FormatDeviceConfig { pub script: PathBuf, @@ -223,13 +170,6 @@ impl FormatDeviceConfig { } } -impl RangeConfig { - #[allow(unused)] - pub(crate) fn new(min: T, max: T) -> RangeConfig { - RangeConfig { min, max } - } -} - impl PlatformConfig { #[cfg(not(test))] async fn load() -> Result> { @@ -271,46 +211,11 @@ impl PlatformConfig { } } -impl DeviceConfig { - #[cfg(not(test))] - async fn load() -> Result> { - let platform = match device_type().await? { - DeviceType::SteamDeck => "jupiter", - DeviceType::LegionGo => "legion-go-series", - DeviceType::LegionGoS => "legion-go-series", - DeviceType::RogAlly => "rog-ally-series", - DeviceType::RogAllyX => "rog-ally-series", - DeviceType::ZotacGamingZone => "zotac-gaming-zone", - _ => return Ok(None), - }; - let config = read_to_string(format!( - "/usr/share/steamos-manager/devices/{platform}.toml" - )) - .await?; - Ok(Some(toml::from_str(config.as_ref())?)) - } -} - -fn de_tdp_limiter_method<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, - D::Error: Error, -{ - let string = String::deserialize(deserializer)?; - TdpLimitingMethod::try_from(string.as_str()) - .map_err(|_| D::Error::unknown_variant(string.as_str(), TdpLimitingMethod::VARIANTS)) -} - #[cfg(not(test))] pub(crate) async fn platform_config() -> Result<&'static Option> { PLATFORM_CONFIG.get_or_try_init(PlatformConfig::load).await } -#[cfg(not(test))] -pub(crate) async fn device_config() -> Result<&'static Option> { - DEVICE_CONFIG.get_or_try_init(DeviceConfig::load).await -} - #[cfg(test)] pub(crate) async fn platform_config() -> Result> { let test = crate::testing::current(); @@ -318,13 +223,6 @@ pub(crate) async fn platform_config() -> Result> { Ok(config) } -#[cfg(test)] -pub(crate) async fn device_config() -> Result> { - let test = crate::testing::current(); - let config = test.device_config.borrow().clone(); - Ok(config) -} - #[cfg(test)] mod test { use super::*; diff --git a/src/power.rs b/src/power.rs index 9a09974..64e7c53 100644 --- a/src/power.rs +++ b/src/power.rs @@ -27,10 +27,9 @@ use tokio::task::JoinSet; use tracing::{debug, error, warn}; use zbus::Connection; -use crate::hardware::{device_type, DeviceType}; +use crate::hardware::{device_config, device_type}; use crate::manager::root::RootManagerProxy; use crate::manager::user::{TdpLimit1, MANAGER_PATH}; -use crate::platform::device_config; use crate::Service; use crate::{path, write_synced}; @@ -246,7 +245,7 @@ pub(crate) async fn get_gpu_power_profile() -> Result { pub(crate) async fn get_available_gpu_power_profiles() -> Result> { let contents = read_gpu_sysfs_contents(GPU_POWER_PROFILE_SUFFIX).await?; - let deck = device_type().await.unwrap_or_default() == DeviceType::SteamDeck; + let deck = device_type().await.unwrap_or_default() == "steam_deck"; let mut map = Vec::new(); let lines = contents.lines(); @@ -856,10 +855,9 @@ pub(crate) mod test { use super::*; use crate::error::to_zbus_fdo_error; use crate::hardware::test::fake_model; - use crate::hardware::SteamDeckVariant; - use crate::platform::{ + use crate::hardware::{ BatteryChargeLimitConfig, DeviceConfig, FirmwareAttributeConfig, PerformanceProfileConfig, - RangeConfig, TdpLimitConfig, + RangeConfig, SteamDeckVariant, TdpLimitConfig, }; use crate::{enum_roundtrip, testing}; use anyhow::anyhow; diff --git a/src/testing.rs b/src/testing.rs index edf376b..a65ea55 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -23,7 +23,8 @@ use zbus::zvariant::ObjectPath; use zbus::Address; use zbus_xml::{Method, Node, Property, Signal}; -use crate::platform::{DeviceConfig, PlatformConfig}; +use crate::hardware::DeviceConfig; +use crate::platform::PlatformConfig; thread_local! { static TEST: RefCell>> = const { RefCell::new(None) };