/* * Copyright © 2023 Collabora Ltd. * Copyright © 2024 Valve Software * Copyright © 2024 Igalia S.L. * * SPDX-License-Identifier: MIT */ use anyhow::Result; use std::collections::HashMap; use tokio::sync::mpsc::{Sender, UnboundedSender}; use tokio::sync::oneshot; use tracing::error; use zbus::object_server::SignalEmitter; use zbus::proxy::{Builder, CacheProperties}; use zbus::{fdo, interface, zvariant, Connection, ObjectServer, Proxy}; 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::{device_type, steam_deck_variant, DeviceType, SteamDeckVariant}; use crate::job::JobManagerCommand; use crate::platform::platform_config; use crate::power::{ get_available_cpu_scaling_governors, get_available_gpu_performance_levels, get_available_gpu_power_profiles, get_cpu_scaling_governor, get_gpu_clocks, get_gpu_clocks_range, get_gpu_performance_level, get_gpu_power_profile, get_max_charge_level, get_tdp_limit, get_tdp_limit_range, }; use crate::wifi::{ get_wifi_backend, get_wifi_power_management_state, list_wifi_interfaces, WifiBackend, }; use crate::API_VERSION; const MANAGER_PATH: &str = "/com/steampowered/SteamOSManager1"; macro_rules! method { ($self:expr, $method:expr, $($args:expr),+) => { $self.proxy .call($method, &($($args,)*)) .await .map_err(zbus_to_zbus_fdo) }; ($self:expr, $method:expr) => { $self.proxy .call($method, &()) .await .map_err(zbus_to_zbus_fdo) }; } macro_rules! job_method { ($self:expr, $method:expr, $($args:expr),+) => { { let (tx, rx) = oneshot::channel(); $self.job_manager.send(JobManagerCommand::MirrorJob { connection: $self.proxy.connection().clone(), path: method!($self, $method, $($args),+)?, reply: tx, }).map_err(to_zbus_fdo_error)?; rx.await.map_err(to_zbus_fdo_error)? } }; ($self:expr, $method:expr) => { { let (tx, rx) = oneshot::channel(); $self.job_manager.send(JobManagerCommand::MirrorJob { connection: $self.proxy.connection().clone(), path: method!($self, $method)?, reply: tx, }).map_err(to_zbus_fdo_error)?; rx.await.map_err(to_zbus_fdo_error)? } }; } macro_rules! getter { ($self:expr, $prop:expr) => { $self .proxy .get_property($prop) .await .map_err(zbus_to_zbus_fdo) }; } macro_rules! setter { ($self:expr, $prop:expr, $value:expr) => { $self .proxy .set_property($prop, $value) .await .map_err(|e| zbus::Error::FDO(Box::new(e))) }; } struct SteamOSManager { proxy: Proxy<'static>, _job_manager: UnboundedSender, } struct AmbientLightSensor1 { proxy: Proxy<'static>, } struct BatteryChargeLimit1 { proxy: Proxy<'static>, } struct CpuScaling1 { proxy: Proxy<'static>, } struct FactoryReset1 { proxy: Proxy<'static>, } struct FanControl1 { proxy: Proxy<'static>, } struct GpuPerformanceLevel1 { proxy: Proxy<'static>, } struct GpuPowerProfile1 { proxy: Proxy<'static>, } struct TdpLimit1 { proxy: Proxy<'static>, } struct HdmiCec1 { hdmi_cec: HdmiCecControl<'static>, } struct Manager2 { proxy: Proxy<'static>, channel: Sender, } struct Storage1 { proxy: Proxy<'static>, job_manager: UnboundedSender, } struct UpdateBios1 { proxy: Proxy<'static>, job_manager: UnboundedSender, } struct UpdateDock1 { proxy: Proxy<'static>, job_manager: UnboundedSender, } struct WifiDebug1 { proxy: Proxy<'static>, } struct WifiDebugDump1 { proxy: Proxy<'static>, } struct WifiPowerManagement1 { proxy: Proxy<'static>, } impl SteamOSManager { pub async fn new( system_conn: Connection, proxy: Proxy<'static>, job_manager: UnboundedSender, ) -> Result { job_manager.send(JobManagerCommand::MirrorConnection(system_conn))?; Ok(SteamOSManager { proxy, // Hold onto extra sender to make sure the channel isn't dropped // early on devices we don't have any interfaces that use job control. _job_manager: job_manager, }) } } #[interface(name = "com.steampowered.SteamOSManager1.Manager")] impl SteamOSManager { #[zbus(property(emits_changed_signal = "const"))] async fn version(&self) -> u32 { API_VERSION } #[zbus(property(emits_changed_signal = "const"))] async fn tdp_limit_min(&self) -> u32 { 0 } #[zbus(property)] async fn wifi_debug_mode_state(&self) -> fdo::Result { getter!(self, "WifiDebugModeState") } async fn set_wifi_debug_mode( &self, mode: u32, buffer_size: u32, #[zbus(signal_emitter)] ctx: SignalEmitter<'_>, ) -> fdo::Result<()> { let _: () = method!(self, "SetWifiDebugMode", mode, buffer_size)?; self.wifi_debug_mode_state_changed(&ctx) .await .map_err(zbus_to_zbus_fdo)?; Ok(()) } #[zbus(property(emits_changed_signal = "false"))] async fn wifi_backend(&self) -> fdo::Result { match get_wifi_backend().await { Ok(backend) => Ok(backend as u32), Err(e) => Err(to_zbus_fdo_error(e)), } } #[zbus(property)] async fn set_wifi_backend(&self, backend: u32) -> zbus::Result<()> { self.proxy.call("SetWifiBackend", &(backend)).await } } #[interface(name = "com.steampowered.SteamOSManager1.AmbientLightSensor1")] impl AmbientLightSensor1 { #[zbus(property(emits_changed_signal = "false"))] async fn als_calibration_gain(&self) -> fdo::Result> { getter!(self, "AlsCalibrationGain") } } impl BatteryChargeLimit1 { const DEFAULT_SUGGESTED_MINIMUM_LIMIT: i32 = 10; } #[interface(name = "com.steampowered.SteamOSManager1.BatteryChargeLimit1")] impl BatteryChargeLimit1 { #[zbus(property(emits_changed_signal = "false"))] async fn max_charge_level(&self) -> fdo::Result { let level = get_max_charge_level().await.map_err(to_zbus_fdo_error)?; if level <= 0 { Ok(-1) } else { Ok(level) } } #[zbus(property)] async fn set_max_charge_level(&self, limit: i32) -> zbus::Result<()> { self.proxy.call("SetMaxChargeLevel", &(limit)).await } #[zbus(property(emits_changed_signal = "const"))] async fn suggested_minimum_limit(&self) -> i32 { let Ok(Some(ref config)) = platform_config().await else { return BatteryChargeLimit1::DEFAULT_SUGGESTED_MINIMUM_LIMIT; }; let Some(ref config) = config.battery_charge_limit else { return BatteryChargeLimit1::DEFAULT_SUGGESTED_MINIMUM_LIMIT; }; config .suggested_minimum_limit .unwrap_or(BatteryChargeLimit1::DEFAULT_SUGGESTED_MINIMUM_LIMIT) } } #[interface(name = "com.steampowered.SteamOSManager1.CpuScaling1")] impl CpuScaling1 { #[zbus(property(emits_changed_signal = "false"))] async fn available_cpu_scaling_governors(&self) -> fdo::Result> { let governors = get_available_cpu_scaling_governors() .await .map_err(to_zbus_fdo_error)?; let mut result = Vec::new(); for g in governors { result.push(g.to_string()); } Ok(result) } #[zbus(property(emits_changed_signal = "false"))] async fn cpu_scaling_governor(&self) -> fdo::Result { let governor = get_cpu_scaling_governor() .await .map_err(to_zbus_fdo_error)?; Ok(governor.to_string()) } #[zbus(property)] async fn set_cpu_scaling_governor(&self, governor: String) -> zbus::Result<()> { self.proxy.call("SetCpuScalingGovernor", &(governor)).await } } #[interface(name = "com.steampowered.SteamOSManager1.FactoryReset1")] impl FactoryReset1 { async fn prepare_factory_reset(&self, flags: u32) -> fdo::Result { method!(self, "PrepareFactoryReset", flags) } } #[interface(name = "com.steampowered.SteamOSManager1.FanControl1")] impl FanControl1 { #[zbus(property(emits_changed_signal = "false"))] async fn fan_control_state(&self) -> fdo::Result { getter!(self, "FanControlState") } #[zbus(property)] async fn set_fan_control_state(&self, state: u32) -> zbus::Result<()> { setter!(self, "FanControlState", state) } } #[interface(name = "com.steampowered.SteamOSManager1.GpuPerformanceLevel1")] impl GpuPerformanceLevel1 { #[zbus(property(emits_changed_signal = "false"))] async fn available_gpu_performance_levels(&self) -> fdo::Result> { get_available_gpu_performance_levels() .await .inspect_err(|message| error!("Error getting GPU performance levels: {message}")) .map(|levels| levels.into_iter().map(|level| level.to_string()).collect()) .map_err(to_zbus_fdo_error) } #[zbus(property(emits_changed_signal = "false"))] async fn gpu_performance_level(&self) -> fdo::Result { match get_gpu_performance_level().await { Ok(level) => Ok(level.to_string()), Err(e) => { error!("Error getting GPU performance level: {e}"); Err(to_zbus_fdo_error(e)) } } } #[zbus(property)] async fn set_gpu_performance_level(&self, level: &str) -> zbus::Result<()> { self.proxy.call("SetGpuPerformanceLevel", &(level)).await } #[zbus(property(emits_changed_signal = "false"))] async fn manual_gpu_clock(&self) -> fdo::Result { get_gpu_clocks() .await .inspect_err(|message| error!("Error getting manual GPU clock: {message}")) .map_err(to_zbus_fdo_error) } #[zbus(property)] async fn set_manual_gpu_clock(&self, clocks: u32) -> zbus::Result<()> { self.proxy.call("SetManualGpuClock", &(clocks)).await } #[zbus(property(emits_changed_signal = "const"))] async fn manual_gpu_clock_min(&self) -> fdo::Result { Ok(get_gpu_clocks_range().await.map_err(to_zbus_fdo_error)?.0) } #[zbus(property(emits_changed_signal = "const"))] async fn manual_gpu_clock_max(&self) -> fdo::Result { Ok(get_gpu_clocks_range().await.map_err(to_zbus_fdo_error)?.1) } } #[interface(name = "com.steampowered.SteamOSManager1.GpuPowerProfile1")] impl GpuPowerProfile1 { #[zbus(property(emits_changed_signal = "false"))] async fn available_gpu_power_profiles(&self) -> fdo::Result> { let (_, names): (Vec, Vec) = get_available_gpu_power_profiles() .await .map_err(to_zbus_fdo_error)? .into_iter() .unzip(); Ok(names) } #[zbus(property(emits_changed_signal = "false"))] async fn gpu_power_profile(&self) -> fdo::Result { match get_gpu_power_profile().await { Ok(profile) => Ok(profile.to_string()), Err(e) => { error!("Error getting GPU power profile: {e}"); Err(to_zbus_fdo_error(e)) } } } #[zbus(property)] async fn set_gpu_power_profile(&self, profile: &str) -> zbus::Result<()> { self.proxy.call("SetGpuPowerProfile", &(profile)).await } } impl HdmiCec1 { async fn new(connection: &Connection) -> Result { let hdmi_cec = HdmiCecControl::new(connection).await?; Ok(HdmiCec1 { hdmi_cec }) } } #[interface(name = "com.steampowered.SteamOSManager1.HdmiCec1")] impl HdmiCec1 { #[zbus(property(emits_changed_signal = "false"))] async fn hdmi_cec_state(&self) -> fdo::Result { match self.hdmi_cec.get_enabled_state().await { Ok(state) => Ok(state as u32), Err(e) => Err(to_zbus_fdo_error(e)), } } #[zbus(property)] async fn set_hdmi_cec_state(&self, state: u32) -> zbus::Result<()> { let state = match HdmiCecState::try_from(state) { Ok(state) => state, Err(err) => return Err(fdo::Error::InvalidArgs(err.to_string()).into()), }; self.hdmi_cec .set_enabled_state(state) .await .inspect_err(|message| error!("Error setting CEC state: {message}")) .map_err(to_zbus_error) } } #[interface(name = "com.steampowered.SteamOSManager1.Manager2")] impl Manager2 { async fn reload_config(&self) -> fdo::Result<()> { self.channel .send(DaemonCommand::ReadConfig) .await .inspect_err(|message| error!("Error sending ReadConfig command: {message}")) .map_err(to_zbus_fdo_error)?; method!(self, "ReloadConfig") } } #[interface(name = "com.steampowered.SteamOSManager1.Storage1")] impl Storage1 { async fn format_device( &mut self, device: &str, label: &str, validate: bool, ) -> fdo::Result { job_method!(self, "FormatDevice", device, label, validate) } async fn trim_devices(&mut self) -> fdo::Result { job_method!(self, "TrimDevices") } } #[interface(name = "com.steampowered.SteamOSManager1.TdpLimit1")] impl TdpLimit1 { #[zbus(property(emits_changed_signal = "false"))] async fn tdp_limit(&self) -> u32 { get_tdp_limit().await.unwrap_or(0) } #[zbus(property)] async fn set_tdp_limit(&self, limit: u32) -> zbus::Result<()> { self.proxy.call("SetTdpLimit", &(limit)).await } #[zbus(property(emits_changed_signal = "const"))] async fn tdp_limit_min(&self) -> u32 { get_tdp_limit_range().await.map(|(min, _)| min).unwrap_or(0) } #[zbus(property(emits_changed_signal = "const"))] async fn tdp_limit_max(&self) -> u32 { get_tdp_limit_range().await.map(|(_, max)| max).unwrap_or(0) } } #[interface(name = "com.steampowered.SteamOSManager1.UpdateBios1")] impl UpdateBios1 { async fn update_bios(&mut self) -> fdo::Result { job_method!(self, "UpdateBios") } } #[interface(name = "com.steampowered.SteamOSManager1.UpdateDock1")] impl UpdateDock1 { async fn update_dock(&mut self) -> fdo::Result { job_method!(self, "UpdateDock") } } #[interface(name = "com.steampowered.SteamOSManager1.WifiDebug1")] impl WifiDebug1 { #[zbus(property)] async fn wifi_debug_mode_state(&self) -> fdo::Result { getter!(self, "WifiDebugModeState") } async fn set_wifi_debug_mode( &self, mode: u32, options: HashMap<&str, zvariant::Value<'_>>, #[zbus(signal_emitter)] ctx: SignalEmitter<'_>, ) -> fdo::Result<()> { let _: () = method!(self, "SetWifiDebugMode", mode, options)?; self.wifi_debug_mode_state_changed(&ctx) .await .map_err(zbus_to_zbus_fdo)?; Ok(()) } #[zbus(property(emits_changed_signal = "false"))] async fn wifi_backend(&self) -> fdo::Result { match get_wifi_backend().await { Ok(backend) => Ok(backend.to_string()), Err(e) => Err(to_zbus_fdo_error(e)), } } #[zbus(property)] async fn set_wifi_backend(&self, backend: &str) -> zbus::Result<()> { let backend = match WifiBackend::try_from(backend) { Ok(backend) => backend, Err(e) => return Err(fdo::Error::InvalidArgs(e.to_string()).into()), }; self.proxy.call("SetWifiBackend", &(backend as u32)).await } async fn capture_debug_trace_output(&self) -> fdo::Result { method!(self, "CaptureDebugTraceOutput") } } #[interface(name = "com.steampowered.SteamOSManager1.WifiDebugDump1")] impl WifiDebugDump1 { async fn generate_debug_dump(&self) -> fdo::Result { method!(self, "GenerateDebugDump") } } #[interface(name = "com.steampowered.SteamOSManager1.WifiPowerManagement1")] impl WifiPowerManagement1 { #[zbus(property(emits_changed_signal = "false"))] async fn wifi_power_management_state(&self) -> fdo::Result { match get_wifi_power_management_state().await { Ok(state) => Ok(state as u32), Err(e) => Err(to_zbus_fdo_error(e)), } } #[zbus(property)] async fn set_wifi_power_management_state(&self, state: u32) -> zbus::Result<()> { self.proxy .call("SetWifiPowerManagementState", &(state)) .await } } async fn create_config_interfaces( proxy: &Proxy<'static>, object_server: &ObjectServer, job_manager: &UnboundedSender, ) -> Result<()> { let Some(config) = platform_config().await? else { return Ok(()); }; let factory_reset = FactoryReset1 { proxy: proxy.clone(), }; let fan_control = FanControl1 { proxy: proxy.clone(), }; let storage = Storage1 { proxy: proxy.clone(), job_manager: job_manager.clone(), }; let update_bios = UpdateBios1 { proxy: proxy.clone(), job_manager: job_manager.clone(), }; let update_dock = UpdateDock1 { proxy: proxy.clone(), job_manager: job_manager.clone(), }; if config.factory_reset.is_some() { object_server.at(MANAGER_PATH, factory_reset).await?; } if config.fan_control.is_some() { object_server.at(MANAGER_PATH, fan_control).await?; } if config.storage.is_some() { object_server.at(MANAGER_PATH, storage).await?; } if let Some(config) = config.update_bios.as_ref() { match config.is_valid(true).await { Ok(true) => { object_server.at(MANAGER_PATH, update_bios).await?; } Ok(false) => (), Err(e) => error!("Failed to verify if BIOS update config is valid: {e}"), } } if let Some(config) = config.update_dock.as_ref() { match config.is_valid(true).await { Ok(true) => { object_server.at(MANAGER_PATH, update_dock).await?; } Ok(false) => (), Err(e) => error!("Failed to verify if dock update config is valid: {e}"), } } Ok(()) } #[allow(clippy::too_many_lines)] pub(crate) async fn create_interfaces( session: Connection, system: Connection, daemon: Sender, job_manager: UnboundedSender, ) -> Result<()> { let proxy = Builder::::new(&system) .destination("com.steampowered.SteamOSManager1")? .path("/com/steampowered/SteamOSManager1")? .interface("com.steampowered.SteamOSManager1.RootManager")? .cache_properties(CacheProperties::No) .build() .await?; let manager = SteamOSManager::new(system.clone(), proxy.clone(), job_manager.clone()).await?; let als = AmbientLightSensor1 { proxy: proxy.clone(), }; let battery_charge_limit = BatteryChargeLimit1 { proxy: proxy.clone(), }; let cpu_scaling = CpuScaling1 { proxy: proxy.clone(), }; let gpu_performance_level = GpuPerformanceLevel1 { proxy: proxy.clone(), }; let gpu_power_profile = GpuPowerProfile1 { proxy: proxy.clone(), }; let hdmi_cec = HdmiCec1::new(&session).await?; let manager2 = Manager2 { proxy: proxy.clone(), channel: daemon, }; let tdp_limit = TdpLimit1 { proxy: proxy.clone(), }; let wifi_debug = WifiDebug1 { proxy: proxy.clone(), }; let wifi_debug_dump = WifiDebugDump1 { proxy: proxy.clone(), }; let wifi_power_management = WifiPowerManagement1 { proxy: proxy.clone(), }; let object_server = session.object_server(); object_server.at(MANAGER_PATH, manager).await?; create_config_interfaces(&proxy, object_server, &job_manager).await?; if device_type().await.unwrap_or_default() == DeviceType::SteamDeck { object_server.at(MANAGER_PATH, als).await?; } if steam_deck_variant().await.unwrap_or_default() == SteamDeckVariant::Galileo { object_server.at(MANAGER_PATH, wifi_debug_dump).await?; } if get_max_charge_level().await.is_ok() { object_server.at(MANAGER_PATH, battery_charge_limit).await?; } object_server.at(MANAGER_PATH, cpu_scaling).await?; if !get_available_gpu_performance_levels() .await .unwrap_or_default() .is_empty() { object_server .at(MANAGER_PATH, gpu_performance_level) .await?; } if !get_available_gpu_power_profiles() .await .unwrap_or_default() .is_empty() { object_server.at(MANAGER_PATH, gpu_power_profile).await?; } if hdmi_cec.hdmi_cec.get_enabled_state().await.is_ok() { object_server.at(MANAGER_PATH, hdmi_cec).await?; } object_server.at(MANAGER_PATH, manager2).await?; if get_tdp_limit().await.is_ok() { object_server.at(MANAGER_PATH, tdp_limit).await?; } if steam_deck_variant().await.unwrap_or_default() == SteamDeckVariant::Galileo { object_server.at(MANAGER_PATH, wifi_debug).await?; } if !list_wifi_interfaces().await.unwrap_or_default().is_empty() { object_server .at(MANAGER_PATH, wifi_power_management) .await?; } Ok(()) } #[cfg(test)] mod test { use super::*; use crate::daemon::channel; use crate::daemon::user::UserContext; use crate::hardware::test::fake_model; use crate::hardware::SteamDeckVariant; use crate::platform::{ BatteryChargeLimitConfig, PlatformConfig, RangeConfig, ResetConfig, ScriptConfig, ServiceConfig, StorageConfig, }; use crate::systemd::test::{MockManager, MockUnit}; use crate::{path, power, testing}; use std::os::unix::fs::PermissionsExt; use std::time::Duration; use tokio::fs::{set_permissions, write}; use tokio::sync::mpsc::unbounded_channel; use tokio::time::sleep; use zbus::object_server::Interface; use zbus::Connection; struct TestHandle { _handle: testing::TestHandle, connection: Connection, } fn all_config() -> Option { Some(PlatformConfig { factory_reset: Some(ResetConfig::default()), update_bios: Some(ScriptConfig::default()), update_dock: Some(ScriptConfig::default()), storage: Some(StorageConfig::default()), fan_control: Some(ServiceConfig::Systemd(String::from( "jupiter-fan-control.service", ))), tdp_limit: Some(RangeConfig::new(3, 15)), gpu_clocks: Some(RangeConfig::new(200, 1600)), battery_charge_limit: Some(BatteryChargeLimitConfig { suggested_minimum_limit: Some(10), hwmon_name: String::from("steamdeck_hwmon"), attribute: String::from("max_battery_charge_level"), }), }) } async fn start(mut platform_config: Option) -> Result { let mut handle = testing::start(); let (tx_ctx, _rx_ctx) = channel::(); let (tx_job, _rx_job) = unbounded_channel::(); if let Some(ref mut config) = platform_config { config.set_test_paths(); } handle.test.platform_config.replace(platform_config); let connection = handle.new_dbus().await?; connection.request_name("org.freedesktop.systemd1").await?; sleep(Duration::from_millis(10)).await; { let object_server = connection.object_server(); object_server .at("/org/freedesktop/systemd1", MockManager::default()) .await?; let mut prc = MockUnit::default(); prc.unit_file = String::from("disabled"); object_server .at( "/org/freedesktop/systemd1/unit/plasma_2dremotecontrollers_2eservice", prc, ) .await?; } let exe_path = path("exe"); write(&exe_path, "").await?; set_permissions(&exe_path, PermissionsExt::from_mode(0o700)).await?; fake_model(SteamDeckVariant::Galileo).await?; handle .test .process_cb .set(|_, _| Ok((0, String::from("Interface wlan0")))); power::test::create_nodes().await?; create_interfaces(connection.clone(), connection.clone(), tx_ctx, tx_job).await?; sleep(Duration::from_millis(1)).await; Ok(TestHandle { _handle: handle, connection, }) } #[tokio::test] async fn interface_matches() { let test = start(None).await.expect("start"); let remote = testing::InterfaceIntrospection::from_remote::( &test.connection, MANAGER_PATH, ) .await .expect("remote"); let local = testing::InterfaceIntrospection::from_local( "com.steampowered.SteamOSManager1.Manager.xml", "com.steampowered.SteamOSManager1.Manager", ) .await .expect("local"); assert!(remote.compare(&local)); } async fn test_interface_matches(connection: &Connection) -> Result { let remote = testing::InterfaceIntrospection::from_remote::(connection, MANAGER_PATH).await?; let local = testing::InterfaceIntrospection::from_local( "com.steampowered.SteamOSManager1.xml", I::name().to_string(), ) .await?; Ok(remote.compare(&local)) } async fn test_interface_missing(connection: &Connection) -> bool { let remote = testing::InterfaceIntrospection::from_remote::(connection, MANAGER_PATH).await; remote.is_err() } #[tokio::test] async fn interface_matches_ambient_light_sensor1() { let test = start(all_config()).await.expect("start"); assert!( test_interface_matches::(&test.connection) .await .unwrap() ); } #[tokio::test] async fn interface_matches_battery_charge_limit() { let test = start(all_config()).await.expect("start"); assert!( test_interface_matches::(&test.connection) .await .unwrap() ); } #[tokio::test] async fn interface_matches_cpu_scaling1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_matches_factory_reset1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_missing_factory_reset1() { let test = start(None).await.expect("start"); assert!(test_interface_missing::(&test.connection).await); } #[tokio::test] async fn interface_matches_fan_control1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_missing_fan_control1() { let test = start(None).await.expect("start"); assert!(test_interface_missing::(&test.connection).await); } #[tokio::test] async fn interface_matches_gpu_performance_level1() { let test = start(all_config()).await.expect("start"); assert!( test_interface_matches::(&test.connection) .await .unwrap() ); } #[tokio::test] async fn interface_matches_gpu_power_profile1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_matches_tdp_limit1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_matches_hdmi_cec1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_matches_manager2() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_matches_storage1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_missing_storage1() { let test = start(None).await.expect("start"); assert!(test_interface_missing::(&test.connection).await); } #[tokio::test] async fn interface_matches_update_bios1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_missing_update_bios1() { let test = start(None).await.expect("start"); assert!(test_interface_missing::(&test.connection).await); } #[tokio::test] async fn interface_matches_update_dock1() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_missing_update_dock1() { let test = start(None).await.expect("start"); assert!(test_interface_missing::(&test.connection).await); } #[tokio::test] async fn interface_matches_wifi_power_management1() { let test = start(all_config()).await.expect("start"); assert!( test_interface_matches::(&test.connection) .await .unwrap() ); } #[tokio::test] async fn interface_matches_wifi_debug() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } #[tokio::test] async fn interface_matches_wifi_debug_dump() { let test = start(all_config()).await.expect("start"); assert!(test_interface_matches::(&test.connection) .await .unwrap()); } }