feat: change limits dynamically

This commit is contained in:
Denis Benato
2025-10-11 20:51:57 +02:00
parent 180d63620b
commit 0dba22529c
5 changed files with 143 additions and 35 deletions

View File

@@ -55,6 +55,25 @@ impl AsusArmouryAttribute {
String::from(self.attr.name()) String::from(self.attr.name())
} }
fn resolve_i32_value(refreshed: Option<i32>, cached: &AttrValue) -> i32 {
refreshed
.or_else(|| match cached {
AttrValue::Integer(i) => Some(*i),
_ => None,
})
.unwrap_or(-1)
}
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name());
let signal = SignalEmitter::new(connection, path)?;
self.min_value_changed(&signal).await?;
self.max_value_changed(&signal).await?;
self.scalar_increment_changed(&signal).await?;
self.current_value_changed(&signal).await?;
Ok(())
}
pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> { pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name()); let path = dbus_path_for_attr(self.attr.name());
connection connection
@@ -117,6 +136,35 @@ impl AsusArmouryAttribute {
} }
} }
#[derive(Clone, Default)]
pub struct ArmouryAttributeRegistry {
attrs: Vec<AsusArmouryAttribute>,
}
impl ArmouryAttributeRegistry {
pub fn push(&mut self, attr: AsusArmouryAttribute) {
self.attrs.push(attr);
}
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
let mut last_err: Option<RogError> = None;
for attr in &self.attrs {
if let Err(e) = attr.emit_limits(connection).await {
error!(
"Failed to emit updated limits for attribute '{}': {e:?}",
attr.attribute_name()
);
last_err = Some(e);
}
}
if let Some(err) = last_err {
Err(err)
} else {
Ok(())
}
}
}
impl crate::Reloadable for AsusArmouryAttribute { impl crate::Reloadable for AsusArmouryAttribute {
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
info!("Reloading {}", self.attr.name()); info!("Reloading {}", self.attr.name());
@@ -256,26 +304,20 @@ impl AsusArmouryAttribute {
#[zbus(property)] #[zbus(property)]
async fn min_value(&self) -> i32 { async fn min_value(&self) -> i32 {
match self.attr.min_value() { Self::resolve_i32_value(self.attr.refresh_min_value(), self.attr.min_value())
AttrValue::Integer(i) => *i,
_ => -1,
}
} }
#[zbus(property)] #[zbus(property)]
async fn max_value(&self) -> i32 { async fn max_value(&self) -> i32 {
match self.attr.max_value() { Self::resolve_i32_value(self.attr.refresh_max_value(), self.attr.max_value())
AttrValue::Integer(i) => *i,
_ => -1,
}
} }
#[zbus(property)] #[zbus(property)]
async fn scalar_increment(&self) -> i32 { async fn scalar_increment(&self) -> i32 {
match self.attr.scalar_increment() { Self::resolve_i32_value(
AttrValue::Integer(i) => *i, self.attr.refresh_scalar_increment(),
_ => -1, self.attr.scalar_increment(),
} )
} }
#[zbus(property)] #[zbus(property)]
@@ -393,7 +435,8 @@ pub async fn start_attributes_zbus(
power: AsusPower, power: AsusPower,
attributes: FirmwareAttributes, attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
) -> Result<(), RogError> { ) -> Result<ArmouryAttributeRegistry, RogError> {
let mut registry = ArmouryAttributeRegistry::default();
for attr in attributes.attributes() { for attr in attributes.attributes() {
let mut attr = AsusArmouryAttribute::new( let mut attr = AsusArmouryAttribute::new(
attr.clone(), attr.clone(),
@@ -402,6 +445,8 @@ pub async fn start_attributes_zbus(
config.clone(), config.clone(),
); );
let registry_attr = attr.clone();
if let Err(e) = attr.reload().await { if let Err(e) = attr.reload().await {
error!( error!(
"Skipping attribute '{}' due to reload error: {e:?}", "Skipping attribute '{}' due to reload error: {e:?}",
@@ -430,9 +475,12 @@ pub async fn start_attributes_zbus(
if let Err(e) = attr.move_to_zbus(conn).await { if let Err(e) = attr.move_to_zbus(conn).await {
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}"); error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
continue;
} }
registry.push(registry_attr);
} }
Ok(()) Ok(registry)
} }
pub async fn set_config_or_default( pub async fn set_config_or_default(

View File

@@ -13,7 +13,7 @@ use zbus::fdo::Error as FdoErr;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection}; use zbus::{interface, Connection};
use crate::asus_armoury::set_config_or_default; use crate::asus_armoury::{set_config_or_default, ArmouryAttributeRegistry};
use crate::config::Config; use crate::config::Config;
use crate::error::RogError; use crate::error::RogError;
use crate::{task_watch_item, CtrlTask, ReloadAndNotify}; use crate::{task_watch_item, CtrlTask, ReloadAndNotify};
@@ -46,6 +46,8 @@ pub struct CtrlPlatform {
attributes: FirmwareAttributes, attributes: FirmwareAttributes,
cpu_control: Option<CPUControl>, cpu_control: Option<CPUControl>,
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
connection: Connection,
armoury_registry: ArmouryAttributeRegistry,
} }
impl CtrlPlatform { impl CtrlPlatform {
@@ -56,6 +58,8 @@ impl CtrlPlatform {
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
config_path: &Path, config_path: &Path,
signal_context: SignalEmitter<'static>, signal_context: SignalEmitter<'static>,
connection: Connection,
armoury_registry: ArmouryAttributeRegistry,
) -> Result<Self, RogError> { ) -> Result<Self, RogError> {
let config1 = config.clone(); let config1 = config.clone();
let config_path = config_path.to_owned(); let config_path = config_path.to_owned();
@@ -68,6 +72,8 @@ impl CtrlPlatform {
cpu_control: CPUControl::new() cpu_control: CPUControl::new()
.map_err(|e| error!("Couldn't get CPU control sysfs: {e}")) .map_err(|e| error!("Couldn't get CPU control sysfs: {e}"))
.ok(), .ok(),
connection,
armoury_registry,
}; };
let mut inotify_self = ret_self.clone(); let mut inotify_self = ret_self.clone();
@@ -729,6 +735,31 @@ impl CtrlTask for CtrlPlatform {
} }
if !sleeping { if !sleeping {
platform1.run_ac_or_bat_cmd(power_plugged > 0).await; platform1.run_ac_or_bat_cmd(power_plugged > 0).await;
if let Ok(profile) =
platform1.platform.get_platform_profile().map(|p| p.into())
{
let attrs = FirmwareAttributes::new();
{
let mut cfg = platform1.config.lock().await;
set_config_or_default(
&attrs,
&mut *cfg,
power_plugged > 0,
profile,
)
.await;
}
if let Err(e) = platform1
.armoury_registry
.emit_limits(&platform1.connection)
.await
{
error!(
"Failed to emit armoury updates after power change: \
{e:?}"
);
}
}
} }
platform1.config.lock().await.last_power_plugged = power_plugged; platform1.config.lock().await.last_power_plugged = power_plugged;
} }
@@ -789,13 +820,17 @@ impl CtrlTask for CtrlPlatform {
{ {
// TODO: manage this better, shouldn't need to create every time // TODO: manage this better, shouldn't need to create every time
let attrs = FirmwareAttributes::new(); let attrs = FirmwareAttributes::new();
set_config_or_default( {
&attrs, let mut cfg = platform3.config.lock().await;
&mut *platform3.config.lock().await, set_config_or_default(&attrs, &mut *cfg, power_plugged, profile).await;
power_plugged, }
profile, if let Err(e) = platform3
) .armoury_registry
.await; .emit_limits(&platform3.connection)
.await
{
error!("Failed to emit armoury updates after AC/DC toggle: {e:?}");
}
platform3 platform3
.enable_ppt_group_changed(&signal_ctxt_copy) .enable_ppt_group_changed(&signal_ctxt_copy)
.await .await
@@ -852,6 +887,9 @@ impl CtrlTask for CtrlPlatform {
profile, profile,
) )
.await; .await;
if let Err(e) = ctrl.armoury_registry.emit_limits(&ctrl.connection).await {
error!("Failed to emit armoury updates after profile change: {e:?}");
}
} }
} }
} }

View File

@@ -3,7 +3,7 @@ use std::error::Error;
use std::sync::Arc; use std::sync::Arc;
use ::zbus::Connection; use ::zbus::Connection;
use asusd::asus_armoury::start_attributes_zbus; use asusd::asus_armoury::{start_attributes_zbus, ArmouryAttributeRegistry};
use asusd::aura_manager::DeviceManager; use asusd::aura_manager::DeviceManager;
use asusd::config::Config; use asusd::config::Config;
use asusd::ctrl_backlight::CtrlBacklight; use asusd::ctrl_backlight::CtrlBacklight;
@@ -74,7 +74,7 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let platform = RogPlatform::new()?; // TODO: maybe needs async mutex? let platform = RogPlatform::new()?; // TODO: maybe needs async mutex?
let power = AsusPower::new()?; // TODO: maybe needs async mutex? let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new(); let attributes = FirmwareAttributes::new();
if let Err(e) = start_attributes_zbus( let armoury_registry = match start_attributes_zbus(
&server, &server,
platform.clone(), platform.clone(),
power.clone(), power.clone(),
@@ -83,8 +83,12 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
) )
.await .await
{ {
error!("Failed to initialize firmware attributes over zbus: {e:?}"); Ok(registry) => registry,
} Err(e) => {
error!("Failed to initialize firmware attributes over zbus: {e:?}");
ArmouryAttributeRegistry::default()
}
};
match CtrlFanCurveZbus::new() { match CtrlFanCurveZbus::new() {
Ok(ctrl) => { Ok(ctrl) => {
@@ -113,6 +117,8 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
config.clone(), config.clone(),
&cfg_path, &cfg_path,
CtrlPlatform::signal_context(&server)?, CtrlPlatform::signal_context(&server)?,
server.clone(),
armoury_registry,
) { ) {
Ok(ctrl) => { Ok(ctrl) => {
let sig_ctx = CtrlPlatform::signal_context(&server)?; let sig_ctx = CtrlPlatform::signal_context(&server)?;

View File

@@ -302,7 +302,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_pl1_spl.current != -1 || SystemPageData.ppt_pl2_sppt.current != -1 || SystemPageData.ppt_pl3_fppt.current != -1 || SystemPageData.ppt_fppt.current != -1 || SystemPageData.ppt_apu_sppt.current != -1 || SystemPageData.nv_temp_target.current != -1 || SystemPageData.nv_dynamic_boost.current != -1: HorizontalLayout { if (SystemPageData.ppt_pl1_spl.max > 0 && SystemPageData.ppt_pl1_spl.current != -1) || (SystemPageData.ppt_pl2_sppt.max > 0 && SystemPageData.ppt_pl2_sppt.current != -1) || (SystemPageData.ppt_pl3_fppt.max > 0 && SystemPageData.ppt_pl3_fppt.current != -1) || (SystemPageData.ppt_fppt.max > 0 && SystemPageData.ppt_fppt.current != -1) || (SystemPageData.ppt_apu_sppt.max > 0 && SystemPageData.ppt_apu_sppt.current != -1) || (SystemPageData.nv_temp_target.max > 0 && SystemPageData.nv_temp_target.current != -1) || (SystemPageData.nv_dynamic_boost.max > 0 && SystemPageData.nv_dynamic_boost.current != -1): HorizontalLayout {
padding-right: 10px; padding-right: 10px;
padding-left: 10px; padding-left: 10px;
alignment: LayoutAlignment.space-between; alignment: LayoutAlignment.space-between;
@@ -331,7 +331,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_pl1_spl.current != -1: SystemSlider { if SystemPageData.ppt_pl1_spl.max > 0 && SystemPageData.ppt_pl1_spl.current != -1: SystemSlider {
text: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit"); text: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit");
title: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit"); title: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit");
help_text: @tr("ppt_pl1_spl_help" => "Long-term CPU power limit that affects sustained workload performance. Higher values may increase heat and power consumption."); help_text: @tr("ppt_pl1_spl_help" => "Long-term CPU power limit that affects sustained workload performance. Higher values may increase heat and power consumption.");
@@ -349,7 +349,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_pl2_sppt.current != -1: SystemSlider { if SystemPageData.ppt_pl2_sppt.max > 0 && SystemPageData.ppt_pl2_sppt.current != -1: SystemSlider {
text: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit"); text: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit");
title: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit"); title: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit");
help_text: @tr("ppt_pl2_sppt_help" => "Short-term CPU power limit for boost periods. Controls maximum power during brief high-performance bursts."); help_text: @tr("ppt_pl2_sppt_help" => "Short-term CPU power limit for boost periods. Controls maximum power during brief high-performance bursts.");
@@ -367,7 +367,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_pl3_fppt.current != -1: SystemSlider { if SystemPageData.ppt_pl3_fppt.max > 0 && SystemPageData.ppt_pl3_fppt.current != -1: SystemSlider {
text: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit"); text: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit");
title: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit"); title: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit");
help_text: @tr("ppt_pl3_fppt_help" => "Ultra-short duration power limit for instantaneous CPU bursts. Affects responsiveness during sudden workload spikes."); help_text: @tr("ppt_pl3_fppt_help" => "Ultra-short duration power limit for instantaneous CPU bursts. Affects responsiveness during sudden workload spikes.");
@@ -384,7 +384,7 @@ export component PageSystem inherits Rectangle {
SystemPageData.cb_ppt_pl3_fppt(Math.round(value)); SystemPageData.cb_ppt_pl3_fppt(Math.round(value));
} }
} }
if SystemPageData.ppt_fppt.current != -1: SystemSlider { if SystemPageData.ppt_fppt.max > 0 && SystemPageData.ppt_fppt.current != -1: SystemSlider {
text: @tr("ppt_fppt" => "Fast Package Power Limit"); text: @tr("ppt_fppt" => "Fast Package Power Limit");
title: @tr("ppt_fppt" => "Fast Package Power Limit"); title: @tr("ppt_fppt" => "Fast Package Power Limit");
help_text: @tr("ppt_fppt_help" => "Ultra-short duration power limit for system package. Controls maximum power during millisecond-scale load spikes."); help_text: @tr("ppt_fppt_help" => "Ultra-short duration power limit for system package. Controls maximum power during millisecond-scale load spikes.");
@@ -402,7 +402,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_apu_sppt.current != -1: SystemSlider { if SystemPageData.ppt_apu_sppt.max > 0 && SystemPageData.ppt_apu_sppt.current != -1: SystemSlider {
text: @tr("ppt_apu_sppt" => "APU Sustained Power Limit"); text: @tr("ppt_apu_sppt" => "APU Sustained Power Limit");
title: @tr("ppt_apu_sppt" => "APU Sustained Power Limit"); title: @tr("ppt_apu_sppt" => "APU Sustained Power Limit");
help_text: @tr("ppt_apu_sppt_help" => "Long-term power limit for integrated graphics and CPU combined. Affects sustained performance of APU-based workloads."); help_text: @tr("ppt_apu_sppt_help" => "Long-term power limit for integrated graphics and CPU combined. Affects sustained performance of APU-based workloads.");
@@ -420,7 +420,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_platform_sppt.current != -1: SystemSlider { if SystemPageData.ppt_platform_sppt.max > 0 && SystemPageData.ppt_platform_sppt.current != -1: SystemSlider {
text: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit"); text: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit");
title: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit"); title: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit");
help_text: @tr("ppt_platform_sppt_help" => "Overall system power limit for sustained operations. Controls total platform power consumption over extended periods."); help_text: @tr("ppt_platform_sppt_help" => "Overall system power limit for sustained operations. Controls total platform power consumption over extended periods.");
@@ -438,7 +438,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.nv_dynamic_boost.current != -1: SystemSlider { if SystemPageData.nv_dynamic_boost.max > 0 && SystemPageData.nv_dynamic_boost.current != -1: SystemSlider {
text: @tr("nv_dynamic_boost" => "GPU Power Boost"); text: @tr("nv_dynamic_boost" => "GPU Power Boost");
title: @tr("nv_dynamic_boost" => "GPU Power Boost"); title: @tr("nv_dynamic_boost" => "GPU Power Boost");
help_text: @tr("nv_dynamic_boost_help" => "Additional power allocation for GPU dynamic boost. Higher values increase GPU performance but generate more heat."); help_text: @tr("nv_dynamic_boost_help" => "Additional power allocation for GPU dynamic boost. Higher values increase GPU performance but generate more heat.");
@@ -456,7 +456,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.nv_temp_target.current != -1: SystemSlider { if SystemPageData.nv_temp_target.max > 0 && SystemPageData.nv_temp_target.current != -1: SystemSlider {
text: @tr("nv_temp_target" => "GPU Temperature Limit"); text: @tr("nv_temp_target" => "GPU Temperature Limit");
title: @tr("nv_temp_target" => "GPU Temperature Limit"); title: @tr("nv_temp_target" => "GPU Temperature Limit");
help_text: @tr("nv_temp_target_help" => "Maximum GPU temperature threshold in Celsius. GPU will throttle to maintain temperature below this limit."); help_text: @tr("nv_temp_target_help" => "Maximum GPU temperature threshold in Celsius. GPU will throttle to maintain temperature below this limit.");

View File

@@ -124,6 +124,22 @@ impl Attribute {
&self.scalar_increment &self.scalar_increment
} }
fn read_attr_i32(&self, name: &str) -> Option<i32> {
read_i32(&self.base_path.join(name)).ok()
}
pub fn refresh_min_value(&self) -> Option<i32> {
self.read_attr_i32("min_value")
}
pub fn refresh_max_value(&self) -> Option<i32> {
self.read_attr_i32("max_value")
}
pub fn refresh_scalar_increment(&self) -> Option<i32> {
self.read_attr_i32("scalar_increment")
}
/// Read all the immutable values to struct data. These should *never* /// Read all the immutable values to struct data. These should *never*
/// change, if they do then it is possibly a driver issue - although this is /// change, if they do then it is possibly a driver issue - although this is
/// subject to `firmware_attributes` class changes in kernel. /// subject to `firmware_attributes` class changes in kernel.