hardware: Split out device type detection, add initial identification of Legion Go S

This commit is contained in:
Vicki Pfau 2025-02-20 03:29:00 -08:00
parent cd9558fd67
commit 7d8bd602a7
5 changed files with 147 additions and 64 deletions

View file

@ -19,15 +19,24 @@ use crate::systemd::SystemdUnit;
const SYS_VENDOR_PATH: &str = "/sys/class/dmi/id/sys_vendor"; const SYS_VENDOR_PATH: &str = "/sys/class/dmi/id/sys_vendor";
const BOARD_NAME_PATH: &str = "/sys/class/dmi/id/board_name"; 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)] #[derive(PartialEq, Debug, Default, Copy, Clone)]
pub(crate) enum HardwareVariant { pub(crate) enum SteamDeckVariant {
#[default] #[default]
Unknown, Unknown,
Jupiter, Jupiter,
Galileo, Galileo,
} }
#[derive(PartialEq, Debug, Default, Copy, Clone)]
pub(crate) enum DeviceType {
#[default]
Unknown,
SteamDeck,
LegionGoS,
}
#[derive(Display, EnumString, PartialEq, Debug, Copy, Clone, TryFromPrimitive)] #[derive(Display, EnumString, PartialEq, Debug, Copy, Clone, TryFromPrimitive)]
#[strum(ascii_case_insensitive)] #[strum(ascii_case_insensitive)]
#[repr(u32)] #[repr(u32)]
@ -38,13 +47,13 @@ pub enum FanControlState {
Os = 1, Os = 1,
} }
impl FromStr for HardwareVariant { impl FromStr for SteamDeckVariant {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<HardwareVariant, Self::Err> { fn from_str(input: &str) -> Result<SteamDeckVariant, Self::Err> {
Ok(match input { Ok(match input {
"Jupiter" => HardwareVariant::Jupiter, "Jupiter" => SteamDeckVariant::Jupiter,
"Galileo" => HardwareVariant::Galileo, "Galileo" => SteamDeckVariant::Galileo,
_ => HardwareVariant::Unknown, _ => SteamDeckVariant::Unknown,
}) })
} }
} }
@ -58,21 +67,27 @@ pub enum FactoryResetKind {
All = 3, All = 3,
} }
pub(crate) async fn variant() -> Result<HardwareVariant> { pub(crate) async fn steam_deck_variant() -> Result<SteamDeckVariant> {
let sys_vendor = fs::read_to_string(path(SYS_VENDOR_PATH)).await?; let sys_vendor = fs::read_to_string(path(SYS_VENDOR_PATH)).await?;
if sys_vendor.trim_end() != "Valve" { 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?; 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<bool> { pub(crate) async fn device_type() -> Result<DeviceType> {
match variant().await { if variant().await? != SteamDeckVariant::Unknown {
Ok(variant) => Ok(variant != HardwareVariant::Unknown), return Ok(DeviceType::SteamDeck);
Err(e) => Err(e),
} }
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 { pub(crate) struct FanControl {
@ -153,15 +168,15 @@ pub mod test {
use zbus::fdo; use zbus::fdo;
use zbus::zvariant::{ObjectPath, OwnedObjectPath}; 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?; create_dir_all(crate::path("/sys/class/dmi/id")).await?;
match model { match model {
HardwareVariant::Unknown => write(crate::path(SYS_VENDOR_PATH), "LENOVO\n").await?, SteamDeckVariant::Unknown => write(crate::path(SYS_VENDOR_PATH), "LENOVO\n").await?,
HardwareVariant::Jupiter => { SteamDeckVariant::Jupiter => {
write(crate::path(SYS_VENDOR_PATH), "Valve\n").await?; write(crate::path(SYS_VENDOR_PATH), "Valve\n").await?;
write(crate::path(BOARD_NAME_PATH), "Jupiter\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(SYS_VENDOR_PATH), "Valve\n").await?;
write(crate::path(BOARD_NAME_PATH), "Galileo\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")) create_dir_all(crate::path("/sys/class/dmi/id"))
.await .await
.expect("create_dir_all"); .expect("create_dir_all");
assert!(variant().await.is_err()); assert!(steam_deck_variant().await.is_err());
write(crate::path(SYS_VENDOR_PATH), "LENOVO\n") write(crate::path(SYS_VENDOR_PATH), "LENOVO\n")
.await .await
.expect("write"); .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") write(crate::path(SYS_VENDOR_PATH), "Valve\n")
.await .await
@ -189,17 +250,35 @@ pub mod test {
write(crate::path(BOARD_NAME_PATH), "Jupiter\n") write(crate::path(BOARD_NAME_PATH), "Jupiter\n")
.await .await
.expect("write"); .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") write(crate::path(BOARD_NAME_PATH), "Galileo\n")
.await .await
.expect("write"); .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") write(crate::path(BOARD_NAME_PATH), "Neptune\n")
.await .await
.expect("write"); .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] #[test]

View file

@ -20,7 +20,9 @@ use zbus::{fdo, interface, Connection};
use crate::daemon::root::{Command, RootCommand}; use crate::daemon::root::{Command, RootCommand};
use crate::daemon::DaemonCommand; use crate::daemon::DaemonCommand;
use crate::error::{to_zbus_error, to_zbus_fdo_error}; 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::job::JobManager;
use crate::platform::platform_config; use crate::platform::platform_config;
use crate::power::{ use crate::power::{
@ -78,7 +80,7 @@ impl SteamOSManager {
Ok(SteamOSManager { Ok(SteamOSManager {
fan_control: FanControl::new(connection.clone()), fan_control: FanControl::new(connection.clone()),
wifi_debug_mode: WifiDebugMode::Off, 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?, job_manager: JobManager::new(connection.clone()).await?,
connection, connection,
channel, channel,
@ -151,9 +153,9 @@ impl SteamOSManager {
async fn als_calibration_gain(&self) -> Vec<f64> { async fn als_calibration_gain(&self) -> Vec<f64> {
// Run script to get calibration value // Run script to get calibration value
let mut gains = Vec::new(); let mut gains = Vec::new();
let indices: &[&str] = match variant().await { let indices: &[&str] = match steam_deck_variant().await {
Ok(HardwareVariant::Jupiter) => &["2"], Ok(SteamDeckVariant::Jupiter) => &["2"],
Ok(HardwareVariant::Galileo) => &["2", "4"], Ok(SteamDeckVariant::Galileo) => &["2", "4"],
_ => return Vec::new(), _ => return Vec::new(),
}; };
for index in indices { for index in indices {
@ -176,10 +178,10 @@ impl SteamOSManager {
async fn get_als_integration_time_file_descriptor(&self, index: u32) -> fdo::Result<Fd> { async fn get_als_integration_time_file_descriptor(&self, index: u32) -> fdo::Result<Fd> {
// Get the file descriptor for the als integration time sysfs path // Get the file descriptor for the als integration time sysfs path
let i0 = match variant().await.map_err(to_zbus_fdo_error)? { let i0 = match steam_deck_variant().await.map_err(to_zbus_fdo_error)? {
HardwareVariant::Jupiter => 1, SteamDeckVariant::Jupiter => 1,
HardwareVariant::Galileo => index, SteamDeckVariant::Galileo => index,
HardwareVariant::Unknown => { SteamDeckVariant::Unknown => {
return Err(fdo::Error::Failed(String::from("Unknown model"))) return Err(fdo::Error::Failed(String::from("Unknown model")))
} }
}; };
@ -457,7 +459,7 @@ mod test {
async fn start() -> Result<TestHandle> { async fn start() -> Result<TestHandle> {
let mut handle = testing::start(); 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?; create_dir_all(crate::path("/etc/NetworkManager/conf.d")).await?;
write( write(
crate::path("/etc/NetworkManager/conf.d/99-valve-wifi-backend.conf"), crate::path("/etc/NetworkManager/conf.d/99-valve-wifi-backend.conf"),
@ -608,22 +610,22 @@ mod test {
.process_cb .process_cb
.set(|_, _| Ok((0, String::from("0.0\n")))); .set(|_, _| Ok((0, String::from("0.0\n"))));
fake_model(HardwareVariant::Jupiter) fake_model(SteamDeckVariant::Jupiter)
.await .await
.expect("fake_model"); .expect("fake_model");
assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[0.0]); assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[0.0]);
fake_model(HardwareVariant::Galileo) fake_model(SteamDeckVariant::Galileo)
.await .await
.expect("fake_model"); .expect("fake_model");
assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[0.0, 0.0]); assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[0.0, 0.0]);
fake_model(HardwareVariant::Unknown) fake_model(SteamDeckVariant::Unknown)
.await .await
.expect("fake_model"); .expect("fake_model");
assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[]); assert_eq!(proxy.als_calibration_gain().await.unwrap(), &[]);
fake_model(HardwareVariant::Jupiter) fake_model(SteamDeckVariant::Jupiter)
.await .await
.expect("fake_model"); .expect("fake_model");

View file

@ -19,7 +19,7 @@ use crate::cec::{HdmiCecControl, HdmiCecState};
use crate::daemon::user::Command; use crate::daemon::user::Command;
use crate::daemon::DaemonCommand; use crate::daemon::DaemonCommand;
use crate::error::{to_zbus_error, to_zbus_fdo_error, zbus_to_zbus_fdo}; 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::job::JobManagerCommand;
use crate::platform::platform_config; use crate::platform::platform_config;
use crate::power::{ use crate::power::{
@ -680,10 +680,10 @@ pub(crate) async fn create_interfaces(
create_config_interfaces(&proxy, object_server, &job_manager).await?; 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?; 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?; 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?; 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?; object_server.at(MANAGER_PATH, wifi_debug).await?;
} }
@ -740,7 +740,7 @@ mod test {
use crate::daemon::channel; use crate::daemon::channel;
use crate::daemon::user::UserContext; use crate::daemon::user::UserContext;
use crate::hardware::test::fake_model; use crate::hardware::test::fake_model;
use crate::hardware::HardwareVariant; use crate::hardware::SteamDeckVariant;
use crate::platform::{ use crate::platform::{
BatteryChargeLimitConfig, PlatformConfig, RangeConfig, ResetConfig, ScriptConfig, BatteryChargeLimitConfig, PlatformConfig, RangeConfig, ResetConfig, ScriptConfig,
ServiceConfig, StorageConfig, ServiceConfig, StorageConfig,
@ -813,7 +813,7 @@ mod test {
write(&exe_path, "").await?; write(&exe_path, "").await?;
set_permissions(&exe_path, PermissionsExt::from_mode(0o700)).await?; set_permissions(&exe_path, PermissionsExt::from_mode(0o700)).await?;
fake_model(HardwareVariant::Galileo).await?; fake_model(SteamDeckVariant::Galileo).await?;
handle handle
.test .test
.process_cb .process_cb

View file

@ -18,7 +18,7 @@ use tokio::sync::OnceCell;
use tokio::task::spawn_blocking; use tokio::task::spawn_blocking;
#[cfg(not(test))] #[cfg(not(test))]
use crate::hardware::is_deck; use crate::hardware::{device_type, DeviceType};
#[cfg(not(test))] #[cfg(not(test))]
static CONFIG: OnceCell<Option<PlatformConfig>> = OnceCell::const_new(); static CONFIG: OnceCell<Option<PlatformConfig>> = OnceCell::const_new();
@ -137,12 +137,14 @@ impl<T: Clone> RangeConfig<T> {
impl PlatformConfig { impl PlatformConfig {
#[cfg(not(test))] #[cfg(not(test))]
async fn load() -> Result<Option<PlatformConfig>> { async fn load() -> Result<Option<PlatformConfig>> {
if !is_deck().await? { let platform = match device_type().await? {
// Non-Steam Deck platforms are not yet supported DeviceType::SteamDeck => "jupiter",
return Ok(None); _ => return Ok(None),
} };
let config = read_to_string(format!(
let config = read_to_string("/usr/share/steamos-manager/platforms/jupiter.toml").await?; "/usr/share/steamos-manager/platforms/{platform}.toml"
))
.await?;
Ok(Some(toml::from_str(config.as_ref())?)) Ok(Some(toml::from_str(config.as_ref())?))
} }

View file

@ -16,7 +16,7 @@ use tokio::fs::{self, try_exists, File};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tracing::{error, warn}; use tracing::{error, warn};
use crate::hardware::is_deck; use crate::hardware::{device_type, DeviceType};
use crate::platform::platform_config; use crate::platform::platform_config;
use crate::{path, write_synced}; use crate::{path, write_synced};
@ -139,7 +139,7 @@ pub(crate) async fn get_gpu_power_profile() -> Result<GPUPowerProfile> {
// check which profile is current and return if possible // check which profile is current and return if possible
let contents = read_gpu_sysfs_contents(GPU_POWER_PROFILE_SUFFIX).await?; 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. // firmware support setting the value to no-op values.
let lines = contents.lines(); let lines = contents.lines();
for line in lines { for line in lines {
@ -162,7 +162,7 @@ pub(crate) async fn get_gpu_power_profile() -> Result<GPUPowerProfile> {
pub(crate) async fn get_available_gpu_power_profiles() -> Result<Vec<(u32, String)>> { pub(crate) async fn get_available_gpu_power_profiles() -> Result<Vec<(u32, String)>> {
let contents = read_gpu_sysfs_contents(GPU_POWER_PROFILE_SUFFIX).await?; 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 mut map = Vec::new();
let lines = contents.lines(); let lines = contents.lines();
@ -437,7 +437,7 @@ pub(crate) async fn set_max_charge_level(limit: i32) -> Result<()> {
pub(crate) mod test { pub(crate) mod test {
use super::*; use super::*;
use crate::hardware::test::fake_model; use crate::hardware::test::fake_model;
use crate::hardware::HardwareVariant; use crate::hardware::SteamDeckVariant;
use crate::platform::{BatteryChargeLimitConfig, PlatformConfig}; use crate::platform::{BatteryChargeLimitConfig, PlatformConfig};
use crate::{enum_roundtrip, testing}; use crate::{enum_roundtrip, testing};
use anyhow::anyhow; use anyhow::anyhow;
@ -815,7 +815,7 @@ CCLK_RANGE in Core0:
write(filename.as_path(), contents).await.expect("write"); write(filename.as_path(), contents).await.expect("write");
fake_model(HardwareVariant::Unknown) fake_model(SteamDeckVariant::Unknown)
.await .await
.expect("fake_model"); .expect("fake_model");
@ -836,7 +836,7 @@ CCLK_RANGE in Core0:
] ]
); );
fake_model(HardwareVariant::Jupiter) fake_model(SteamDeckVariant::Jupiter)
.await .await
.expect("fake_model"); .expect("fake_model");
@ -872,7 +872,7 @@ CCLK_RANGE in Core0:
write(filename.as_path(), contents).await.expect("write"); write(filename.as_path(), contents).await.expect("write");
fake_model(HardwareVariant::Unknown) fake_model(SteamDeckVariant::Unknown)
.await .await
.expect("fake_model"); .expect("fake_model");
@ -894,7 +894,7 @@ CCLK_RANGE in Core0:
] ]
); );
fake_model(HardwareVariant::Jupiter) fake_model(SteamDeckVariant::Jupiter)
.await .await
.expect("fake_model"); .expect("fake_model");
@ -929,7 +929,7 @@ CCLK_RANGE in Core0:
write(filename.as_path(), contents).await.expect("write"); write(filename.as_path(), contents).await.expect("write");
fake_model(HardwareVariant::Unknown) fake_model(SteamDeckVariant::Unknown)
.await .await
.expect("fake_model"); .expect("fake_model");
assert_eq!( assert_eq!(
@ -937,7 +937,7 @@ CCLK_RANGE in Core0:
GPUPowerProfile::Video GPUPowerProfile::Video
); );
fake_model(HardwareVariant::Jupiter) fake_model(SteamDeckVariant::Jupiter)
.await .await
.expect("fake_model"); .expect("fake_model");
assert_eq!( assert_eq!(
@ -967,12 +967,12 @@ CCLK_RANGE in Core0:
write(filename.as_path(), contents).await.expect("write"); write(filename.as_path(), contents).await.expect("write");
fake_model(HardwareVariant::Unknown) fake_model(SteamDeckVariant::Unknown)
.await .await
.expect("fake_model"); .expect("fake_model");
assert!(get_gpu_power_profile().await.is_err()); assert!(get_gpu_power_profile().await.is_err());
fake_model(HardwareVariant::Jupiter) fake_model(SteamDeckVariant::Jupiter)
.await .await
.expect("fake_model"); .expect("fake_model");
assert!(get_gpu_power_profile().await.is_err()); assert!(get_gpu_power_profile().await.is_err());
@ -1000,12 +1000,12 @@ CCLK_RANGE in Core0:
write(filename.as_path(), contents).await.expect("write"); write(filename.as_path(), contents).await.expect("write");
fake_model(HardwareVariant::Unknown) fake_model(SteamDeckVariant::Unknown)
.await .await
.expect("fake_model"); .expect("fake_model");
assert!(get_gpu_power_profile().await.is_err()); assert!(get_gpu_power_profile().await.is_err());
fake_model(HardwareVariant::Jupiter) fake_model(SteamDeckVariant::Jupiter)
.await .await
.expect("fake_model"); .expect("fake_model");
assert!(get_gpu_power_profile().await.is_err()); assert!(get_gpu_power_profile().await.is_err());