platform: Split out DeviceConfig from PlatformConfig

This commit is contained in:
Vicki Pfau 2025-06-10 19:49:41 -07:00
parent d5d2d2c9a3
commit e3ce1853e9
12 changed files with 269 additions and 179 deletions

View file

@ -24,9 +24,11 @@ install: target/release/steamos-manager target/release/steamosctl
install -Ds -m755 "target/release/steamos-manager" "$(DESTDIR)/usr/lib/steamos-manager"
install -D -m755 "target/release/steamosctl" "$(DESTDIR)/usr/bin/steamosctl"
install -D -m644 -t "$(DESTDIR)/usr/share/steamos-manager/platforms" "data/platforms/"*
install -D -m644 -t "$(DESTDIR)/usr/share/steamos-manager/devices" "data/devices/"*
install -D -m644 LICENSE "$(DESTDIR)/usr/share/licenses/steamos-manager/LICENSE"
install -m644 "data/platform.toml" "$(DESTDIR)/usr/share/steamos-manager/"
install -m644 "data/system/com.steampowered.SteamOSManager1.service" "$(DESTDIR)/usr/share/dbus-1/system-services/"
install -m644 "data/system/com.steampowered.SteamOSManager1.conf" "$(DESTDIR)/usr/share/dbus-1/system.d/"
install -m644 "data/system/steamos-manager.service" "$(DESTDIR)/usr/lib/systemd/system/"

16
data/devices/jupiter.toml Normal file
View file

@ -0,0 +1,16 @@
[tdp_limit]
method = "gpu_hwmon"
download_mode_limit = 6
[tdp_limit.range]
min = 3
max = 15
[gpu_clocks]
min = 200
max = 1600
[battery_charge_limit]
suggested_minimum_limit = 10
hwmon_name = "steamdeck_hwmon"
attribute = "max_battery_charge_level"

View file

@ -28,20 +28,3 @@ no_validate_flag = "--skip-validation"
[fan_control]
systemd = "jupiter-fan-control.service"
[tdp_limit]
method = "gpu_hwmon"
download_mode_limit = 6
[tdp_limit.range]
min = 3
max = 15
[gpu_clocks]
min = 200
max = 1600
[battery_charge_limit]
suggested_minimum_limit = 10
hwmon_name = "steamdeck_hwmon"
attribute = "max_battery_charge_level"

View file

