diff --git a/src/hardware.rs b/src/hardware.rs index 56a9730..671a441 100644 --- a/src/hardware.rs +++ b/src/hardware.rs @@ -19,15 +19,24 @@ use crate::systemd::SystemdUnit; 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"; #[derive(PartialEq, Debug, Default, Copy, Clone)] -pub(crate) enum HardwareVariant { +pub(crate) enum SteamDeckVariant { #[default] Unknown, Jupiter, Galileo, } +#[derive(PartialEq, Debug, Default, Copy, Clone)] +pub(crate) enum DeviceType { + #[default] + Unknown, + SteamDeck, + LegionGoS, +} + #[derive(Display, EnumString, PartialEq, Debug, Copy, Clone, TryFromPrimitive)] #[strum(ascii_case_insensitive)] #[repr(u32)] @@ -38,13 +47,13 @@ pub enum FanControlState { Os = 1, } -impl FromStr for HardwareVariant { +impl FromStr for SteamDeckVariant { type Err = Error; - fn from_str(input: &str) -> Result { + fn from_str(input: &str) -> Result { Ok(match input { - "Jupiter" => HardwareVariant::Jupiter, - "Galileo" => HardwareVariant::Galileo, - _ => HardwareVariant::Unknown, + "Jupiter" => SteamDeckVariant::Jupiter, + "Galileo" => SteamDeckVariant::Galileo, + _ => SteamDeckVariant::Unknown, }) } } @@ -58,21 +67,27 @@ pub enum FactoryResetKind { All = 3, } -pub(crate) async fn variant() -> Result { +pub(crate) async fn steam_deck_variant() -> Result { let sys_vendor = fs::read_to_string(path(SYS_VENDOR_PATH)).await?; if sys_vendor.trim_end() != "Valve" { - return Ok(HardwareVariant::Unknown); + return Ok(SteamDeckVariant::Unknown); } - let board_name = fs::read_to_string(path(BOARD_NAME_PATH)).await?; - HardwareVariant::from_str(board_name.trim_end()) + SteamDeckVariant::from_str(board_name.trim_end()) } -pub(crate) async fn is_deck() -> Result { - match variant().await { - Ok(variant) => Ok(variant != HardwareVariant::Unknown), - Err(e) => Err(e), +pub(crate) async fn device_type() -> Result { + if variant().await? != SteamDeckVariant::Unknown { + return Ok(DeviceType::SteamDeck); } + let board_vendor = fs::read_to_string(path(SYS_VENDOR_PATH)).await?; + if board_vendor.trim_end() == "LENOVO" { + let product_name = fs::read_to_string(path(PRODUCT_NAME_PATH)).await?; + if ["83L3", "83N6", "83Q2", "83Q3"].contains(&product_name.trim_end()) { + return Ok(DeviceType::LegionGoS); + } + } + Ok(DeviceType::Unknown) } pub(crate) struct FanControl { @@ -153,15 +168,15 @@ pub mod test { use zbus::fdo; use zbus::zvariant::{ObjectPath, OwnedObjectPath}; - pub(crate) async fn fake_model(model: HardwareVariant) -> Result<()> { + pub(crate) async fn fake_model(model: SteamDeckVariant) -> Result<()> { create_dir_all(crate::path("/sys/class/dmi/id")).await?; match model { - HardwareVariant::Unknown => write(crate::path(SYS_VENDOR_PATH), "LENOVO\n").await?, - HardwareVariant::Jupiter => { + SteamDeckVariant::Unknown => write(crate::path(SYS_VENDOR_PATH), "LENOVO\n").await?, + SteamDeckVariant::Jupiter => { write(crate::path(SYS_VENDOR_PATH), "Valve\n").await?; write(crate::path(BOARD_NAME_PATH), "Jupiter\n").await?; } - HardwareVariant::Galileo => { + SteamDeckVariant::Galileo => { write(crate::path(SYS_VENDOR_PATH), "Valve\n").await?; write(crate::path(BOARD_NAME_PATH), "Galileo\n").await?; } @@ -176,12 +191,58 @@ pub mod test { create_dir_all(crate::path("/sys/class/dmi/id")) .await .expect("create_dir_all"); - assert!(variant().await.is_err()); + assert!(steam_deck_variant().await.is_err()); write(crate::path(SYS_VENDOR_PATH), "LENOVO\n") .await .expect("write"); - assert_eq!(variant().await.unwrap(), HardwareVariant::Unknown); + write(crate::path(BOARD_NAME_PATH), "INVALID\n") + .await + .expect("write"); + write(crate::path(PRODUCT_NAME_PATH), "INVALID\n") + .await + .expect("write"); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Unknown + ); + assert_eq!(device_type().await.unwrap(), DeviceType::Unknown); + + write(crate::path(PRODUCT_NAME_PATH), "83L3\n") + .await + .expect("write"); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Unknown + ); + assert_eq!(device_type().await.unwrap(), DeviceType::LegionGoS); + + write(crate::path(PRODUCT_NAME_PATH), "83N6\n") + .await + .expect("write"); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Unknown + ); + assert_eq!(device_type().await.unwrap(), DeviceType::LegionGoS); + + write(crate::path(PRODUCT_NAME_PATH), "83Q2\n") + .await + .expect("write"); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Unknown + ); + assert_eq!(device_type().await.unwrap(), DeviceType::LegionGoS); + + write(crate::path(PRODUCT_NAME_PATH), "83Q3\n") + .await + .expect("write"); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Unknown + ); + assert_eq!(device_type().await.unwrap(), DeviceType::LegionGoS); write(crate::path(SYS_VENDOR_PATH), "Valve\n") .await @@ -189,17 +250,35 @@ pub mod test { write(crate::path(BOARD_NAME_PATH), "Jupiter\n") .await .expect("write"); - assert_eq!(variant().await.unwrap(), HardwareVariant::Jupiter); + write(crate::path(PRODUCT_NAME_PATH), "Jupiter\n") + .await + .expect("write"); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Jupiter + ); + assert_eq!(device_type().await.unwrap(), DeviceType::SteamDeck); write(crate::path(BOARD_NAME_PATH), "Galileo\n") .await .expect("write"); - assert_eq!(variant().await.unwrap(), HardwareVariant::Galileo); + write(crate::path(PRODUCT_NAME_PATH), "Galileo\n") + .await + .expect("write"); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Galileo + ); + assert_eq!(device_type().await.unwrap(), DeviceType::SteamDeck); write(crate::path(BOARD_NAME_PATH), "Neptune\n") .await .expect("write"); - assert_eq!(variant().await.unwrap(), HardwareVariant::Unknown); + assert_eq!( + steam_deck_variant().await.unwrap(), + SteamDeckVariant::Unknown + ); + assert_eq!(device_type().await.unwrap(), DeviceType::Unknown); } #[test] diff --git a/src/manager/root.rs b/src/manager/root.rs index 1a7dcf1..e0dd091 100644 --- a/src/manager/root.rs +++ b/src/manager/root.rs @@ -20,7 +20,9 @@ use zbus::{fdo, interface, Connection}; use crate::daemon::root::{Command, RootCommand}; use crate::daemon::DaemonCommand; use crate::error::{to_zbus_error, to_zbus_fdo_error}; -use crate::hardware::{variant, FactoryResetKind, FanControl, FanControlState, HardwareVariant}; +use crate::hardware::{ + steam_deck_variant, FactoryResetKind, FanControl, FanControlState, SteamDeckVariant, +}; use crate::job::JobManager; use crate::platform::platform_config; use crate::power::{ @@ -78,7 +80,7 @@ impl SteamOSManager { Ok(SteamOSManager { fan_control: FanControl::new(connection.clone()), wifi_debug_mode: WifiDebugMode::Off, - should_trace: variant().await? == HardwareVariant::Galileo, + should_trace: steam_deck_variant().await? == SteamDeckVariant::Galileo, job_manager: JobManager::new(connection.clone()).await?, connection, channel, @@ -151,9 +153,9 @@ impl SteamOSManager { async fn als_calibration_gain(&self) -> Vec { // Run script to get calibration value let mut gains = Vec::new(); - let indices: &[&str] = match variant().await { - Ok(HardwareVariant::Jupiter) => &["2"], - Ok(HardwareVariant::Galileo) => &["2", "4"], + let indices: &[&str] = match steam_deck_variant().await { + Ok(SteamDeckVariant::Jupiter) => &["2"], + Ok(SteamDeckVariant::Galileo) => &["2", "4"], _ => return Vec::new(), }; for index in indices { @@ -176,10 +178,10 @@ impl SteamOSManager { async fn get_als_integration_time_file_descriptor(&self, index: u32) -> fdo::Result { // Get the file descriptor for the als integration time sysfs path - let i0 = match variant().await.map_err(to_zbus_fdo_error)? { - HardwareVariant::Jupiter => 1, - HardwareVariant::Galileo => index, - HardwareVariant::Unknown => { + let i0 = match steam_deck_variant().await.map_err(to_zbus_fdo_error)? { + SteamDeckVariant::Jupiter => 1, + SteamDeckVariant::Galileo => index, + SteamDeckVariant::Unknown => { return Err(fdo::Error::Failed(String::from("Unknown model"))) } }; @@ -457,7 +459,7 @@ mod test { async fn start() -> Result { let mut handle = testing::start(); - fake_model(HardwareVariant::Jupiter).await?; + fake_model(SteamDeckVariant::Jupiter).await?; create_dir_all(crate::path("/etc/NetworkManager/conf.d")).await?; write( crate::path("/etc/NetworkManager/conf.d/99-valve-wifi-backend.conf"), @@ -608,22 +610,22 @@ mod test { .process_cb .set(|_, _| Ok((0, String::from("0.0\n")))); - fake_model(HardwareVariant::Jupiter) + fake_model(SteamDeckVariant::Jupiter) .await .expect("fake_model"); assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[0.0]); - fake_model(HardwareVariant::Galileo) + fake_model(SteamDeckVariant::Galileo) .await .expect("fake_model"); assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[0.0, 0.0]); - fake_model(HardwareVariant::Unknown) + fake_model(SteamDeckVariant::Unknown) .await .expect("fake_model"); assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[]); - fake_model(HardwareVariant::Jupiter) + fake_model(SteamDeckVariant::Jupiter) .await .expect("fake_model"); diff --git a/src/manager/user.rs b/src/manager/user.rs index 00943b9..87fddb2 100644 --- a/src/manager/user.rs +++ b/src/manager/user.rs @@ -19,7 +19,7 @@ use crate::cec::{HdmiCecControl, HdmiCecState}; 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::{is_deck, variant, HardwareVariant}; +use crate::hardware::{device_type, steam_deck_variant, DeviceType, SteamDeckVariant}; use crate::job::JobManagerCommand; use crate::platform::platform_config; use crate::power::{ @@ -680,10 +680,10 @@ pub(crate) async fn create_interfaces( create_config_interfaces(&proxy, object_server, &job_manager).await?; - if is_deck().await? { + if device_type().await.unwrap_or_default() == DeviceType::SteamDeck { object_server.at(MANAGER_PATH, als).await?; } - if variant().await? == HardwareVariant::Galileo { + if steam_deck_variant().await.unwrap_or_default() == SteamDeckVariant::Galileo { object_server.at(MANAGER_PATH, wifi_debug_dump).await?; } @@ -721,7 +721,7 @@ pub(crate) async fn create_interfaces( object_server.at(MANAGER_PATH, tdp_limit).await?; } - if variant().await.unwrap_or_default() == HardwareVariant::Galileo { + if steam_deck_variant().await.unwrap_or_default() == SteamDeckVariant::Galileo { object_server.at(MANAGER_PATH, wifi_debug).await?; } @@ -740,7 +740,7 @@ mod test { use crate::daemon::channel; use crate::daemon::user::UserContext; use crate::hardware::test::fake_model; - use crate::hardware::HardwareVariant; + use crate::hardware::SteamDeckVariant; use crate::platform::{ BatteryChargeLimitConfig, PlatformConfig, RangeConfig, ResetConfig, ScriptConfig, ServiceConfig, StorageConfig, @@ -813,7 +813,7 @@ mod test { write(&exe_path, "").await?; set_permissions(&exe_path, PermissionsExt::from_mode(0o700)).await?; - fake_model(HardwareVariant::Galileo).await?; + fake_model(SteamDeckVariant::Galileo).await?; handle .test .process_cb diff --git a/src/platform.rs b/src/platform.rs index b03e447..c8a8808 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -18,7 +18,7 @@ use tokio::sync::OnceCell; use tokio::task::spawn_blocking; #[cfg(not(test))] -use crate::hardware::is_deck; +use crate::hardware::{device_type, DeviceType}; #[cfg(not(test))] static CONFIG: OnceCell> = OnceCell::const_new(); @@ -137,12 +137,14 @@ impl RangeConfig { impl PlatformConfig { #[cfg(not(test))] async fn load() -> Result> { - if !is_deck().await? { - // Non-Steam Deck platforms are not yet supported - return Ok(None); - } - - let config = read_to_string("/usr/share/steamos-manager/platforms/jupiter.toml").await?; + let platform = match device_type().await? { + DeviceType::SteamDeck => "jupiter", + _ => return Ok(None), + }; + let config = read_to_string(format!( + "/usr/share/steamos-manager/platforms/{platform}.toml" + )) + .await?; Ok(Some(toml::from_str(config.as_ref())?)) } diff --git a/src/power.rs b/src/power.rs index 4abca49..1ab05c9 100644 --- a/src/power.rs +++ b/src/power.rs @@ -16,7 +16,7 @@ use tokio::fs::{self, try_exists, File}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tracing::{error, warn}; -use crate::hardware::is_deck; +use crate::hardware::{device_type, DeviceType}; use crate::platform::platform_config; use crate::{path, write_synced}; @@ -139,7 +139,7 @@ pub(crate) async fn get_gpu_power_profile() -> Result { // check which profile is current and return if possible let contents = read_gpu_sysfs_contents(GPU_POWER_PROFILE_SUFFIX).await?; - // NOTE: We don't filter based on is_deck here because the sysfs + // NOTE: We don't filter based on deck here because the sysfs // firmware support setting the value to no-op values. let lines = contents.lines(); for line in lines { @@ -162,7 +162,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 = is_deck().await?; + let deck = device_type().await.unwrap_or_default() == DeviceType::SteamDeck; let mut map = Vec::new(); let lines = contents.lines(); @@ -437,7 +437,7 @@ pub(crate) async fn set_max_charge_level(limit: i32) -> Result<()> { pub(crate) mod test { use super::*; use crate::hardware::test::fake_model; - use crate::hardware::HardwareVariant; + use crate::hardware::SteamDeckVariant; use crate::platform::{BatteryChargeLimitConfig, PlatformConfig}; use crate::{enum_roundtrip, testing}; use anyhow::anyhow; @@ -815,7 +815,7 @@ CCLK_RANGE in Core0: write(filename.as_path(), contents).await.expect("write"); - fake_model(HardwareVariant::Unknown) + fake_model(SteamDeckVariant::Unknown) .await .expect("fake_model"); @@ -836,7 +836,7 @@ CCLK_RANGE in Core0: ] ); - fake_model(HardwareVariant::Jupiter) + fake_model(SteamDeckVariant::Jupiter) .await .expect("fake_model"); @@ -872,7 +872,7 @@ CCLK_RANGE in Core0: write(filename.as_path(), contents).await.expect("write"); - fake_model(HardwareVariant::Unknown) + fake_model(SteamDeckVariant::Unknown) .await .expect("fake_model"); @@ -894,7 +894,7 @@ CCLK_RANGE in Core0: ] ); - fake_model(HardwareVariant::Jupiter) + fake_model(SteamDeckVariant::Jupiter) .await .expect("fake_model"); @@ -929,7 +929,7 @@ CCLK_RANGE in Core0: write(filename.as_path(), contents).await.expect("write"); - fake_model(HardwareVariant::Unknown) + fake_model(SteamDeckVariant::Unknown) .await .expect("fake_model"); assert_eq!( @@ -937,7 +937,7 @@ CCLK_RANGE in Core0: GPUPowerProfile::Video ); - fake_model(HardwareVariant::Jupiter) + fake_model(SteamDeckVariant::Jupiter) .await .expect("fake_model"); assert_eq!( @@ -967,12 +967,12 @@ CCLK_RANGE in Core0: write(filename.as_path(), contents).await.expect("write"); - fake_model(HardwareVariant::Unknown) + fake_model(SteamDeckVariant::Unknown) .await .expect("fake_model"); assert!(get_gpu_power_profile().await.is_err()); - fake_model(HardwareVariant::Jupiter) + fake_model(SteamDeckVariant::Jupiter) .await .expect("fake_model"); assert!(get_gpu_power_profile().await.is_err()); @@ -1000,12 +1000,12 @@ CCLK_RANGE in Core0: write(filename.as_path(), contents).await.expect("write"); - fake_model(HardwareVariant::Unknown) + fake_model(SteamDeckVariant::Unknown) .await .expect("fake_model"); assert!(get_gpu_power_profile().await.is_err()); - fake_model(HardwareVariant::Jupiter) + fake_model(SteamDeckVariant::Jupiter) .await .expect("fake_model"); assert!(get_gpu_power_profile().await.is_err());