mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-05 14:10:34 -04:00
manager/user: Add BatteryChargeLimit1 interface (#12)
This commit is contained in:
parent
02ed562bd0
commit
91890e5948
10 changed files with 292 additions and 36 deletions
|
@ -29,6 +29,31 @@
|
|||
|
||||
</interface>
|
||||
|
||||
<!--
|
||||
com.steampowered.SteamOSManager1.BatteryChargeLimit1
|
||||
@short_description: Optional interface for battery charging limit
|
||||
properties.
|
||||
-->
|
||||
<interface name="com.steampowered.SteamOSManager1.BatteryChargeLimit1">
|
||||
|
||||
<!--
|
||||
MaxChargeLevel:
|
||||
|
||||
The maximum allowable percentage for battery charging. If setting to
|
||||
-1, this will reset to the default.
|
||||
-->
|
||||
<property name="MaxChargeLevel" type="i" access="readwrite"/>
|
||||
|
||||
<!--
|
||||
SuggestedMinimumLimit:
|
||||
|
||||
The suggested minimum value for a frontend to allow setting the max
|
||||
charge level.
|
||||
-->
|
||||
<property name="SuggestedMinimumLimit" type="i" access="read"/>
|
||||
|
||||
</interface>
|
||||
|
||||
<!--
|
||||
com.steampowered.SteamOSManager1.CpuScaling1
|
||||
@short_description: Optional interface for adjusting CPU scaling.
|
||||
|
|
|
@ -5,7 +5,7 @@ script_args = ["factory-reset", "--reset-all"]
|
|||
[factory_reset.os]
|
||||
script = "/usr/bin/steamos-reset-tool"
|
||||
script_args = ["factory-reset", "--reset-os"]
|
||||
|
||||
|
||||
[factory_reset.user]
|
||||
script = "/usr/bin/steamos-reset-tool"
|
||||
script_args = ["factory-reset", "--reset-user-data"]
|
||||
|
@ -36,3 +36,8 @@ max = 15
|
|||
[gpu_clocks]
|
||||
min = 200
|
||||
max = 1600
|
||||
|
||||
[battery_charge_limit]
|
||||
suggested_minimum_limit = 10
|
||||
hwmon_name = "steamdeck_hwmon"
|
||||
attribute = "max_battery_charge_level"
|
||||
|
|
|
@ -14,10 +14,10 @@ use steamos_manager::cec::HdmiCecState;
|
|||
use steamos_manager::hardware::{FactoryResetKind, FanControlState};
|
||||
use steamos_manager::power::{CPUScalingGovernor, GPUPerformanceLevel, GPUPowerProfile};
|
||||
use steamos_manager::proxy::{
|
||||
AmbientLightSensor1Proxy, CpuScaling1Proxy, FactoryReset1Proxy, FanControl1Proxy,
|
||||
GpuPerformanceLevel1Proxy, GpuPowerProfile1Proxy, HdmiCec1Proxy, Manager2Proxy, Storage1Proxy,
|
||||
TdpLimit1Proxy, UpdateBios1Proxy, UpdateDock1Proxy, WifiDebug1Proxy, WifiDebugDump1Proxy,
|
||||
WifiPowerManagement1Proxy,
|
||||
AmbientLightSensor1Proxy, BatteryChargeLimit1Proxy, CpuScaling1Proxy, FactoryReset1Proxy,
|
||||
FanControl1Proxy, GpuPerformanceLevel1Proxy, GpuPowerProfile1Proxy, HdmiCec1Proxy,
|
||||
Manager2Proxy, Storage1Proxy, TdpLimit1Proxy, UpdateBios1Proxy, UpdateDock1Proxy,
|
||||
WifiDebug1Proxy, WifiDebugDump1Proxy, WifiPowerManagement1Proxy,
|
||||
};
|
||||
use steamos_manager::wifi::{WifiBackend, WifiDebugMode, WifiPowerManagement};
|
||||
use zbus::fdo::{IntrospectableProxy, PropertiesProxy};
|
||||
|
@ -172,6 +172,18 @@ enum Commands {
|
|||
/// Valid kind(s) are `user`, `os`, `all`
|
||||
kind: FactoryResetKind,
|
||||
},
|
||||
|
||||
/// Get the maximum charge level set for the battery
|
||||
GetMaxChargeLevel,
|
||||
|
||||
/// Set the maximum charge level set for the battery
|
||||
SetMaxChargeLevel {
|
||||
/// Valid levels are 1 - 100, or -1 to reset to default
|
||||
level: i32,
|
||||
},
|
||||
|
||||
/// Get the recommended minimum for a charge level limit
|
||||
SuggestedMinimumChargeLimit,
|
||||
}
|
||||
|
||||
async fn get_all_properties(conn: &Connection) -> Result<()> {
|
||||
|
@ -437,6 +449,20 @@ async fn main() -> Result<()> {
|
|||
let proxy = Storage1Proxy::new(&conn).await?;
|
||||
let _ = proxy.trim_devices().await?;
|
||||
}
|
||||
Commands::GetMaxChargeLevel => {
|
||||
let proxy = BatteryChargeLimit1Proxy::new(&conn).await?;
|
||||
let level = proxy.max_charge_level().await?;
|
||||
println!("Max charge level: {level}");
|
||||
}
|
||||
Commands::SetMaxChargeLevel { level } => {
|
||||
let proxy = BatteryChargeLimit1Proxy::new(&conn).await?;
|
||||
proxy.set_max_charge_level(*level).await?;
|
||||
}
|
||||
Commands::SuggestedMinimumChargeLimit => {
|
||||
let proxy = BatteryChargeLimit1Proxy::new(&conn).await?;
|
||||
let limit = proxy.suggested_minimum_limit().await?;
|
||||
println!("Suggested minimum charge limit: {limit}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -336,6 +336,7 @@ pub mod test {
|
|||
))),
|
||||
tdp_limit: None,
|
||||
gpu_clocks: None,
|
||||
battery_charge_limit: None,
|
||||
}));
|
||||
|
||||
let fan_control = FanControl::new(connection);
|
||||
|
|
|
@ -25,7 +25,7 @@ use crate::job::JobManager;
|
|||
use crate::platform::platform_config;
|
||||
use crate::power::{
|
||||
set_cpu_scaling_governor, set_gpu_clocks, set_gpu_performance_level, set_gpu_power_profile,
|
||||
set_tdp_limit, CPUScalingGovernor, GPUPerformanceLevel, GPUPowerProfile,
|
||||
set_max_charge_level, set_tdp_limit, CPUScalingGovernor, GPUPerformanceLevel, GPUPowerProfile,
|
||||
};
|
||||
use crate::process::{run_script, script_output};
|
||||
use crate::wifi::{
|
||||
|
@ -421,6 +421,12 @@ impl SteamOSManager {
|
|||
.map_err(to_zbus_fdo_error)
|
||||
}
|
||||
|
||||
async fn set_max_charge_level(&self, level: i32) -> fdo::Result<()> {
|
||||
set_max_charge_level(if level == -1 { 0 } else { level })
|
||||
.await
|
||||
.map_err(to_zbus_fdo_error)
|
||||
}
|
||||
|
||||
/// A version property.
|
||||
#[zbus(property(emits_changed_signal = "const"))]
|
||||
async fn version(&self) -> u32 {
|
||||
|
|
|
@ -27,8 +27,8 @@ 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_tdp_limit,
|
||||
get_tdp_limit_range,
|
||||
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};
|
||||
use crate::API_VERSION;
|
||||
|
@ -103,6 +103,10 @@ struct AmbientLightSensor1 {
|
|||
proxy: Proxy<'static>,
|
||||
}
|
||||
|
||||
struct BatteryChargeLimit1 {
|
||||
proxy: Proxy<'static>,
|
||||
}
|
||||
|
||||
struct CpuScaling1 {
|
||||
proxy: Proxy<'static>,
|
||||
}
|
||||
|
@ -226,6 +230,41 @@ impl AmbientLightSensor1 {
|
|||
}
|
||||
}
|
||||
|
||||
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<i32> {
|
||||
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"))]
|
||||
|
@ -541,6 +580,9 @@ pub(crate) async fn create_interfaces(
|
|||
let als = AmbientLightSensor1 {
|
||||
proxy: proxy.clone(),
|
||||
};
|
||||
let battery_charge_limit = BatteryChargeLimit1 {
|
||||
proxy: proxy.clone(),
|
||||
};
|
||||
let cpu_scaling = CpuScaling1 {
|
||||
proxy: proxy.clone(),
|
||||
};
|
||||
|
@ -597,6 +639,10 @@ pub(crate) async fn create_interfaces(
|
|||
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 config
|
||||
|
@ -683,7 +729,8 @@ mod test {
|
|||
use crate::hardware::test::fake_model;
|
||||
use crate::hardware::HardwareVariant;
|
||||
use crate::platform::{
|
||||
PlatformConfig, RangeConfig, ResetConfig, ScriptConfig, ServiceConfig, StorageConfig,
|
||||
BatteryChargeLimitConfig, PlatformConfig, RangeConfig, ResetConfig, ScriptConfig,
|
||||
ServiceConfig, StorageConfig,
|
||||
};
|
||||
use crate::systemd::test::{MockManager, MockUnit};
|
||||
use crate::{power, testing};
|
||||
|
@ -710,6 +757,11 @@ mod test {
|
|||
))),
|
||||
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"),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -801,6 +853,17 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interface_matches_battery_charge_limit() {
|
||||
let test = start(all_config()).await.expect("start");
|
||||
|
||||
assert!(
|
||||
test_interface_matches::<BatteryChargeLimit1>(&test.connection)
|
||||
.await
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interface_matches_cpu_scaling1() {
|
||||
let test = start(all_config()).await.expect("start");
|
||||
|
|
|
@ -28,6 +28,7 @@ pub(crate) struct PlatformConfig {
|
|||
pub fan_control: Option<ServiceConfig>,
|
||||
pub tdp_limit: Option<RangeConfig<u32>>,
|
||||
pub gpu_clocks: Option<RangeConfig<u32>>,
|
||||
pub battery_charge_limit: Option<BatteryChargeLimitConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Debug)]
|
||||
|
@ -70,6 +71,13 @@ pub(crate) struct StorageConfig {
|
|||
pub format_device: FormatDeviceConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Debug)]
|
||||
pub(crate) struct BatteryChargeLimitConfig {
|
||||
pub suggested_minimum_limit: Option<i32>,
|
||||
pub hwmon_name: String,
|
||||
pub attribute: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize, Debug)]
|
||||
pub(crate) struct FormatDeviceConfig {
|
||||
pub script: PathBuf,
|
||||
|
|
143
src/power.rs
143
src/power.rs
|
@ -20,8 +20,10 @@ use crate::hardware::is_deck;
|
|||
use crate::platform::platform_config;
|
||||
use crate::{path, write_synced};
|
||||
|
||||
const GPU_HWMON_PREFIX: &str = "/sys/class/hwmon";
|
||||
const HWMON_PREFIX: &str = "/sys/class/hwmon";
|
||||
|
||||
const GPU_HWMON_NAME: &str = "amdgpu";
|
||||
|
||||
const CPU_PREFIX: &str = "/sys/devices/system/cpu/cpufreq";
|
||||
|
||||
const CPU0_NAME: &str = "policy0";
|
||||
|
@ -85,14 +87,14 @@ pub enum CPUScalingGovernor {
|
|||
|
||||
async fn read_gpu_sysfs_contents<S: AsRef<Path>>(suffix: S) -> Result<String> {
|
||||
// Read a given suffix for the GPU
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
fs::read_to_string(base.join(suffix.as_ref()))
|
||||
.await
|
||||
.map_err(|message| anyhow!("Error opening sysfs file for reading {message}"))
|
||||
}
|
||||
|
||||
async fn write_gpu_sysfs_contents<S: AsRef<Path>>(suffix: S, data: &[u8]) -> Result<()> {
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
write_synced(base.join(suffix), data)
|
||||
.await
|
||||
.inspect_err(|message| error!("Error writing to sysfs file: {message}"))
|
||||
|
@ -195,7 +197,7 @@ pub(crate) async fn set_gpu_power_profile(value: GPUPowerProfile) -> Result<()>
|
|||
}
|
||||
|
||||
pub(crate) async fn get_available_gpu_performance_levels() -> Result<Vec<GPUPerformanceLevel>> {
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
if try_exists(base.join(GPU_PERFORMANCE_LEVEL_SUFFIX)).await? {
|
||||
Ok(vec![
|
||||
GPUPerformanceLevel::Auto,
|
||||
|
@ -288,7 +290,7 @@ pub(crate) async fn get_gpu_clocks_range() -> Result<(u32, u32)> {
|
|||
pub(crate) async fn set_gpu_clocks(clocks: u32) -> Result<()> {
|
||||
// Set GPU clocks to given value valid
|
||||
// Only used when GPU Performance Level is manual, but write whenever called.
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
let mut myfile = File::create(base.join(GPU_CLOCKS_SUFFIX))
|
||||
.await
|
||||
.inspect_err(|message| error!("Error opening sysfs file for writing: {message}"))?;
|
||||
|
@ -317,7 +319,7 @@ pub(crate) async fn set_gpu_clocks(clocks: u32) -> Result<()> {
|
|||
}
|
||||
|
||||
pub(crate) async fn get_gpu_clocks() -> Result<u32> {
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
let clocks_file = File::open(base.join(GPU_CLOCKS_SUFFIX)).await?;
|
||||
let mut reader = BufReader::new(clocks_file);
|
||||
loop {
|
||||
|
@ -343,8 +345,8 @@ pub(crate) async fn get_gpu_clocks() -> Result<u32> {
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
async fn find_hwmon() -> Result<PathBuf> {
|
||||
let mut dir = fs::read_dir(path(GPU_HWMON_PREFIX)).await?;
|
||||
async fn find_hwmon(hwmon: &str) -> Result<PathBuf> {
|
||||
let mut dir = fs::read_dir(path(HWMON_PREFIX)).await?;
|
||||
loop {
|
||||
let base = match dir.next_entry().await? {
|
||||
Some(entry) => entry.path(),
|
||||
|
@ -355,14 +357,14 @@ async fn find_hwmon() -> Result<PathBuf> {
|
|||
.await?
|
||||
.trim()
|
||||
.to_string();
|
||||
if name == GPU_HWMON_NAME {
|
||||
if name == hwmon {
|
||||
return Ok(base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_tdp_limit() -> Result<u32> {
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
let power1cap = fs::read_to_string(base.join(TDP_LIMIT1)).await?;
|
||||
let power1cap: u32 = power1cap.trim_end().parse()?;
|
||||
Ok(power1cap / 1_000_000)
|
||||
|
@ -374,7 +376,7 @@ pub(crate) async fn set_tdp_limit(limit: u32) -> Result<()> {
|
|||
ensure!((3..=15).contains(&limit), "Invalid limit");
|
||||
let data = format!("{limit}000000");
|
||||
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
write_synced(base.join(TDP_LIMIT1), data.as_bytes())
|
||||
.await
|
||||
.inspect_err(|message| {
|
||||
|
@ -400,19 +402,51 @@ pub(crate) async fn get_tdp_limit_range() -> Result<(u32, u32)> {
|
|||
Ok((range.min, range.max))
|
||||
}
|
||||
|
||||
pub(crate) async fn get_max_charge_level() -> Result<i32> {
|
||||
let config = platform_config()
|
||||
.await?
|
||||
.as_ref()
|
||||
.and_then(|config| config.battery_charge_limit.clone())
|
||||
.ok_or(anyhow!("No battery charge limit configured"))?;
|
||||
let base = find_hwmon(config.hwmon_name.as_str()).await?;
|
||||
|
||||
fs::read_to_string(base.join(config.attribute.as_str()))
|
||||
.await
|
||||
.map_err(|message| anyhow!("Error reading sysfs: {message}"))?
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(|e| anyhow!("Error parsing value: {e}"))
|
||||
}
|
||||
|
||||
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?
|
||||
.as_ref()
|
||||
.and_then(|config| config.battery_charge_limit.clone())
|
||||
.ok_or(anyhow!("No battery charge limit configured"))?;
|
||||
let base = find_hwmon(config.hwmon_name.as_str()).await?;
|
||||
|
||||
write_synced(base.join(config.attribute.as_str()), data.as_bytes())
|
||||
.await
|
||||
.inspect_err(|message| error!("Error writing to sysfs file: {message}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::hardware::test::fake_model;
|
||||
use crate::hardware::HardwareVariant;
|
||||
use crate::platform::{BatteryChargeLimitConfig, PlatformConfig};
|
||||
use crate::{enum_roundtrip, testing};
|
||||
use anyhow::anyhow;
|
||||
use tokio::fs::{create_dir_all, read_to_string, remove_dir, write};
|
||||
|
||||
pub async fn setup() -> Result<()> {
|
||||
// Use hwmon5 just as a test. We needed a subfolder of GPU_HWMON_PREFIX
|
||||
// Use hwmon5 just as a test. We needed a subfolder of HWMON_PREFIX
|
||||
// and this is as good as any.
|
||||
let base = path(GPU_HWMON_PREFIX).join("hwmon5");
|
||||
let base = path(HWMON_PREFIX).join("hwmon5");
|
||||
let filename = base.join(GPU_PERFORMANCE_LEVEL_SUFFIX);
|
||||
// Creates hwmon path, including device subpath
|
||||
create_dir_all(filename.parent().unwrap()).await?;
|
||||
|
@ -423,7 +457,7 @@ pub(crate) mod test {
|
|||
|
||||
pub async fn create_nodes() -> Result<()> {
|
||||
setup().await?;
|
||||
let base = find_hwmon().await?;
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await?;
|
||||
|
||||
let filename = base.join(GPU_PERFORMANCE_LEVEL_SUFFIX);
|
||||
write(filename.as_path(), "auto\n").await?;
|
||||
|
@ -441,11 +475,18 @@ pub(crate) mod test {
|
|||
let filename = base.join(TDP_LIMIT1);
|
||||
write(filename.as_path(), "15000000\n").await?;
|
||||
|
||||
let base = path(HWMON_PREFIX).join("hwmon6");
|
||||
create_dir_all(&base).await?;
|
||||
|
||||
write(base.join("name"), "steamdeck_hwmon\n").await?;
|
||||
|
||||
write(base.join("max_battery_charge_level"), "10\n").await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_clocks(mhz: u32) {
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_CLOCKS_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -467,7 +508,7 @@ CCLK_RANGE in Core0:
|
|||
}
|
||||
|
||||
pub async fn read_clocks() -> Result<String, std::io::Error> {
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
read_to_string(base.join(GPU_CLOCKS_SUFFIX)).await
|
||||
}
|
||||
|
||||
|
@ -480,7 +521,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_PERFORMANCE_LEVEL_SUFFIX);
|
||||
assert!(get_gpu_performance_level().await.is_err());
|
||||
|
||||
|
@ -525,7 +566,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_PERFORMANCE_LEVEL_SUFFIX);
|
||||
|
||||
set_gpu_performance_level(GPUPerformanceLevel::Auto)
|
||||
|
@ -570,7 +611,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let hwmon = path(GPU_HWMON_PREFIX);
|
||||
let hwmon = path(HWMON_PREFIX);
|
||||
|
||||
assert!(get_tdp_limit().await.is_err());
|
||||
|
||||
|
@ -594,7 +635,7 @@ CCLK_RANGE in Core0:
|
|||
);
|
||||
assert!(set_tdp_limit(10).await.is_err());
|
||||
|
||||
let hwmon = path(GPU_HWMON_PREFIX);
|
||||
let hwmon = path(HWMON_PREFIX);
|
||||
assert_eq!(
|
||||
set_tdp_limit(10).await.unwrap_err().to_string(),
|
||||
anyhow!("No such file or directory (os error 2)").to_string()
|
||||
|
@ -645,7 +686,7 @@ CCLK_RANGE in Core0:
|
|||
assert!(get_gpu_clocks().await.is_err());
|
||||
setup().await.expect("setup");
|
||||
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_CLOCKS_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -678,7 +719,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_CLOCK_LEVELS_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -758,7 +799,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_POWER_PROFILE_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -814,7 +855,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_POWER_PROFILE_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -872,7 +913,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_POWER_PROFILE_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -910,7 +951,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_POWER_PROFILE_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -942,7 +983,7 @@ CCLK_RANGE in Core0:
|
|||
let _h = testing::start();
|
||||
|
||||
setup().await.expect("setup");
|
||||
let base = find_hwmon().await.unwrap();
|
||||
let base = find_hwmon(GPU_HWMON_NAME).await.unwrap();
|
||||
let filename = base.join(GPU_POWER_PROFILE_SUFFIX);
|
||||
create_dir_all(filename.parent().unwrap())
|
||||
.await
|
||||
|
@ -1053,4 +1094,52 @@ CCLK_RANGE in Core0:
|
|||
|
||||
assert!(get_cpu_scaling_governor().await.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_max_charge_level() {
|
||||
let handle = testing::start();
|
||||
|
||||
let platform_config = Some(PlatformConfig {
|
||||
factory_reset: None,
|
||||
update_bios: None,
|
||||
update_dock: None,
|
||||
storage: None,
|
||||
fan_control: None,
|
||||
tdp_limit: None,
|
||||
gpu_clocks: None,
|
||||
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(platform_config);
|
||||
|
||||
let base = path(HWMON_PREFIX).join("hwmon6");
|
||||
create_dir_all(&base).await.expect("create_dir_all");
|
||||
|
||||
write(base.join("name"), "steamdeck_hwmon\n")
|
||||
.await
|
||||
.expect("write");
|
||||
|
||||
assert_eq!(
|
||||
find_hwmon("steamdeck_hwmon").await.unwrap(),
|
||||
path(HWMON_PREFIX).join("hwmon6")
|
||||
);
|
||||
|
||||
write(base.join("max_battery_charge_level"), "10\n")
|
||||
.await
|
||||
.expect("write");
|
||||
|
||||
assert_eq!(get_max_charge_level().await.unwrap(), 10);
|
||||
|
||||
set_max_charge_level(99).await.expect("set");
|
||||
assert_eq!(get_max_charge_level().await.unwrap(), 99);
|
||||
|
||||
set_max_charge_level(0).await.expect("set");
|
||||
assert_eq!(get_max_charge_level().await.unwrap(), 0);
|
||||
|
||||
assert!(set_max_charge_level(101).await.is_err());
|
||||
assert!(set_max_charge_level(-1).await.is_err());
|
||||
}
|
||||
}
|
||||
|
|
31
src/proxy/battery_charge_limit1.rs
Normal file
31
src/proxy/battery_charge_limit1.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
//! # D-Bus interface proxy for: `com.steampowered.SteamOSManager1.BatteryChargeLimit1`
|
||||
//!
|
||||
//! This code was generated by `zbus-xmlgen` `5.0.1` from D-Bus introspection data.
|
||||
//! Source: `com.steampowered.SteamOSManager1.xml`.
|
||||
//!
|
||||
//! You may prefer to adapt it, instead of using it verbatim.
|
||||
//!
|
||||
//! More information can be found in the [Writing a client proxy] section of the zbus
|
||||
//! documentation.
|
||||
//!
|
||||
//!
|
||||
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
|
||||
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
|
||||
use zbus::proxy;
|
||||
#[proxy(
|
||||
interface = "com.steampowered.SteamOSManager1.BatteryChargeLimit1",
|
||||
default_service = "com.steampowered.SteamOSManager1",
|
||||
default_path = "/com/steampowered/SteamOSManager1",
|
||||
assume_defaults = true
|
||||
)]
|
||||
pub trait BatteryChargeLimit1 {
|
||||
/// MaxChargeLevel property
|
||||
#[zbus(property)]
|
||||
fn max_charge_level(&self) -> zbus::Result<i32>;
|
||||
#[zbus(property)]
|
||||
fn set_max_charge_level(&self, value: i32) -> zbus::Result<()>;
|
||||
|
||||
/// SuggestedMinimumLimit property
|
||||
#[zbus(property)]
|
||||
fn suggested_minimum_limit(&self) -> zbus::Result<i32>;
|
||||
}
|
|
@ -15,6 +15,7 @@ pub use crate::proxy::manager::ManagerProxy;
|
|||
|
||||
// Optional interfaces
|
||||
mod ambient_light_sensor1;
|
||||
mod battery_charge_limit1;
|
||||
mod cpu_scaling1;
|
||||
mod factory_reset1;
|
||||
mod fan_control1;
|
||||
|
@ -30,6 +31,7 @@ mod wifi_debug1;
|
|||
mod wifi_debug_dump1;
|
||||
mod wifi_power_management1;
|
||||
pub use crate::proxy::ambient_light_sensor1::AmbientLightSensor1Proxy;
|
||||
pub use crate::proxy::battery_charge_limit1::BatteryChargeLimit1Proxy;
|
||||
pub use crate::proxy::cpu_scaling1::CpuScaling1Proxy;
|
||||
pub use crate::proxy::factory_reset1::FactoryReset1Proxy;
|
||||
pub use crate::proxy::fan_control1::FanControl1Proxy;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue