mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 09:23:19 +01:00
333 lines
12 KiB
Rust
333 lines
12 KiB
Rust
use std::str::FromStr;
|
|
use std::sync::Arc;
|
|
|
|
use async_trait::async_trait;
|
|
use config_traits::StdConfig;
|
|
use log::{error, info, warn};
|
|
use rog_profiles::fan_curve_set::CurveData;
|
|
use rog_profiles::{FanCurvePU, FanCurveProfiles, Profile};
|
|
use zbus::export::futures_util::lock::Mutex;
|
|
use zbus::export::futures_util::StreamExt;
|
|
use zbus::fdo::Error;
|
|
use zbus::{dbus_interface, Connection, SignalContext};
|
|
|
|
use super::controller::CtrlPlatformProfile;
|
|
use crate::error::RogError;
|
|
use crate::CtrlTask;
|
|
|
|
const MOD_NAME: &str = "ProfileZbus";
|
|
|
|
const ZBUS_PATH: &str = "/org/asuslinux/Profile";
|
|
const UNSUPPORTED_MSG: &str =
|
|
"Fan curves are not supported on this laptop or you require a patched kernel";
|
|
|
|
#[derive(Clone)]
|
|
pub struct ProfileZbus(pub Arc<Mutex<CtrlPlatformProfile>>);
|
|
|
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
|
impl ProfileZbus {
|
|
/// Fetch profile names
|
|
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
|
|
if let Ok(profiles) = Profile::get_profile_names() {
|
|
return Ok(profiles);
|
|
}
|
|
Err(Error::Failed(
|
|
"Failed to get all profile details".to_owned(),
|
|
))
|
|
}
|
|
|
|
/// Toggle to next platform_profile. Names provided by `Profiles`.
|
|
/// If fan-curves are supported will also activate a fan curve for profile.
|
|
async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
|
|
let mut ctrl = self.0.lock().await;
|
|
let next = Profile::get_next_profile(ctrl.profile_config.active_profile);
|
|
Profile::set_profile(next)
|
|
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
|
|
.ok();
|
|
ctrl.profile_config.active_profile = next;
|
|
ctrl.save_config();
|
|
|
|
Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
/// Fetch the active profile name
|
|
async fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
|
|
let mut ctrl = self.0.lock().await;
|
|
ctrl.profile_config.read();
|
|
Ok(ctrl.profile_config.active_profile)
|
|
}
|
|
|
|
/// Set this platform_profile name as active
|
|
async fn set_active_profile(
|
|
&self,
|
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
|
profile: Profile,
|
|
) {
|
|
let mut ctrl = self.0.lock().await;
|
|
// Read first just incase the user has modified the config before calling this
|
|
ctrl.profile_config.read();
|
|
Profile::set_profile(profile)
|
|
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
|
|
.ok();
|
|
ctrl.profile_config.active_profile = profile;
|
|
ctrl.write_profile_curve_to_platform()
|
|
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
|
|
.ok();
|
|
|
|
ctrl.save_config();
|
|
|
|
Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
/// Set all fan curves for a profile to enabled status. Will also activate a
|
|
/// fan curve if in the same profile mode
|
|
async fn set_fan_curves_enabled(
|
|
&mut self,
|
|
profile: Profile,
|
|
enabled: bool,
|
|
) -> zbus::fdo::Result<()> {
|
|
let mut ctrl = self.0.lock().await;
|
|
ctrl.profile_config.read();
|
|
if let Some(curves) = &mut ctrl.fan_curves {
|
|
curves
|
|
.profiles_mut()
|
|
.set_profile_curves_enabled(profile, enabled);
|
|
|
|
ctrl.write_profile_curve_to_platform()
|
|
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
|
|
.ok();
|
|
|
|
ctrl.save_config();
|
|
Ok(())
|
|
} else {
|
|
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
|
|
}
|
|
}
|
|
|
|
/// Set a single fan curve for a profile to enabled status. Will also
|
|
/// activate a fan curve if in the same profile mode
|
|
async fn set_profile_fan_curve_enabled(
|
|
&mut self,
|
|
profile: Profile,
|
|
fan: FanCurvePU,
|
|
enabled: bool,
|
|
) -> zbus::fdo::Result<()> {
|
|
let mut ctrl = self.0.lock().await;
|
|
ctrl.profile_config.read();
|
|
if let Some(curves) = &mut ctrl.fan_curves {
|
|
curves
|
|
.profiles_mut()
|
|
.set_profile_fan_curve_enabled(profile, fan, enabled);
|
|
|
|
ctrl.write_profile_curve_to_platform()
|
|
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
|
|
.ok();
|
|
|
|
ctrl.save_config();
|
|
Ok(())
|
|
} else {
|
|
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
|
|
}
|
|
}
|
|
|
|
/// Get the fan-curve data for the currently active Profile
|
|
async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<Vec<CurveData>> {
|
|
let mut ctrl = self.0.lock().await;
|
|
ctrl.profile_config.read();
|
|
if let Some(curves) = &mut ctrl.fan_curves {
|
|
let curve = curves.profiles().get_fan_curves_for(profile);
|
|
return Ok(curve.to_vec());
|
|
}
|
|
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
|
|
}
|
|
|
|
/// Set the fan curve for the specified profile.
|
|
/// Will also activate the fan curve if the user is in the same mode.
|
|
async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> {
|
|
let mut ctrl = self.0.lock().await;
|
|
ctrl.profile_config.read();
|
|
if let Some(curves) = &mut ctrl.fan_curves {
|
|
curves
|
|
.profiles_mut()
|
|
.save_fan_curve(curve, profile)
|
|
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
|
|
} else {
|
|
return Err(Error::Failed(UNSUPPORTED_MSG.to_owned()));
|
|
}
|
|
ctrl.write_profile_curve_to_platform()
|
|
.map_err(|e| warn!("{MOD_NAME}: Profile::set_profile, {}", e))
|
|
.ok();
|
|
ctrl.save_config();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Reset the stored (self) and device curve to the defaults of the
|
|
/// platform.
|
|
///
|
|
/// Each platform_profile has a different default and the defualt can be
|
|
/// read only for the currently active profile.
|
|
async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> {
|
|
let mut ctrl = self.0.lock().await;
|
|
ctrl.profile_config.read();
|
|
ctrl.set_active_curve_to_defaults()
|
|
.map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
|
|
.ok();
|
|
ctrl.save_config();
|
|
Ok(())
|
|
}
|
|
|
|
/// Reset the stored (self) and device curve to the defaults of the
|
|
/// platform.
|
|
///
|
|
/// Each platform_profile has a different default and the defualt can be
|
|
/// read only for the currently active profile.
|
|
async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> {
|
|
let mut ctrl = self.0.lock().await;
|
|
ctrl.profile_config.read();
|
|
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
|
|
|
|
Profile::set_profile(profile)
|
|
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
|
|
.ok();
|
|
ctrl.set_active_curve_to_defaults()
|
|
.map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
|
|
.ok();
|
|
|
|
Profile::set_profile(active)
|
|
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
|
|
.ok();
|
|
ctrl.save_config();
|
|
Ok(())
|
|
}
|
|
|
|
#[dbus_interface(signal)]
|
|
async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> {
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl crate::ZbusRun for ProfileZbus {
|
|
async fn add_to_server(self, server: &mut Connection) {
|
|
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl CtrlTask for ProfileZbus {
|
|
fn zbus_path() -> &'static str {
|
|
ZBUS_PATH
|
|
}
|
|
|
|
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
|
|
let ctrl = self.0.clone();
|
|
let sig_ctx = signal_ctxt.clone();
|
|
let watch = self
|
|
.0
|
|
.lock()
|
|
.await
|
|
.platform
|
|
.monitor_throttle_thermal_policy()?;
|
|
|
|
tokio::spawn(async move {
|
|
let mut buffer = [0; 32];
|
|
if let Ok(stream) = watch.into_event_stream(&mut buffer) {
|
|
stream
|
|
.for_each(|_| async {
|
|
let mut lock = ctrl.lock().await;
|
|
if let Ok(profile) =
|
|
lock.platform.get_throttle_thermal_policy().map_err(|e| {
|
|
error!("{MOD_NAME}: get_throttle_thermal_policy error: {e}");
|
|
})
|
|
{
|
|
let new_profile = Profile::from_throttle_thermal_policy(profile);
|
|
if new_profile != lock.profile_config.active_profile {
|
|
info!("{MOD_NAME}: platform_profile changed to {new_profile}");
|
|
lock.profile_config.active_profile = new_profile;
|
|
lock.write_profile_curve_to_platform().unwrap();
|
|
lock.save_config();
|
|
Profile::set_profile(lock.profile_config.active_profile)
|
|
.map_err(|e| {
|
|
error!("Profile::set_profile() error: {e}");
|
|
})
|
|
.ok();
|
|
|
|
Self::notify_profile(&sig_ctx, lock.profile_config.active_profile)
|
|
.await
|
|
.ok();
|
|
}
|
|
}
|
|
})
|
|
.await;
|
|
}
|
|
});
|
|
|
|
let ctrl = self.0.clone();
|
|
let watch = self.0.lock().await.platform.monitor_platform_profile()?;
|
|
|
|
tokio::spawn(async move {
|
|
let mut buffer = [0; 32];
|
|
if let Ok(stream) = watch.into_event_stream(&mut buffer) {
|
|
stream
|
|
.for_each(|_| async {
|
|
let mut lock = ctrl.lock().await;
|
|
if let Ok(profile) = lock.platform.get_platform_profile().map_err(|e| {
|
|
error!("get_platform_profile error: {e}");
|
|
}) {
|
|
if let Ok(new_profile) = Profile::from_str(&profile).map_err(|e| {
|
|
error!("Profile::from_str(&profile) error: {e}");
|
|
}) {
|
|
if new_profile != lock.profile_config.active_profile {
|
|
info!("{MOD_NAME}: platform_profile changed to {new_profile}");
|
|
lock.profile_config.active_profile = new_profile;
|
|
lock.write_profile_curve_to_platform().unwrap();
|
|
lock.save_config();
|
|
Profile::set_profile(lock.profile_config.active_profile)
|
|
.map_err(|e| {
|
|
error!("Profile::set_profile() error: {e}");
|
|
})
|
|
.ok();
|
|
|
|
Self::notify_profile(
|
|
&signal_ctxt,
|
|
lock.profile_config.active_profile,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.await;
|
|
}
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl crate::Reloadable for ProfileZbus {
|
|
/// Fetch the active profile and use that to set all related components up
|
|
async fn reload(&mut self) -> Result<(), RogError> {
|
|
let mut ctrl = self.0.lock().await;
|
|
let active = ctrl.profile_config.active_profile;
|
|
if let Some(curves) = &mut ctrl.fan_curves {
|
|
if let Ok(mut device) = FanCurveProfiles::get_device() {
|
|
// There is a possibility that the curve was default zeroed, so this call
|
|
// initialises the data from system read and we need to save it
|
|
// after
|
|
curves
|
|
.profiles_mut()
|
|
.write_profile_curve_to_platform(active, &mut device)?;
|
|
ctrl.profile_config.write();
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|