manager: Set up signal relay service, use it for MaxChargeLevelChanged

This commit is contained in:
Vicki Pfau 2025-06-12 22:18:42 -07:00
parent 4f691bf053
commit 0b3c66e924
4 changed files with 101 additions and 33 deletions

View file

@ -21,7 +21,7 @@ use zbus::fdo::ObjectManager;
use crate::daemon::{channel, Daemon, DaemonCommand, DaemonContext};
use crate::job::{JobManager, JobManagerService};
use crate::manager::user::create_interfaces;
use crate::manager::user::{create_interfaces, SignalRelayService};
use crate::path;
use crate::power::TdpManagerService;
use crate::udev::UdevMonitor;
@ -114,6 +114,7 @@ async fn create_connections(
Connection,
JobManagerService,
Result<TdpManagerService>,
SignalRelayService,
)> {
let system = Connection::system().await?;
let connection = Builder::session()?
@ -133,9 +134,16 @@ async fn create_connections(
None
};
create_interfaces(connection.clone(), system.clone(), channel, jm_tx, tdp_tx).await?;
let signal_relay_service =
create_interfaces(connection.clone(), system.clone(), channel, jm_tx, tdp_tx).await?;
Ok((connection, system, jm_service, tdp_service))
Ok((
connection,
system,
jm_service,
tdp_service,
signal_relay_service,
))
}
pub async fn daemon() -> Result<()> {
@ -146,20 +154,22 @@ pub async fn daemon() -> Result<()> {
let subscriber = Registry::default().with(stdout_log);
let (tx, rx) = channel::<UserContext>();
let (session, system, mirror_service, tdp_service) = match create_connections(tx).await {
Ok(c) => c,
Err(e) => {
let _guard = tracing::subscriber::set_default(subscriber);
error!("Error connecting to DBus: {}", e);
bail!(e);
}
};
let (session, system, mirror_service, tdp_service, signal_relay_service) =
match create_connections(tx).await {
Ok(c) => c,
Err(e) => {
let _guard = tracing::subscriber::set_default(subscriber);
error!("Error connecting to DBus: {}", e);
bail!(e);
}
};
let context = UserContext {
session: session.clone(),
};
let mut daemon = Daemon::new(subscriber, system, rx).await?;
daemon.add_service(signal_relay_service);
daemon.add_service(mirror_service);
if let Ok(tdp_service) = tdp_service {
daemon.add_service(tdp_service);

View file

@ -10,6 +10,7 @@ use anyhow::{anyhow, Result};
use std::collections::HashMap;
use std::ffi::OsStr;
use tokio::fs::File;
use tokio::spawn;
use tokio::sync::mpsc::Sender;
use tokio::sync::oneshot;
use tracing::{error, info};
@ -29,7 +30,7 @@ use crate::platform::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,
GPUPerformanceLevel, GPUPowerProfile, TdpLimitManager,
GPUPerformanceLevel, GPUPowerProfile, SysfsWritten, TdpLimitManager,
};
use crate::process::{run_script, script_output};
use crate::wifi::{
@ -444,10 +445,34 @@ 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 })
#[zbus(signal)]
async fn max_charge_level_changed(signal_emitter: &SignalEmitter<'_>) -> zbus::Result<()>;
async fn set_max_charge_level(
&self,
level: i32,
#[zbus(connection)] connection: &Connection,
) -> fdo::Result<()> {
let written = set_max_charge_level(if level == -1 { 0 } else { level })
.await
.map_err(to_zbus_fdo_error)
.map_err(to_zbus_fdo_error)?;
let connection = connection.clone();
spawn(async move {
match written.await {
Ok(SysfsWritten::Written(res)) => {
if let Ok(interface) = connection
.object_server()
.interface::<_, Self>("/com/steampowered/SteamOSManager1")
.await
{
interface.max_charge_level_changed().await?;
}
res
}
_ => Ok(()),
}
});
Ok(())
}
async fn set_performance_profile(&self, profile: &str) -> fdo::Result<()> {

View file

@ -10,6 +10,7 @@ use anyhow::{Error, Result};
use std::collections::HashMap;
use tokio::sync::mpsc::{Sender, UnboundedSender};
use tokio::sync::oneshot;
use tokio_stream::StreamExt;
use tracing::error;
use zbus::object_server::SignalEmitter;
use zbus::proxy::{Builder, CacheProperties};
@ -35,7 +36,7 @@ use crate::screenreader::{OrcaManager, ScreenReaderMode};
use crate::wifi::{
get_wifi_backend, get_wifi_power_management_state, list_wifi_interfaces, WifiBackend,
};
use crate::API_VERSION;
use crate::{Service, API_VERSION};
pub(crate) const MANAGER_PATH: &str = "/com/steampowered/SteamOSManager1";
@ -185,6 +186,11 @@ struct WifiPowerManagement1 {
proxy: Proxy<'static>,
}
pub(crate) struct SignalRelayService {
proxy: Proxy<'static>,
session: Connection,
}
impl SteamOSManager {
pub async fn new(
system_conn: Connection,
@ -275,13 +281,8 @@ impl BatteryChargeLimit1 {
}
#[zbus(property)]
async fn set_max_charge_level(
&self,
limit: i32,
#[zbus(signal_emitter)] ctx: SignalEmitter<'_>,
) -> zbus::Result<()> {
let _: () = self.proxy.call("SetMaxChargeLevel", &(limit)).await?;
self.max_charge_level_changed(&ctx).await
async fn set_max_charge_level(&self, limit: i32) -> zbus::Result<()> {
self.proxy.call("SetMaxChargeLevel", &(limit)).await
}
#[zbus(property(emits_changed_signal = "const"))]
@ -874,6 +875,33 @@ impl WifiPowerManagement1 {
}
}
impl Service for SignalRelayService {
const NAME: &'static str = "signal-relay";
async fn run(&mut self) -> Result<()> {
let Ok(battery_charge_limit) = self
.session
.object_server()
.interface::<_, BatteryChargeLimit1>(MANAGER_PATH)
.await
else {
return Ok(());
};
let ctx = battery_charge_limit.signal_emitter();
let mut max_charge_level_changed =
self.proxy.receive_signal("MaxChargeLevelChanged").await?;
loop {
max_charge_level_changed.next().await;
battery_charge_limit
.get()
.await
.max_charge_level_changed(ctx)
.await?;
}
}
}
async fn create_platform_interfaces(
proxy: &Proxy<'static>,
object_server: &ObjectServer,
@ -1014,7 +1042,7 @@ pub(crate) async fn create_interfaces(
daemon: Sender<Command>,
job_manager: UnboundedSender<JobManagerCommand>,
tdp_manager: Option<UnboundedSender<TdpManagerCommand>>,
) -> Result<()> {
) -> Result<SignalRelayService> {
let proxy = Builder::<Proxy>::new(&system)
.destination("com.steampowered.SteamOSManager1")?
.path("/com/steampowered/SteamOSManager1")?
@ -1111,7 +1139,7 @@ pub(crate) async fn create_interfaces(
.await?;
}
Ok(())
Ok(SignalRelayService { session, proxy })
}
#[cfg(test)]

View file

@ -681,7 +681,7 @@ pub(crate) async fn get_max_charge_level() -> Result<i32> {
.map_err(|e| anyhow!("Error parsing value: {e}"))
}
pub(crate) async fn set_max_charge_level(limit: i32) -> Result<()> {
pub(crate) async fn set_max_charge_level(limit: i32) -> Result<oneshot::Receiver<SysfsWritten>> {
ensure!((0..=100).contains(&limit), "Invalid limit");
let data = limit.to_string();
let config = device_config().await?;
@ -691,9 +691,14 @@ pub(crate) async fn set_max_charge_level(limit: i32) -> Result<()> {
.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}"))
Ok(SYSFS_WRITER
.get()
.ok_or(anyhow!("sysfs writer not running"))?
.send(
base.join(config.attribute.clone()),
data.as_bytes().to_owned(),
)
.await)
}
pub(crate) async fn get_available_platform_profiles(name: &str) -> Result<Vec<String>> {
@ -1650,11 +1655,11 @@ CCLK_RANGE in Core0:
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);
write(base.join("max_battery_charge_level"), "99\n")
.await
.expect("write");
set_max_charge_level(0).await.expect("set");
assert_eq!(get_max_charge_level().await.unwrap(), 0);
assert_eq!(get_max_charge_level().await.unwrap(), 99);
assert!(set_max_charge_level(101).await.is_err());
assert!(set_max_charge_level(-1).await.is_err());