diff --git a/Cargo.lock b/Cargo.lock index e26db738..78679b2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2739,6 +2739,7 @@ dependencies = [ "inotify", "log", "rog_aura", + "rog_profiles", "rusb", "serde", "serde_derive", @@ -2753,6 +2754,7 @@ name = "rog_profiles" version = "4.7.0-RC3" dependencies = [ "cargo-husky", + "log", "serde", "serde_derive", "typeshare", diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 25b55594..751e9eaf 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -660,7 +660,7 @@ fn handle_fan_curve( supported: &PlatformProfileFunctions, cmd: &FanCurveCommand, ) -> Result<(), Box> { - if !supported.fan_curves { + if supported.fans.is_empty() { println!("Fan-curves not supported by either this kernel or by the laptop."); println!("This requires kernel 5.17 or the fan curve patch listed in the readme."); return Err(ProfileError::NotSupported.into()); @@ -686,8 +686,9 @@ fn handle_fan_curve( } if cmd.get_enabled { - let res = dbus.proxies().profile().enabled_fan_profiles()?; - println!("{:?}", res); + // TODO: + // let res = dbus.proxies().profile().enabled_fan_profiles()?; + // println!("{:?}", res); } if cmd.default { diff --git a/asusctl/src/profiles_cli.rs b/asusctl/src/profiles_cli.rs index 1a1e2983..e9ba3d2c 100644 --- a/asusctl/src/profiles_cli.rs +++ b/asusctl/src/profiles_cli.rs @@ -34,12 +34,12 @@ pub struct FanCurveCommand { pub mod_profile: Option, #[options( meta = "", - help = "enable or disable fan curve. `mod-profile` required" + help = "enable or disable fan curve. `mod_profile` required" )] pub enabled: Option, #[options( meta = "", - help = "select fan to modify. `mod-profile` required" + help = "select fan to modify. `mod_profile` required" )] pub fan: Option, diff --git a/asusd/src/ctrl_profiles/config.rs b/asusd/src/ctrl_profiles/config.rs index a66f8a75..2e2ce56f 100644 --- a/asusd/src/ctrl_profiles/config.rs +++ b/asusd/src/ctrl_profiles/config.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use config_traits::{StdConfig, StdConfigLoad}; -use rog_profiles::fan_curve_set::FanCurveSet; +use rog_profiles::fan_curve_set::CurveData; use rog_profiles::Profile; use serde_derive::{Deserialize, Serialize}; @@ -36,9 +36,9 @@ impl StdConfigLoad for ProfileConfig {} #[derive(Deserialize, Serialize, Debug, Default)] pub struct FanCurveConfig { - pub balanced: FanCurveSet, - pub performance: FanCurveSet, - pub quiet: FanCurveSet, + pub balanced: Vec, + pub performance: Vec, + pub quiet: Vec, } impl StdConfig for FanCurveConfig { diff --git a/asusd/src/ctrl_profiles/controller.rs b/asusd/src/ctrl_profiles/controller.rs index 381d6adb..ebf58439 100644 --- a/asusd/src/ctrl_profiles/controller.rs +++ b/asusd/src/ctrl_profiles/controller.rs @@ -56,13 +56,9 @@ impl GetSupported for CtrlPlatformProfile { ); } - let res = FanCurveProfiles::is_supported(); - let mut fan_curve_supported = res.is_err(); - if let Ok(r) = res { - fan_curve_supported = r; - }; + let res = FanCurveProfiles::supported_fans(); - if !fan_curve_supported { + if res.is_err() { info!( "fan curves kernel interface not found, your laptop does not support this, or the \ interface is missing." @@ -71,7 +67,7 @@ impl GetSupported for CtrlPlatformProfile { PlatformProfileFunctions { platform_profile: Profile::is_platform_profile_supported(), - fan_curves: fan_curve_supported, + fans: res.unwrap_or_default(), } } } @@ -113,10 +109,10 @@ impl CtrlPlatformProfile { let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); if let Some(curves) = controller.fan_curves.as_ref() { - info!( - "{MOD_NAME}: {active:?}: {}", - String::from(curves.profiles().get_fan_curves_for(active)) - ); + info!("{MOD_NAME}: {active:?}:"); + for curve in curves.profiles().get_fan_curves_for(active) { + info!("{}", String::from(curve)); + } } } if let Some(curves) = controller.fan_curves.as_ref() { diff --git a/asusd/src/ctrl_profiles/trait_impls.rs b/asusd/src/ctrl_profiles/trait_impls.rs index 9f26fd72..c9e95a1a 100644 --- a/asusd/src/ctrl_profiles/trait_impls.rs +++ b/asusd/src/ctrl_profiles/trait_impls.rs @@ -4,7 +4,7 @@ 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, FanCurveSet}; +use rog_profiles::fan_curve_set::CurveData; use rog_profiles::{FanCurveProfiles, Profile}; use zbus::export::futures_util::lock::Mutex; use zbus::export::futures_util::StreamExt; @@ -83,16 +83,6 @@ impl ProfileZbus { .ok(); } - /// Get a list of profiles that have fan-curves enabled. - async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result> { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_curves { - return Ok(curves.profiles().get_enabled_curve_profiles()); - } - Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) - } - /// Set a profile fan curve enabled status. Will also activate a fan curve /// if in the same profile mode async fn set_fan_curve_enabled( @@ -119,12 +109,12 @@ impl ProfileZbus { } /// Get the fan-curve data for the currently active Profile - async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result { + async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result> { 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.clone()); + return Ok(curve.to_vec()); } Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) } diff --git a/rog-control-center/src/pages/fan_curve_page.rs b/rog-control-center/src/pages/fan_curve_page.rs index e9725575..b56d839d 100644 --- a/rog-control-center/src/pages/fan_curve_page.rs +++ b/rog-control-center/src/pages/fan_curve_page.rs @@ -1,6 +1,6 @@ -use egui::Ui; +use egui::{RichText, Ui}; use rog_platform::supported::SupportedFunctions; -use rog_profiles::Profile; +use rog_profiles::{FanCurvePU, Profile}; use crate::system_state::{FanCurvesState, ProfilesState, SystemState}; use crate::widgets::fan_graphs; @@ -12,10 +12,6 @@ impl RogApp { egui::CentralPanel::default().show(ctx, |ui| { ui.heading("Custom fan curves"); - ui.label( - "A fan curve is only active when the related profile is active and the curve is \ - enabled", - ); Self::fan_curve( supported, &mut states.profiles, @@ -44,35 +40,35 @@ impl RogApp { ui: &mut Ui, ) { ui.separator(); - ui.label("Enabled fan-curves"); let mut changed = false; ui.horizontal(|ui| { - let mut item = |p: Profile, curves: &mut FanCurvesState, mut checked: bool| { + ui.label("Current profile: "); + ui.label(RichText::new(format!("{}", profiles.current)).strong()); + }); + + ui.horizontal(|ui| { + ui.label("Enabled fan-curves: "); + let mut fan_curve_enable = |profile: Profile, fan: FanCurvePU, mut checked: bool| { if ui - .add(egui::Checkbox::new(&mut checked, format!("{:?}", p))) + .add(egui::Checkbox::new(&mut checked, format!("{:?}", fan))) .changed() { dbus.proxies() .profile() - .set_fan_curve_enabled(p, checked) + .set_fan_curve_enabled(profile, checked) .map_err(|err| { *do_error = Some(err.to_string()); }) .ok(); - - if !checked { - curves.enabled.remove(&p); - } else { - curves.enabled.insert(p); - } changed = true; } }; - profiles.list.sort(); - for f in &profiles.list { - item(*f, curves, curves.enabled.contains(f)); + if let Some(curves) = curves.curves.get_mut(&profiles.current) { + for curve in curves.iter_mut() { + fan_curve_enable(profiles.current, curve.fan, curve.enabled); + } } }); diff --git a/rog-control-center/src/system_state.rs b/rog-control-center/src/system_state.rs index c1ab548d..99cb5efa 100644 --- a/rog-control-center/src/system_state.rs +++ b/rog-control-center/src/system_state.rs @@ -12,7 +12,7 @@ use rog_aura::usb::AuraPowerDev; use rog_aura::{AuraEffect, AuraModeNum}; use rog_platform::platform::GpuMode; use rog_platform::supported::SupportedFunctions; -use rog_profiles::fan_curve_set::FanCurveSet; +use rog_profiles::fan_curve_set::CurveData; use rog_profiles::{FanCurvePU, Profile}; use supergfxctl::pci_device::{GfxMode, GfxPower}; #[cfg(not(feature = "mocking"))] @@ -96,8 +96,8 @@ impl ProfilesState { pub struct FanCurvesState { pub show_curve: Profile, pub show_graph: FanCurvePU, - pub enabled: HashSet, - pub curves: BTreeMap, + pub curves: BTreeMap>, + pub available_fans: HashSet, pub drag_delta: Vec2, } @@ -108,34 +108,24 @@ impl FanCurvesState { } else { vec![Profile::Balanced, Profile::Quiet, Profile::Performance] }; - let enabled = if supported.platform_profile.fan_curves { - dbus.proxies() - .profile() - .enabled_fan_profiles()? - .iter() - .cloned() - .collect::>() - } else { - HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance]) - }; - let mut curves: BTreeMap = BTreeMap::new(); + let mut curves: BTreeMap> = BTreeMap::new(); for p in &profiles { - if supported.platform_profile.fan_curves { + if !supported.platform_profile.fans.is_empty() { if let Ok(curve) = dbus.proxies().profile().fan_curve_data(*p) { curves.insert(*p, curve); } } else { - let mut curve = FanCurveSet::default(); - curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250]; - curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100]; - curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250]; - curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100]; - curves.insert(*p, curve); + curves.insert(*p, Vec::default()); } } - let show_curve = if supported.platform_profile.fan_curves { + let mut available_fans = HashSet::new(); + for fan in supported.platform_profile.fans.iter() { + available_fans.insert(*fan); + } + + let show_curve = if !supported.platform_profile.fans.is_empty() { dbus.proxies().profile().active_profile()? } else { Profile::Balanced @@ -144,8 +134,8 @@ impl FanCurvesState { Ok(Self { show_curve, show_graph: FanCurvePU::CPU, - enabled, curves, + available_fans, drag_delta: Vec2::default(), }) } diff --git a/rog-control-center/src/widgets/fan_graph.rs b/rog-control-center/src/widgets/fan_graph.rs index a51711cc..6300e2d2 100644 --- a/rog-control-center/src/widgets/fan_graph.rs +++ b/rog-control-center/src/widgets/fan_graph.rs @@ -1,6 +1,7 @@ use egui::plot::Points; use egui::Ui; use rog_platform::supported::SupportedFunctions; +use rog_profiles::fan_curve_set::CurveData; use rog_profiles::{FanCurvePU, Profile}; use crate::system_state::FanCurvesState; @@ -15,20 +16,36 @@ pub fn fan_graphs( ) { ui.separator(); - let mut item = |p: Profile, ui: &mut Ui| { + let mut item = |profile: Profile, ui: &mut Ui| { ui.group(|ui| { - ui.selectable_value(&mut curves.show_curve, p, format!("{p:?}")); - ui.add_enabled_ui(curves.show_curve == p, |ui| { - ui.selectable_value( - &mut curves.show_graph, - FanCurvePU::CPU, - format!("{:?}", FanCurvePU::CPU), - ); - ui.selectable_value( - &mut curves.show_graph, - FanCurvePU::GPU, - format!("{:?}", FanCurvePU::GPU), - ); + if ui + .selectable_value(&mut curves.show_curve, profile, format!("{profile:?}")) + .clicked() + { + dbus.proxies().profile().set_active_profile(profile).ok(); + } + ui.add_enabled_ui(curves.show_curve == profile, |ui| { + if curves.available_fans.contains(&FanCurvePU::CPU) { + ui.selectable_value( + &mut curves.show_graph, + FanCurvePU::CPU, + format!("{:?}", FanCurvePU::CPU), + ); + } + if curves.available_fans.contains(&FanCurvePU::GPU) { + ui.selectable_value( + &mut curves.show_graph, + FanCurvePU::GPU, + format!("{:?}", FanCurvePU::GPU), + ); + } + if curves.available_fans.contains(&FanCurvePU::MID) { + ui.selectable_value( + &mut curves.show_graph, + FanCurvePU::MID, + format!("{:?}", FanCurvePU::MID), + ); + } }); }); }; @@ -43,11 +60,13 @@ pub fn fan_graphs( use egui::plot::{Line, Plot}; - let data = if curves.show_graph == FanCurvePU::CPU { - &mut curve.cpu - } else { - &mut curve.gpu - }; + let mut data = &mut CurveData::default(); + for c in curve { + if c.fan == curves.show_graph { + data = c; + break; + } + } let mut points: Vec<[f64; 2]> = data .temp diff --git a/rog-control-center/src/widgets/side_panel.rs b/rog-control-center/src/widgets/side_panel.rs index 619bb43a..631de7ad 100644 --- a/rog-control-center/src/widgets/side_panel.rs +++ b/rog-control-center/src/widgets/side_panel.rs @@ -18,7 +18,7 @@ impl RogApp { *page = Page::System; } - if self.supported.platform_profile.fan_curves { + if !self.supported.platform_profile.fans.is_empty() { ui.separator(); if ui .selectable_value(page, Page::FanCurves, "Fan Curves") diff --git a/rog-dbus/src/zbus_profile.rs b/rog-dbus/src/zbus_profile.rs index d438f4e5..7ffffdb7 100644 --- a/rog-dbus/src/zbus_profile.rs +++ b/rog-dbus/src/zbus_profile.rs @@ -20,7 +20,7 @@ //! //! …consequently `zbus-xmlgen` did not generate code for the above interfaces. -use rog_profiles::fan_curve_set::{CurveData, FanCurveSet}; +use rog_profiles::fan_curve_set::CurveData; use rog_profiles::Profile; use zbus::dbus_proxy; @@ -30,14 +30,11 @@ use zbus::dbus_proxy; )] trait Profile { /// Get the fan-curve data for the currently active Profile - fn fan_curve_data(&self, profile: Profile) -> zbus::Result; + fn fan_curve_data(&self, profile: Profile) -> zbus::Result>; /// Fetch the active profile name fn active_profile(&self) -> zbus::Result; - /// Get a list of profiles that have fan-curves enabled. - fn enabled_fan_profiles(&self) -> zbus::Result>; - /// Toggle to next platform_profile. Names provided by `Profiles`. /// If fan-curves are supported will also activate a fan curve for profile. fn next_profile(&self) -> zbus::Result<()>; diff --git a/rog-platform/Cargo.toml b/rog-platform/Cargo.toml index 23ca7566..86144bb0 100644 --- a/rog-platform/Cargo.toml +++ b/rog-platform/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] log.workspace = true rog_aura = { path = "../rog-aura" } +rog_profiles = { path = "../rog-profiles" } serde.workspace = true serde_derive.workspace = true zbus.workspace = true diff --git a/rog-platform/src/supported.rs b/rog-platform/src/supported.rs index 933b6b39..9fe1d4d1 100644 --- a/rog-platform/src/supported.rs +++ b/rog-platform/src/supported.rs @@ -3,6 +3,7 @@ use std::fmt; use rog_aura::aura_detection::PowerZones; use rog_aura::usb::AuraDevice; use rog_aura::{AdvancedAuraType, AuraModeNum, AuraZone}; +use rog_profiles::FanCurvePU; use serde_derive::{Deserialize, Serialize}; use typeshare::typeshare; use zbus::zvariant::Type; @@ -31,7 +32,7 @@ pub struct ChargeSupportedFunctions { #[derive(Serialize, Deserialize, Type, Debug, Default, Clone)] pub struct PlatformProfileFunctions { pub platform_profile: bool, - pub fan_curves: bool, + pub fans: Vec, } #[typeshare] @@ -106,7 +107,7 @@ impl fmt::Display for PlatformProfileFunctions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Platform profiles:")?; writeln!(f, "\tplatform: {}", self.platform_profile)?; - writeln!(f, "\tfan curves: {}", self.fan_curves) + writeln!(f, "\tfan curves: {:?}", self.fans) } } impl fmt::Display for LedSupportedFunctions { diff --git a/rog-profiles/Cargo.toml b/rog-profiles/Cargo.toml index 76a07e24..d32edd20 100644 --- a/rog-profiles/Cargo.toml +++ b/rog-profiles/Cargo.toml @@ -10,6 +10,7 @@ default = ["dbus"] dbus = ["zbus"] [dependencies] +log.workspace = true udev.workspace = true serde.workspace = true serde_derive.workspace = true diff --git a/rog-profiles/src/fan_curve_set.rs b/rog-profiles/src/fan_curve_set.rs index b6069824..e28e434c 100644 --- a/rog-profiles/src/fan_curve_set.rs +++ b/rog-profiles/src/fan_curve_set.rs @@ -1,3 +1,4 @@ +use log::trace; use serde_derive::{Deserialize, Serialize}; use typeshare::typeshare; use udev::Device; @@ -8,7 +9,8 @@ use crate::error::ProfileError; use crate::FanCurvePU; pub(crate) fn pwm_str(fan: char, index: usize) -> String { - let mut buf = "pwm1_auto_point1_pwm".to_owned(); + // The char 'X' is replaced via indexing + let mut buf = "pwmX_auto_pointX_pwm".to_owned(); unsafe { let tmp = buf.as_bytes_mut(); tmp[3] = fan as u8; @@ -18,7 +20,8 @@ pub(crate) fn pwm_str(fan: char, index: usize) -> String { } pub(crate) fn temp_str(fan: char, index: usize) -> String { - let mut buf = "pwm1_auto_point1_temp".to_owned(); + // The char 'X' is replaced via indexing + let mut buf = "pwmX_auto_pointX_temp".to_owned(); unsafe { let tmp = buf.as_bytes_mut(); tmp[3] = fan as u8; @@ -34,6 +37,7 @@ pub struct CurveData { pub fan: FanCurvePU, pub pwm: [u8; 8], pub temp: [u8; 8], + pub enabled: bool, } impl From<&CurveData> for String { @@ -65,7 +69,7 @@ impl std::str::FromStr for CurveData { type Err = ProfileError; /// Parse a string to the correct values that the fan curve kernel driver - /// expects + /// expects. The returned `CurveData` is not enabled by default. /// /// If the fan curve is given with percentage char '%' then the fan power /// values are converted otherwise the expected fan power range is @@ -126,6 +130,7 @@ impl std::str::FromStr for CurveData { fan: FanCurvePU::CPU, pwm, temp, + enabled: false, }) } } @@ -144,114 +149,39 @@ impl CurveData { } } - fn read_from_device(&mut self, device: &Device) { + pub fn read_from_device(&mut self, device: &Device) { for attr in device.attributes() { let tmp = attr.name().to_string_lossy(); - if tmp.starts_with("pwm1") && tmp.ends_with("_temp") { + let pwm_num: char = self.fan.into(); + let pwm = format!("pwm{pwm_num}"); + if tmp.starts_with(&pwm) && tmp.ends_with("_temp") { Self::set_val_from_attr(tmp.as_ref(), device, &mut self.temp); } - if tmp.starts_with("pwm1") && tmp.ends_with("_pwm") { + if tmp.starts_with(&pwm) && tmp.ends_with("_pwm") { Self::set_val_from_attr(tmp.as_ref(), device, &mut self.pwm); } } } - fn init_if_zeroed(&mut self, device: &mut Device) -> std::io::Result<()> { - if self.pwm == [0u8; 8] && self.temp == [0u8; 8] { - // Need to reset the device to defaults to get the proper profile defaults - match self.fan { - FanCurvePU::CPU => device.set_attribute_value("pwm1_enable", "3")?, - FanCurvePU::GPU => device.set_attribute_value("pwm2_enable", "3")?, - }; - self.read_from_device(device); - } - Ok(()) - } - /// Write this curve to the device fan specified by `self.fan` - fn write_to_device(&self, device: &mut Device, enable: bool) -> std::io::Result<()> { - let pwm_num = match self.fan { - FanCurvePU::CPU => '1', - FanCurvePU::GPU => '2', - }; - let enable = if enable { "1" } else { "2" }; + pub fn write_to_device(&self, device: &mut Device) -> std::io::Result<()> { + let pwm_num: char = self.fan.into(); + let enable = if self.enabled { "1" } else { "2" }; for (index, out) in self.pwm.iter().enumerate() { let pwm = pwm_str(pwm_num, index); + trace!("writing {pwm}"); device.set_attribute_value(&pwm, &out.to_string())?; } for (index, out) in self.temp.iter().enumerate() { let temp = temp_str(pwm_num, index); + trace!("writing {temp}"); device.set_attribute_value(&temp, &out.to_string())?; } // Enable must be done *after* all points are written - match self.fan { - FanCurvePU::CPU => device.set_attribute_value("pwm1_enable", enable)?, - FanCurvePU::GPU => device.set_attribute_value("pwm2_enable", enable)?, - }; - - Ok(()) - } -} - -/// A `FanCurveSet` contains both CPU and GPU fan curve data -#[typeshare] -#[cfg_attr(feature = "dbus", derive(Type))] -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct FanCurveSet { - pub enabled: bool, - pub cpu: CurveData, - pub gpu: CurveData, -} - -impl Default for FanCurveSet { - fn default() -> Self { - Self { - enabled: false, - cpu: CurveData { - fan: FanCurvePU::CPU, - pwm: [0u8; 8], - temp: [0u8; 8], - }, - gpu: CurveData { - fan: FanCurvePU::GPU, - pwm: [0u8; 8], - temp: [0u8; 8], - }, - } - } -} - -impl From<&FanCurveSet> for String { - fn from(s: &FanCurveSet) -> Self { - format!( - "Enabled: {}, {}, {}", - s.enabled, - String::from(&s.cpu), - String::from(&s.gpu), - ) - } -} - -impl FanCurveSet { - pub(crate) fn read_cpu_from_device(&mut self, device: &Device) { - self.cpu.read_from_device(device); - } - - pub(crate) fn read_gpu_from_device(&mut self, device: &Device) { - self.gpu.read_from_device(device); - } - - pub(crate) fn write_cpu_fan(&mut self, device: &mut Device) -> std::io::Result<()> { - self.cpu.init_if_zeroed(device)?; - self.cpu.write_to_device(device, self.enabled) - } - - pub(crate) fn write_gpu_fan(&mut self, device: &mut Device) -> std::io::Result<()> { - self.gpu.init_if_zeroed(device)?; - self.gpu.write_to_device(device, self.enabled) + device.set_attribute_value(format!("pwm{pwm_num}_enable"), enable) } } @@ -315,14 +245,14 @@ mod tests { assert_eq!(temp_str('1', 7), "pwm1_auto_point8_temp"); } - #[test] - fn set_to_string() { - let set = FanCurveSet::default(); - let string = String::from(&set); - assert_eq!( - string.as_str(), - "Enabled: false, CPU: 0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%, GPU: \ - 0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%" - ); - } + // #[test] + // fn set_to_string() { + // let set = FanCurveSet::default(); + // let string = String::from(&set); + // assert_eq!( + // string.as_str(), + // "Enabled: false, CPU: + // 0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%, GPU: \ 0c: + // 0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%" ); + // } } diff --git a/rog-profiles/src/lib.rs b/rog-profiles/src/lib.rs index c23ccca7..2531a297 100644 --- a/rog-profiles/src/lib.rs +++ b/rog-profiles/src/lib.rs @@ -7,7 +7,8 @@ use std::io::Write; use std::path::Path; use error::ProfileError; -use fan_curve_set::{CurveData, FanCurveSet}; +use fan_curve_set::CurveData; +use log::debug; use serde_derive::{Deserialize, Serialize}; use typeshare::typeshare; use udev::Device; @@ -131,10 +132,30 @@ impl Display for Profile { #[typeshare] #[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))] -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Deserialize, Serialize, Debug, Hash, PartialEq, Eq, Clone, Copy)] pub enum FanCurvePU { CPU, GPU, + MID, +} + +impl FanCurvePU { + fn which_fans(device: &Device) -> Vec { + let mut fans = Vec::with_capacity(3); + for fan in [Self::CPU, Self::GPU, Self::MID] { + let pwm_num: char = fan.into(); + let pwm_enable = format!("pwm{pwm_num}_enable"); + debug!("Looking for {pwm_enable}"); + for attr in device.attributes() { + let tmp = attr.name().to_string_lossy(); + if tmp.contains(&pwm_enable) { + debug!("Found {pwm_enable}"); + fans.push(fan); + } + } + } + fans + } } impl From for &str { @@ -142,6 +163,17 @@ impl From for &str { match pu { FanCurvePU::CPU => "cpu", FanCurvePU::GPU => "gpu", + FanCurvePU::MID => "mid", + } + } +} + +impl From for char { + fn from(pu: FanCurvePU) -> char { + match pu { + FanCurvePU::CPU => '1', + FanCurvePU::GPU => '2', + FanCurvePU::MID => '3', } } } @@ -153,6 +185,7 @@ impl std::str::FromStr for FanCurvePU { match fan.to_ascii_lowercase().trim() { "cpu" => Ok(FanCurvePU::CPU), "gpu" => Ok(FanCurvePU::GPU), + "mid" => Ok(FanCurvePU::MID), _ => Err(ProfileError::ParseProfileName), } } @@ -169,9 +202,9 @@ impl Default for FanCurvePU { #[cfg_attr(feature = "dbus", derive(Type))] #[derive(Deserialize, Serialize, Debug, Default)] pub struct FanCurveProfiles { - pub balanced: FanCurveSet, - pub performance: FanCurveSet, - pub quiet: FanCurveSet, + pub balanced: Vec, + pub performance: Vec, + pub quiet: Vec, } impl FanCurveProfiles { @@ -180,35 +213,49 @@ impl FanCurveProfiles { enumerator.match_subsystem("hwmon")?; for device in enumerator.scan_devices()? { - // if device.parent_with_subsystem("platform")?.is_some() { if let Some(name) = device.attribute_value("name") { if name == "asus_custom_fan_curve" { + debug!("asus_custom_fan_curve found"); return Ok(device); } } - // } } Err(ProfileError::NotSupported) } - pub fn is_supported() -> Result { - if Self::get_device().is_ok() { - return Ok(true); - } - - Ok(false) + /// Return an array of `FanCurvePU`. An empty array indicates no support for + /// Curves. + pub fn supported_fans() -> Result, ProfileError> { + let device = Self::get_device()?; + Ok(FanCurvePU::which_fans(&device)) } /// - pub fn read_from_dev_profile(&mut self, profile: Profile, device: &Device) { - let mut tmp = FanCurveSet::default(); - tmp.read_cpu_from_device(device); - tmp.read_gpu_from_device(device); - match profile { - Profile::Balanced => self.balanced = tmp, - Profile::Performance => self.performance = tmp, - Profile::Quiet => self.quiet = tmp, + pub fn read_from_dev_profile( + &mut self, + profile: Profile, + device: &Device, + ) -> Result<(), ProfileError> { + let fans = Self::supported_fans()?; + let mut curves = Vec::with_capacity(3); + + for fan in fans { + let mut curve = CurveData { + fan, + ..Default::default() + }; + debug!("Reading curve for {fan:?}"); + curve.read_from_device(device); + debug!("Curve: {curve:?}"); + curves.push(curve); } + + match profile { + Profile::Balanced => self.balanced = curves, + Profile::Performance => self.performance = curves, + Profile::Quiet => self.quiet = curves, + } + Ok(()) } /// Reset the stored (self) and device curve to the defaults of the @@ -220,19 +267,15 @@ impl FanCurveProfiles { &mut self, profile: Profile, device: &mut Device, - ) -> std::io::Result<()> { - // Do reset - device.set_attribute_value("pwm1_enable", "3")?; - device.set_attribute_value("pwm2_enable", "3")?; - // Then read - let mut tmp = FanCurveSet::default(); - tmp.read_cpu_from_device(device); - tmp.read_gpu_from_device(device); - match profile { - Profile::Balanced => self.balanced = tmp, - Profile::Performance => self.performance = tmp, - Profile::Quiet => self.quiet = tmp, + ) -> Result<(), ProfileError> { + let fans = Self::supported_fans()?; + // Do reset for all + for fan in fans { + let pwm_num: char = fan.into(); + let pwm = format!("pwm{pwm_num}_enable"); + device.set_attribute_value(&pwm, "3")?; } + self.read_from_dev_profile(profile, device)?; Ok(()) } @@ -250,50 +293,34 @@ impl FanCurveProfiles { Profile::Performance => &mut self.performance, Profile::Quiet => &mut self.quiet, }; - fans.write_cpu_fan(device)?; - fans.write_gpu_fan(device)?; + for fan in fans { + debug!("write_profile_curve_to_platform: writing profile:{profile}, {fan:?}"); + fan.write_to_device(device)?; + } Ok(()) } - pub fn get_enabled_curve_profiles(&self) -> Vec { - let mut tmp = Vec::new(); - if self.balanced.enabled { - tmp.push(Profile::Balanced); - } - if self.performance.enabled { - tmp.push(Profile::Performance); - } - if self.quiet.enabled { - tmp.push(Profile::Quiet); - } - tmp - } - pub fn set_profile_curve_enabled(&mut self, profile: Profile, enabled: bool) { match profile { - Profile::Balanced => self.balanced.enabled = enabled, - Profile::Performance => self.performance.enabled = enabled, - Profile::Quiet => self.quiet.enabled = enabled, + Profile::Balanced => { + for curve in self.balanced.iter_mut() { + curve.enabled = enabled; + } + } + Profile::Performance => { + for curve in self.performance.iter_mut() { + curve.enabled = enabled; + } + } + Profile::Quiet => { + for curve in self.quiet.iter_mut() { + curve.enabled = enabled; + } + } } } - pub fn get_all_fan_curves(&self) -> Vec { - vec![ - self.balanced.clone(), - self.performance.clone(), - self.quiet.clone(), - ] - } - - pub fn get_active_fan_curves(&self) -> Result<&FanCurveSet, ProfileError> { - match Profile::get_active_profile()? { - Profile::Balanced => Ok(&self.balanced), - Profile::Performance => Ok(&self.performance), - Profile::Quiet => Ok(&self.quiet), - } - } - - pub fn get_fan_curves_for(&self, name: Profile) -> &FanCurveSet { + pub fn get_fan_curves_for(&self, name: Profile) -> &[CurveData] { match name { Profile::Balanced => &self.balanced, Profile::Performance => &self.performance, @@ -301,37 +328,59 @@ impl FanCurveProfiles { } } - pub fn get_fan_curve_for(&self, name: &Profile, pu: &FanCurvePU) -> &CurveData { + pub fn get_fan_curve_for(&self, name: &Profile, pu: FanCurvePU) -> Option<&CurveData> { match name { - Profile::Balanced => match pu { - FanCurvePU::CPU => &self.balanced.cpu, - FanCurvePU::GPU => &self.balanced.gpu, - }, - Profile::Performance => match pu { - FanCurvePU::CPU => &self.performance.cpu, - FanCurvePU::GPU => &self.performance.gpu, - }, - Profile::Quiet => match pu { - FanCurvePU::CPU => &self.quiet.cpu, - FanCurvePU::GPU => &self.quiet.gpu, - }, + Profile::Balanced => { + for this_curve in self.balanced.iter() { + if this_curve.fan == pu { + return Some(this_curve); + } + } + } + Profile::Performance => { + for this_curve in self.performance.iter() { + if this_curve.fan == pu { + return Some(this_curve); + } + } + } + Profile::Quiet => { + for this_curve in self.quiet.iter() { + if this_curve.fan == pu { + return Some(this_curve); + } + } + } } + None } pub fn save_fan_curve(&mut self, curve: CurveData, profile: Profile) -> std::io::Result<()> { match profile { - Profile::Balanced => match curve.fan { - FanCurvePU::CPU => self.balanced.cpu = curve, - FanCurvePU::GPU => self.balanced.gpu = curve, - }, - Profile::Performance => match curve.fan { - FanCurvePU::CPU => self.performance.cpu = curve, - FanCurvePU::GPU => self.performance.gpu = curve, - }, - Profile::Quiet => match curve.fan { - FanCurvePU::CPU => self.quiet.cpu = curve, - FanCurvePU::GPU => self.quiet.gpu = curve, - }, + Profile::Balanced => { + for this_curve in self.balanced.iter_mut() { + if this_curve.fan == curve.fan { + *this_curve = curve; + break; + } + } + } + Profile::Performance => { + for this_curve in self.performance.iter_mut() { + if this_curve.fan == curve.fan { + *this_curve = curve; + break; + } + } + } + Profile::Quiet => { + for this_curve in self.quiet.iter_mut() { + if this_curve.fan == curve.fan { + *this_curve = curve; + break; + } + } + } } Ok(()) }