@ -483,19 +483,11 @@ pub mod test {
sleep(Duration::from_millis(10)).await;
h.test.platform_config.replace(Some(PlatformConfig {
factory_reset: None,
update_bios: None,
update_dock: None,
storage: None,
fan_control: Some(ServiceConfig::Systemd(String::from(
"jupiter-fan-control.service",
))),
tdp_limit: None,
gpu_clocks: None,
battery_charge_limit: None,
performance_profile: None,
}));
let mut platform_config = PlatformConfig::default();
platform_config.fan_control = Some(ServiceConfig::Systemd(String::from(
"jupiter-fan-control.service",
)));
h.test.platform_config.replace(Some(platform_config));
let fan_control = FanControl::new(connection);
assert_eq!(

View file

@ -24,7 +24,7 @@ use crate::hardware::{
steam_deck_variant, FactoryResetKind, FanControl, FanControlState, SteamDeckVariant,
};
use crate::job::JobManager;
use crate::platform::platform_config;
use crate::platform::{device_config, 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,
@ -450,7 +450,7 @@ impl SteamOSManager {
}
async fn set_performance_profile(&self, profile: &str) -> fdo::Result<()> {
let config = platform_config().await.map_err(to_zbus_fdo_error)?;
let config = device_config().await.map_err(to_zbus_fdo_error)?;
let config = config
.as_ref()
.and_then(|config| config.performance_profile.as_ref())

View file

@ -24,7 +24,7 @@ use crate::hardware::{
device_type, device_variant, steam_deck_variant, DeviceType, SteamDeckVariant,
};
use crate::job::JobManagerCommand;
use crate::platform::platform_config;
use crate::platform::{device_config, 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,
@ -286,7 +286,7 @@ impl BatteryChargeLimit1 {
#[zbus(property(emits_changed_signal = "const"))]
async fn suggested_minimum_limit(&self) -> i32 {
let Ok(Some(ref config)) = platform_config().await else {
let Ok(Some(ref config)) = device_config().await else {
return BatteryChargeLimit1::DEFAULT_SUGGESTED_MINIMUM_LIMIT;
};
let Some(ref config) = config.battery_charge_limit else {
@ -552,7 +552,7 @@ impl Manager2 {
impl PerformanceProfile1 {
#[zbus(property(emits_changed_signal = "const"))]
async fn available_performance_profiles(&self) -> fdo::Result<Vec<String>> {
let config = platform_config().await.map_err(to_zbus_fdo_error)?;
let config = device_config().await.map_err(to_zbus_fdo_error)?;
let config = config
.as_ref()
.and_then(|config| config.performance_profile.as_ref())
@ -566,7 +566,7 @@ impl PerformanceProfile1 {
#[zbus(property)]
async fn performance_profile(&self) -> fdo::Result<String> {
let config = platform_config().await.map_err(to_zbus_fdo_error)?;
let config = device_config().await.map_err(to_zbus_fdo_error)?;
let config = config
.as_ref()
.and_then(|config| config.performance_profile.as_ref())
@ -614,7 +614,7 @@ impl PerformanceProfile1 {
#[zbus(property(emits_changed_signal = "const"))]
async fn suggested_default_performance_profile(&self) -> fdo::Result<String> {
let config = platform_config().await.map_err(to_zbus_fdo_error)?;
let config = device_config().await.map_err(to_zbus_fdo_error)?;
let config = config
.as_ref()
.and_then(|config| config.performance_profile.as_ref())
@ -852,12 +852,11 @@ impl WifiPowerManagement1 {
}
}
async fn create_config_interfaces(
async fn create_platform_interfaces(
proxy: &Proxy<'static>,
object_server: &ObjectServer,
connection: &Connection,
job_manager: &UnboundedSender<JobManagerCommand>,
tdp_manager: Option<UnboundedSender<TdpManagerCommand>>,
) -> Result<()> {
let Some(config) = platform_config().await? else {
return Ok(());
@ -869,10 +868,6 @@ async fn create_config_interfaces(
let fan_control = FanControl1 {
proxy: proxy.clone(),
};
let performance_profile = PerformanceProfile1 {
proxy: proxy.clone(),
tdp_limit_manager: tdp_manager.clone(),
};
let storage = Storage1 {
proxy: proxy.clone(),
job_manager: job_manager.clone(),
@ -906,41 +901,6 @@ async fn create_config_interfaces(
}
}
if let Some(manager) = tdp_manager {
let low_power_mode = LowPowerMode1 {
manager: manager.clone(),
};
if config
.tdp_limit
.as_ref()
.and_then(|config| config.download_mode_limit)
.is_some()
{
object_server.at(MANAGER_PATH, low_power_mode).await?;
}
let object_server = object_server.clone();
tokio::spawn(async move {
let (tx, rx) = oneshot::channel();
manager.send(TdpManagerCommand::IsActive(tx))?;
if rx.await?? {
let tdp_limit = TdpLimit1 { manager };
object_server.at(MANAGER_PATH, tdp_limit).await?;
}
Ok::<(), Error>(())
});
}
if let Some(config) = config.performance_profile.as_ref() {
if !get_available_platform_profiles(&config.platform_profile_name)
.await
.unwrap_or_default()
.is_empty()
{
object_server.at(MANAGER_PATH, performance_profile).await?;
}
}
if let Some(config) = config.storage.as_ref() {
match config.is_valid(true).await {
Ok(true) => {
@ -974,7 +934,58 @@ async fn create_config_interfaces(
Ok(())
}
#[allow(clippy::too_many_lines)]
async fn create_device_interfaces(
proxy: &Proxy<'static>,
object_server: &ObjectServer,
tdp_manager: Option<UnboundedSender<TdpManagerCommand>>,
) -> Result<()> {
let Some(config) = device_config().await? else {
return Ok(());
};
let performance_profile = PerformanceProfile1 {
proxy: proxy.clone(),
tdp_limit_manager: tdp_manager.clone(),
};
if let Some(manager) = tdp_manager {
let low_power_mode = LowPowerMode1 {
manager: manager.clone(),
};
if config
.tdp_limit
.as_ref()
.and_then(|config| config.download_mode_limit)
.is_some()
{
object_server.at(MANAGER_PATH, low_power_mode).await?;
}
let object_server = object_server.clone();
tokio::spawn(async move {
let (tx, rx) = oneshot::channel();
manager.send(TdpManagerCommand::IsActive(tx))?;
if rx.await?? {
let tdp_limit = TdpLimit1 { manager };
object_server.at(MANAGER_PATH, tdp_limit).await?;
}
Ok::<(), Error>(())
});
}
if let Some(config) = config.performance_profile.as_ref() {
if !get_available_platform_profiles(&config.platform_profile_name)
.await
.unwrap_or_default()
.is_empty()
{
object_server.at(MANAGER_PATH, performance_profile).await?;
}
}
Ok(())
}
pub(crate) async fn create_interfaces(
session: Connection,
system: Connection,
@ -1026,7 +1037,8 @@ pub(crate) async fn create_interfaces(
let object_server = session.object_server();
object_server.at(MANAGER_PATH, manager).await?;
create_config_interfaces(&proxy, object_server, &system, &job_manager, tdp_manager).await?;
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 {
object_server.at(MANAGER_PATH, als).await?;
@ -1088,8 +1100,9 @@ mod test {
use crate::hardware::test::fake_model;
use crate::hardware::SteamDeckVariant;
use crate::platform::{
BatteryChargeLimitConfig, FormatDeviceConfig, PerformanceProfileConfig, PlatformConfig,
RangeConfig, ResetConfig, ScriptConfig, ServiceConfig, StorageConfig, TdpLimitConfig,
BatteryChargeLimitConfig, DeviceConfig, FormatDeviceConfig, PerformanceProfileConfig,
PlatformConfig, RangeConfig, ResetConfig, ScriptConfig, ServiceConfig, StorageConfig,
TdpLimitConfig,
};
use crate::power::TdpLimitingMethod;
use crate::systemd::test::{MockManager, MockUnit};
@ -1112,7 +1125,7 @@ mod test {
rx_tdp: Option<UnboundedReceiver<TdpManagerCommand>>,
}
fn all_config() -> Option<PlatformConfig> {
fn all_platform_config() -> Option<PlatformConfig> {
Some(PlatformConfig {
factory_reset: Some(ResetConfig::default()),
update_bios: Some(ScriptConfig::default()),
@ -1121,6 +1134,11 @@ mod test {
fan_control: Some(ServiceConfig::Systemd(String::from(
"jupiter-fan-control.service",
))),
})
}
fn all_device_config() -> Option<DeviceConfig> {
Some(DeviceConfig {
tdp_limit: Some(TdpLimitConfig {
method: TdpLimitingMethod::GpuHwmon,
range: Some(RangeConfig::new(3, 15)),
@ -1140,12 +1158,15 @@ mod test {
})
}
async fn start(mut platform_config: Option<PlatformConfig>) -> Result<TestHandle> {
async fn start(
mut platform_config: Option<PlatformConfig>,
device_config: Option<DeviceConfig>,
) -> Result<TestHandle> {
let mut handle = testing::start();
let (tx_ctx, _rx_ctx) = channel::<UserContext>();
let (tx_job, rx_job) = unbounded_channel::<JobManagerCommand>();
let (tx_tdp, rx_tdp) = {
if platform_config
if device_config
.as_ref()
.and_then(|config| config.tdp_limit.as_ref())
.is_some()
@ -1162,6 +1183,7 @@ mod test {
}
handle.test.platform_config.replace(platform_config);
handle.test.device_config.replace(device_config);
let connection = handle.new_dbus().await?;
connection.request_name("org.freedesktop.systemd1").await?;
sleep(Duration::from_millis(10)).await;
@ -1212,7 +1234,7 @@ mod test {
#[tokio::test]
async fn interface_matches() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
let remote = testing::InterfaceIntrospection::from_remote::<SteamOSManager, _>(
&test.connection,
@ -1248,7 +1270,9 @@ mod test {
#[tokio::test]
async fn interface_matches_ambient_light_sensor1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(
test_interface_matches::<AmbientLightSensor1>(&test.connection)
@ -1259,7 +1283,9 @@ mod test {
#[tokio::test]
async fn interface_matches_battery_charge_limit() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(
test_interface_matches::<BatteryChargeLimit1>(&test.connection)
@ -1270,7 +1296,9 @@ mod test {
#[tokio::test]
async fn interface_matches_cpu_scaling1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<CpuScaling1>(&test.connection)
.await
@ -1279,7 +1307,9 @@ mod test {
#[tokio::test]
async fn interface_matches_factory_reset1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<FactoryReset1>(&test.connection)
.await
@ -1288,50 +1318,52 @@ mod test {
#[tokio::test]
async fn interface_missing_factory_reset1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<FactoryReset1>(&test.connection).await);
}
#[tokio::test]
async fn interface_missing_invalid_all_factory_reset1() {
let mut config = all_config().unwrap();
let mut config = all_platform_config().unwrap();
config.factory_reset.as_mut().unwrap().all = ScriptConfig {
script: PathBuf::from("oxo"),
script_args: Vec::new(),
};
let test = start(Some(config)).await.expect("start");
let test = start(Some(config), None).await.expect("start");
assert!(test_interface_missing::<FactoryReset1>(&test.connection).await);
}
#[tokio::test]
async fn interface_missing_invalid_os_factory_reset1() {
let mut config = all_config().unwrap();
let mut config = all_platform_config().unwrap();
config.factory_reset.as_mut().unwrap().os = ScriptConfig {
script: PathBuf::from("oxo"),
script_args: Vec::new(),
};
let test = start(Some(config)).await.expect("start");
let test = start(Some(config), None).await.expect("start");
assert!(test_interface_missing::<FactoryReset1>(&test.connection).await);
}
#[tokio::test]
async fn interface_missing_invalid_user_factory_reset1() {
let mut config = all_config().unwrap();
let mut config = all_platform_config().unwrap();
config.factory_reset.as_mut().unwrap().user = ScriptConfig {
script: PathBuf::from("oxo"),
script_args: Vec::new(),
};
let test = start(Some(config)).await.expect("start");
let test = start(Some(config), None).await.expect("start");
assert!(test_interface_missing::<FactoryReset1>(&test.connection).await);
}
#[tokio::test]
async fn interface_matches_fan_control1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<FanControl1>(&test.connection)
.await
@ -1340,14 +1372,16 @@ mod test {
#[tokio::test]
async fn interface_missing_fan_control1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<FanControl1>(&test.connection).await);
}
#[tokio::test]
async fn interface_matches_gpu_performance_level1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(
test_interface_matches::<GpuPerformanceLevel1>(&test.connection)
@ -1358,7 +1392,9 @@ mod test {
#[tokio::test]
async fn interface_matches_gpu_power_profile1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<GpuPowerProfile1>(&test.connection)
.await
@ -1367,7 +1403,9 @@ mod test {
#[tokio::test]
async fn interface_matches_tdp_limit1() {
let mut test = start(all_config()).await.expect("start");
let mut test = start(all_platform_config(), all_device_config())
.await
.expect("start");
let TdpManagerCommand::IsActive(reply) =
test.rx_tdp.as_mut().unwrap().recv().await.unwrap()
@ -1384,14 +1422,16 @@ mod test {
#[tokio::test]
async fn interface_missing_tdp_limit1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<TdpLimit1>(&test.connection).await);
}
#[tokio::test]
async fn interface_inactive_tdp_limit1() {
let mut test = start(all_config()).await.expect("start");
let mut test = start(all_platform_config(), all_device_config())
.await
.expect("start");
let TdpManagerCommand::IsActive(reply) =
test.rx_tdp.as_mut().unwrap().recv().await.unwrap()
@ -1406,7 +1446,9 @@ mod test {
#[tokio::test]
async fn interface_matches_hdmi_cec1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<HdmiCec1>(&test.connection)
.await
@ -1415,7 +1457,9 @@ mod test {
#[tokio::test]
async fn interface_matches_low_power_mode1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<LowPowerMode1>(&test.connection)
.await
@ -1424,14 +1468,16 @@ mod test {
#[tokio::test]
async fn interface_missing_low_power_mode1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<LowPowerMode1>(&test.connection).await);
}
#[tokio::test]
async fn interface_matches_manager2() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<Manager2>(&test.connection)
.await
@ -1440,7 +1486,9 @@ mod test {
#[tokio::test]
async fn interface_matches_performance_profile1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(
test_interface_matches::<PerformanceProfile1>(&test.connection)
@ -1451,14 +1499,16 @@ mod test {
#[tokio::test]
async fn interface_missing_performance_profile1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<PerformanceProfile1>(&test.connection).await);
}
#[tokio::test]
async fn interface_matches_storage1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<Storage1>(&test.connection)
.await
@ -1467,37 +1517,43 @@ mod test {
#[tokio::test]
async fn interface_missing_storage1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<Storage1>(&test.connection).await);
}
#[tokio::test]
async fn interface_missing_invalid_trim_storage1() {
let mut config = all_config().unwrap();
let mut config = all_platform_config().unwrap();
config.storage.as_mut().unwrap().trim_devices = ScriptConfig {
script: PathBuf::from("oxo"),
script_args: Vec::new(),
};
let test = start(Some(config)).await.expect("start");
let test = start(Some(config), all_device_config())
.await
.expect("start");
assert!(test_interface_missing::<Storage1>(&test.connection).await);
}
#[tokio::test]
async fn interface_missing_invalid_format_storage1() {
let mut config = all_config().unwrap();
let mut config = all_platform_config().unwrap();
let mut format_config = FormatDeviceConfig::default();
format_config.script = PathBuf::from("oxo");
config.storage.as_mut().unwrap().format_device = format_config;
let test = start(Some(config)).await.expect("start");
let test = start(Some(config), all_device_config())
.await
.expect("start");
assert!(test_interface_missing::<Storage1>(&test.connection).await);
}
#[tokio::test]
async fn interface_matches_update_bios1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<UpdateBios1>(&test.connection)
.await
@ -1506,26 +1562,30 @@ mod test {
#[tokio::test]
async fn interface_missing_update_bios1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<UpdateBios1>(&test.connection).await);
}
#[tokio::test]
async fn interface_missing_invalid_update_bios1() {
let mut config = all_config().unwrap();
let mut config = all_platform_config().unwrap();
config.update_bios = Some(ScriptConfig {
script: PathBuf::from("oxo"),
script_args: Vec::new(),
});
let test = start(Some(config)).await.expect("start");
let test = start(Some(config), all_device_config())
.await
.expect("start");
assert!(test_interface_missing::<UpdateBios1>(&test.connection).await);
}
#[tokio::test]
async fn interface_matches_update_dock1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<UpdateDock1>(&test.connection)
.await
@ -1534,26 +1594,30 @@ mod test {
#[tokio::test]
async fn interface_missing_update_dock1() {
let test = start(None).await.expect("start");
let test = start(None, None).await.expect("start");
assert!(test_interface_missing::<UpdateDock1>(&test.connection).await);
}
#[tokio::test]
async fn interface_missing_invalid_update_dock1() {
let mut config = all_config().unwrap();
let mut config = all_platform_config().unwrap();
config.update_dock = Some(ScriptConfig {
script: PathBuf::from("oxo"),
script_args: Vec::new(),
});
let test = start(Some(config)).await.expect("start");
let test = start(Some(config), all_device_config())
.await
.expect("start");
assert!(test_interface_missing::<UpdateDock1>(&test.connection).await);
}
#[tokio::test]
async fn interface_matches_wifi_power_management1() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(
test_interface_matches::<WifiPowerManagement1>(&test.connection)
@ -1564,7 +1628,9 @@ mod test {
#[tokio::test]
async fn interface_matches_wifi_debug() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<WifiDebug1>(&test.connection)
.await
@ -1573,7 +1639,9 @@ mod test {
#[tokio::test]
async fn interface_matches_wifi_debug_dump() {
let test = start(all_config()).await.expect("start");
let test = start(all_platform_config(), all_device_config())
.await
.expect("start");
assert!(test_interface_matches::<WifiDebugDump1>(&test.connection)
.await

View file

@ -23,11 +23,15 @@ 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 CONFIG: OnceCell<Option<PlatformConfig>> = OnceCell::const_new();
static PLATFORM_CONFIG: OnceCell<Option<PlatformConfig>> = OnceCell::const_new();
#[cfg(not(test))]
static DEVICE_CONFIG: OnceCell<Option<DeviceConfig>> = OnceCell::const_new();
#[derive(Clone, Default, Deserialize, Debug)]
#[serde(default)]
@ -37,6 +41,11 @@ pub(crate) struct PlatformConfig {
pub update_dock: Option<ScriptConfig>,
pub storage: Option<StorageConfig>,
pub fan_control: Option<ServiceConfig>,
}
#[derive(Clone, Default, Deserialize, Debug)]
#[serde(default)]
pub(crate) struct DeviceConfig {
pub tdp_limit: Option<TdpLimitConfig>,
pub gpu_clocks: Option<RangeConfig<u32>>,
pub battery_charge_limit: Option<BatteryChargeLimitConfig>,
@ -224,26 +233,12 @@ impl<T: Clone> RangeConfig<T> {
impl PlatformConfig {
#[cfg(not(test))]
async fn load() -> Result<Option<PlatformConfig>> {
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/platforms/{platform}.toml"
))
.await?;
let config = read_to_string("/usr/share/steamos-manager/platform.toml").await?;
Ok(Some(toml::from_str(config.as_ref())?))
}
#[cfg(test)]
pub(crate) fn set_test_paths(&mut self) {
use crate::path;
if let Some(ref mut factory_reset) = self.factory_reset {
if factory_reset.all.script.as_os_str().is_empty() {
factory_reset.all.script = path("exe");
@ -276,6 +271,26 @@ impl PlatformConfig {
}
}
impl DeviceConfig {
#[cfg(not(test))]
async fn load() -> Result<Option<DeviceConfig>> {
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<TdpLimitingMethod, D::Error>
where
D: Deserializer<'de>,
@ -288,7 +303,12 @@ where
#[cfg(not(test))]
pub(crate) async fn platform_config() -> Result<&'static Option<PlatformConfig>> {
CONFIG.get_or_try_init(PlatformConfig::load).await
PLATFORM_CONFIG.get_or_try_init(PlatformConfig::load).await
}
#[cfg(not(test))]
pub(crate) async fn device_config() -> Result<&'static Option<DeviceConfig>> {
DEVICE_CONFIG.get_or_try_init(DeviceConfig::load).await
}
#[cfg(test)]
@ -298,6 +318,13 @@ pub(crate) async fn platform_config() -> Result<Option<PlatformConfig>> {
Ok(config)
}
#[cfg(test)]
pub(crate) async fn device_config() -> Result<Option<DeviceConfig>> {
let test = crate::testing::current();
let config = test.device_config.borrow().clone();
Ok(config)
}
#[cfg(test)]
mod test {
use super::*;
@ -411,7 +438,7 @@ mod test {
#[tokio::test]
async fn jupiter_valid() {
let config = read_to_string("data/platforms/jupiter.toml")
let config = read_to_string("data/devices/jupiter.toml")
.await
.expect("read_to_string");
let res = toml::from_str::<PlatformConfig>(config.as_ref());

View file

@ -30,7 +30,7 @@ use zbus::Connection;
use crate::hardware::{device_type, DeviceType};
use crate::manager::root::RootManagerProxy;
use crate::manager::user::{TdpLimit1, MANAGER_PATH};
use crate::platform::platform_config;
use crate::platform::device_config;
use crate::Service;
use crate::{path, write_synced};
@ -128,7 +128,7 @@ pub(crate) trait TdpLimitManager: Send + Sync {
}
pub(crate) async fn tdp_limit_manager() -> Result<Box<dyn TdpLimitManager>> {
let config = platform_config().await?;
let config = device_config().await?;
let config = config
.as_ref()
.and_then(|config| config.tdp_limit.as_ref())
@ -340,7 +340,7 @@ pub(crate) async fn set_cpu_scaling_governor(governor: CPUScalingGovernor) -> Re
}
pub(crate) async fn get_gpu_clocks_range() -> Result<RangeInclusive<u32>> {
if let Some(range) = platform_config()
if let Some(range) = device_config()
.await?
.as_ref()
.and_then(|config| config.gpu_clocks)
@ -490,7 +490,7 @@ impl TdpLimitManager for GpuHwmonTdpLimitManager {
}
async fn get_tdp_limit_range(&self) -> Result<RangeInclusive<u32>> {
let config = platform_config().await?;
let config = device_config().await?;
let config = config
.as_ref()
.and_then(|config| config.tdp_limit.as_ref())
@ -578,7 +578,7 @@ impl TdpLimitManager for FirmwareAttributeLimitManager {
let Some(ref performance_profile) = self.performance_profile else {
return Ok(true);
};
let config = platform_config().await?;
let config = device_config().await?;
if let Some(config) = config
.as_ref()
.and_then(|config| config.performance_profile.as_ref())
@ -591,7 +591,7 @@ impl TdpLimitManager for FirmwareAttributeLimitManager {
}
pub(crate) async fn get_max_charge_level() -> Result<i32> {
let config = platform_config().await?;
let config = device_config().await?;
let config = config
.as_ref()
.and_then(|config| config.battery_charge_limit.as_ref())
@ -609,7 +609,7 @@ pub(crate) async fn get_max_charge_level() -> Result<i32> {
pub(crate) async fn set_max_charge_level(limit: i32) -> Result<()> {
ensure!((0..=100).contains(&limit), "Invalid limit");
let data = limit.to_string();
let config = platform_config().await?;
let config = device_config().await?;
let config = config
.as_ref()
.and_then(|config| config.battery_charge_limit.as_ref())
@ -654,7 +654,7 @@ impl TdpManagerService {
system: &Connection,
session: &Connection,
) -> Result<TdpManagerService> {
let config = platform_config().await?;
let config = device_config().await?;
let config = config
.as_ref()
.and_then(|config| config.tdp_limit.as_ref())
@ -858,8 +858,8 @@ pub(crate) mod test {
use crate::hardware::test::fake_model;
use crate::hardware::SteamDeckVariant;
use crate::platform::{
BatteryChargeLimitConfig, FirmwareAttributeConfig, PerformanceProfileConfig,
PlatformConfig, RangeConfig, TdpLimitConfig,
BatteryChargeLimitConfig, DeviceConfig, FirmwareAttributeConfig, PerformanceProfileConfig,
RangeConfig, TdpLimitConfig,
};
use crate::{enum_roundtrip, testing};
use anyhow::anyhow;
@ -1041,14 +1041,14 @@ CCLK_RANGE in Core0:
async fn test_gpu_hwmon_get_tdp_limit() {
let handle = testing::start();
let mut platform_config = PlatformConfig::default();
platform_config.tdp_limit = Some(TdpLimitConfig {
let mut config = DeviceConfig::default();
config.tdp_limit = Some(TdpLimitConfig {
method: TdpLimitingMethod::GpuHwmon,
range: Some(RangeConfig { min: 3, max: 15 }),
download_mode_limit: None,
firmware_attribute: None,
});
handle.test.platform_config.replace(Some(platform_config));
handle.test.device_config.replace(Some(config));
let manager = tdp_limit_manager().await.unwrap();
setup().await.expect("setup");
@ -1066,14 +1066,14 @@ CCLK_RANGE in Core0:
async fn test_gpu_hwmon_set_tdp_limit() {
let handle = testing::start();
let mut platform_config = PlatformConfig::default();
platform_config.tdp_limit = Some(TdpLimitConfig {
let mut config = DeviceConfig::default();
config.tdp_limit = Some(TdpLimitConfig {
method: TdpLimitingMethod::GpuHwmon,
range: Some(RangeConfig { min: 3, max: 15 }),
download_mode_limit: None,
firmware_attribute: None,
});
handle.test.platform_config.replace(Some(platform_config));
handle.test.device_config.replace(Some(config));
let manager = tdp_limit_manager().await.unwrap();
assert_eq!(
@ -1550,13 +1550,13 @@ CCLK_RANGE in Core0:
async fn read_max_charge_level() {
let handle = testing::start();
let mut platform_config = PlatformConfig::default();
platform_config.battery_charge_limit = Some(BatteryChargeLimitConfig {
let mut config = DeviceConfig::default();
config.battery_charge_limit = Some(BatteryChargeLimitConfig {
suggested_minimum_limit: Some(10),
hwmon_name: String::from("steamdeck_hwmon"),
attribute: String::from("max_battery_charge_level"),
});
handle.test.platform_config.replace(Some(platform_config));
handle.test.device_config.replace(Some(config));
let base = path(HWMON_PREFIX).join("hwmon6");
create_dir_all(&base).await.expect("create_dir_all");
@ -1650,14 +1650,14 @@ CCLK_RANGE in Core0:
let iface = MockTdpLimit { queue: reply_tx };
let mut platform_config = PlatformConfig::default();
platform_config.tdp_limit = Some(TdpLimitConfig {
let mut config = DeviceConfig::default();
config.tdp_limit = Some(TdpLimitConfig {
method: TdpLimitingMethod::GpuHwmon,
range: Some(RangeConfig { min: 3, max: 15 }),
download_mode_limit: NonZeroU32::new(6),
firmware_attribute: None,
});
h.test.platform_config.replace(Some(platform_config));
h.test.device_config.replace(Some(config));
let manager = tdp_limit_manager().await.unwrap();
connection
@ -1746,14 +1746,14 @@ CCLK_RANGE in Core0:
let iface = MockTdpLimit { queue: reply_tx };
let mut platform_config = PlatformConfig::default();
platform_config.tdp_limit = Some(TdpLimitConfig {
let mut config = DeviceConfig::default();
config.tdp_limit = Some(TdpLimitConfig {
method: TdpLimitingMethod::GpuHwmon,
range: Some(RangeConfig { min: 3, max: 15 }),
download_mode_limit: None,
firmware_attribute: None,
});
h.test.platform_config.replace(Some(platform_config));
h.test.device_config.replace(Some(config));
let manager = tdp_limit_manager().await.unwrap();
connection
@ -1808,12 +1808,12 @@ CCLK_RANGE in Core0:
let h = testing::start();
setup().await.expect("setup");
let mut platform_config = PlatformConfig::default();
platform_config.performance_profile = Some(PerformanceProfileConfig {
let mut config = DeviceConfig::default();
config.performance_profile = Some(PerformanceProfileConfig {
platform_profile_name: String::from("platform-profile0"),
suggested_default: String::from("custom"),
});
platform_config.tdp_limit = Some(TdpLimitConfig {
config.tdp_limit = Some(TdpLimitConfig {
method: TdpLimitingMethod::FirmwareAttribute,
range: Some(RangeConfig { min: 3, max: 15 }),
download_mode_limit: None,
@ -1822,7 +1822,7 @@ CCLK_RANGE in Core0:
performance_profile: Some(String::from("custom")),
}),
});
h.test.platform_config.replace(Some(platform_config));
h.test.device_config.replace(Some(config));
let attributes_base = path(FirmwareAttributeLimitManager::PREFIX)
.join("tdp0")
@ -1903,8 +1903,8 @@ CCLK_RANGE in Core0:
let h = testing::start();
setup().await.expect("setup");
let mut platform_config = PlatformConfig::default();
platform_config.tdp_limit = Some(TdpLimitConfig {
let mut config = DeviceConfig::default();
config.tdp_limit = Some(TdpLimitConfig {
method: TdpLimitingMethod::FirmwareAttribute,
range: Some(RangeConfig { min: 3, max: 15 }),
download_mode_limit: None,
@ -1913,7 +1913,7 @@ CCLK_RANGE in Core0:
performance_profile: None,
}),
});
h.test.platform_config.replace(Some(platform_config));
h.test.device_config.replace(Some(config));
let attributes_base = path(FirmwareAttributeLimitManager::PREFIX)
.join("tdp0")

View file

@ -23,7 +23,7 @@ use zbus::zvariant::ObjectPath;
use zbus::Address;
use zbus_xml::{Method, Node, Property, Signal};
use crate::platform::PlatformConfig;
use crate::platform::{DeviceConfig, PlatformConfig};
thread_local! {
static TEST: RefCell<Option<Rc<Test>>> = const { RefCell::new(None) };
@ -70,6 +70,7 @@ pub fn start() -> TestHandle {
mock_dbus: Cell::new(None),
dbus_address: Mutex::new(None),
platform_config: RefCell::new(None),
device_config: RefCell::new(None),
});
*lock.borrow_mut() = Some(test.clone());
TestHandle { test }
@ -103,6 +104,7 @@ pub struct Test {
pub mock_dbus: Cell<Option<MockDBus>>,
pub dbus_address: Mutex<Option<Address>>,
pub platform_config: RefCell<Option<PlatformConfig>>,
pub device_config: RefCell<Option<DeviceConfig>>,
}
pub struct TestHandle {