Begin syncing changes with patch series

This commit is contained in:
Luke D. Jones
2021-09-07 20:20:37 +12:00
parent 5f677bc3b9
commit bfaa478a4a
9 changed files with 298 additions and 233 deletions

1
Cargo.lock generated
View File

@@ -938,6 +938,7 @@ version = "1.0.0"
dependencies = [ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"udev",
"zvariant", "zvariant",
"zvariant_derive", "zvariant_derive",
] ]

View File

@@ -1,4 +1,5 @@
use log::{error, warn}; use log::{error, warn};
use rog_profiles::fan_curves::FanCurveSet;
use rog_profiles::{FanCurves, Profile}; use rog_profiles::{FanCurves, Profile};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
@@ -22,10 +23,11 @@ impl ProfileConfig {
fan_curves: None, fan_curves: None,
}; };
if FanCurves::is_fan_curves_supported() { if let Ok(res) = FanCurveSet::is_supported() {
let mut curves = FanCurves::default(); if res {
curves.update_from_platform(); let mut curves = FanCurves::default();
platform.fan_curves = Some(curves); platform.fan_curves = Some(curves);
}
} }
platform platform

View File

@@ -2,37 +2,16 @@ use crate::error::RogError;
use crate::GetSupported; use crate::GetSupported;
use log::{info, warn}; use log::{info, warn};
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_profiles::{FanCurves, Profile}; use rog_profiles::fan_curves::FanCurveSet;
use rog_profiles::{Profile};
use rog_supported::PlatformProfileFunctions; use rog_supported::PlatformProfileFunctions;
use std::sync::Arc; use udev::Device;
use std::sync::Mutex;
use super::config::ProfileConfig; use super::config::ProfileConfig;
pub struct CtrlPlatformTask {
config: Arc<Mutex<ProfileConfig>>,
}
impl CtrlPlatformTask {
pub fn new(config: Arc<Mutex<ProfileConfig>>) -> Self {
Self { config }
}
}
impl crate::CtrlTask for CtrlPlatformTask {
fn do_task(&self) -> Result<(), RogError> {
if let Ok(mut lock) = self.config.try_lock() {
// Refresh the config in-case the user has edited it
if let Some(curves) = &mut lock.fan_curves {
curves.update_from_platform();
}
}
Ok(())
}
}
pub struct CtrlPlatformProfile { pub struct CtrlPlatformProfile {
pub config: Arc<Mutex<ProfileConfig>>, pub config: ProfileConfig,
pub fan_device: Option<Device>,
} }
impl GetSupported for CtrlPlatformProfile { impl GetSupported for CtrlPlatformProfile {
@@ -48,7 +27,14 @@ https://lkml.org/lkml/2021/8/18/1022
"# "#
); );
} }
if !FanCurves::is_fan_curves_supported() {
let res = FanCurveSet::is_supported();
let mut fan_curve_supported = res.is_err();
if let Ok(r) = res {
fan_curve_supported = r;
};
if fan_curve_supported {
info!( info!(
r#" r#"
fan curves kernel interface not found, your laptop does not support this, or the interface is missing. fan curves kernel interface not found, your laptop does not support this, or the interface is missing.
@@ -58,9 +44,10 @@ Please note that as of 24/08/2021 this is not final.
"# "#
); );
} }
PlatformProfileFunctions { PlatformProfileFunctions {
platform_profile: Profile::is_platform_profile_supported(), platform_profile: Profile::is_platform_profile_supported(),
fan_curves: FanCurves::is_fan_curves_supported(), fan_curves: fan_curve_supported,
} }
} }
} }
@@ -68,53 +55,54 @@ Please note that as of 24/08/2021 this is not final.
impl crate::Reloadable for CtrlPlatformProfile { impl crate::Reloadable for CtrlPlatformProfile {
/// Fetch the active profile and use that to set all related components up /// Fetch the active profile and use that to set all related components up
fn reload(&mut self) -> Result<(), RogError> { fn reload(&mut self) -> Result<(), RogError> {
if let Ok(cfg) = self.config.clone().try_lock() { if let Some(curves) = &self.config.fan_curves {
if let Some(curves) = &cfg.fan_curves { if let Ok(mut device) = FanCurveSet::get_device() {
curves.update_platform(); curves.write_to_platform(self.config.active, &mut device);
}
} }
}
Ok(()) Ok(())
} }
} }
impl CtrlPlatformProfile { impl CtrlPlatformProfile {
pub fn new(config: Arc<Mutex<ProfileConfig>>) -> Result<Self, RogError> { pub fn new(config: ProfileConfig, fan_device: Option<Device>) -> Result<Self, RogError> {
if Profile::is_platform_profile_supported() { if Profile::is_platform_profile_supported() {
info!("Device has profile control available"); info!("Device has profile control available");
return Ok(CtrlPlatformProfile { config }); return Ok(CtrlPlatformProfile { config, fan_device });
} }
Err(ProfileError::NotSupported.into()) Err(ProfileError::NotSupported.into())
} }
pub fn get_device(&self) -> Option<Device> {
self.fan_device.clone()
}
pub fn save_config(&self) { pub fn save_config(&self) {
if let Ok(lock) = self.config.lock() { self.config.write();
lock.write();
}
} }
/// Toggle to next profile in list. This will first read the config, switch, then write out /// Toggle to next profile in list. This will first read the config, switch, then write out
pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> { pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> {
if let Ok(mut config) = self.config.clone().try_lock() { // Read first just incase the user has modified the config before calling this
// Read first just incase the user has modified the config before calling this self.config.read();
config.read();
match config.active { match self.config.active {
Profile::Balanced => { Profile::Balanced => {
Profile::set_profile(Profile::Performance)?; Profile::set_profile(Profile::Performance)?;
config.active = Profile::Performance; self.config.active = Profile::Performance;
} }
Profile::Performance => { Profile::Performance => {
Profile::set_profile(Profile::Quiet)?; Profile::set_profile(Profile::Quiet)?;
config.active = Profile::Quiet; self.config.active = Profile::Quiet;
} }
Profile::Quiet => { Profile::Quiet => {
Profile::set_profile(Profile::Balanced)?; Profile::set_profile(Profile::Balanced)?;
config.active = Profile::Balanced; self.config.active = Profile::Balanced;
}
} }
config.write();
} }
self.config.write();
Ok(()) Ok(())
} }
} }

View File

@@ -1,5 +1,5 @@
use log::warn; use log::warn;
use rog_profiles::FanCurve; use rog_profiles::fan_curves::FanCurveSet;
use rog_profiles::Profile; use rog_profiles::Profile;
use std::sync::Arc; use std::sync::Arc;
@@ -45,11 +45,9 @@ impl ProfileZbus {
/// Fetch the active profile name /// Fetch the active profile name
fn active_profile(&mut self) -> zbus::fdo::Result<Profile> { fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() { ctrl.config.read();
cfg.read(); return Ok(ctrl.config.active);
return Ok(cfg.active);
}
} }
Err(Error::Failed( Err(Error::Failed(
"Failed to get active profile name".to_string(), "Failed to get active profile name".to_string(),
@@ -58,15 +56,14 @@ impl ProfileZbus {
/// Set this platform_profile name as active /// Set this platform_profile name as active
fn set_active_profile(&self, profile: Profile) { fn set_active_profile(&self, profile: Profile) {
if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() { // Read first just incase the user has modified the config before calling this
// Read first just incase the user has modified the config before calling this ctrl.config.read();
cfg.read(); Profile::set_profile(profile)
Profile::set_profile(profile) .map_err(|e| warn!("Profile::set_profile, {}", e))
.map_err(|e| warn!("Profile::set_profile, {}", e)) .ok();
.ok(); ctrl.config.active = profile;
cfg.active = profile;
}
ctrl.save_config(); ctrl.save_config();
} }
self.do_notification(); self.do_notification();
@@ -74,14 +71,12 @@ impl ProfileZbus {
/// Get a list of profiles that have fan-curves enabled. /// Get a list of profiles that have fan-curves enabled.
fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> { fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() { ctrl.config.read();
cfg.read(); if let Some(curves) = &ctrl.config.fan_curves {
if let Some(curves) = &cfg.fan_curves { return Ok(curves.get_enabled_curve_names().to_vec());
return Ok(curves.get_enabled_curve_names().to_vec());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
} }
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
} }
Err(Error::Failed( Err(Error::Failed(
"Failed to get enabled fan curve names".to_string(), "Failed to get enabled fan curve names".to_string(),
@@ -89,43 +84,41 @@ impl ProfileZbus {
} }
/// Get the fan-curve data for the currently active Profile /// Get the fan-curve data for the currently active Profile
fn active_fan_curve_data(&mut self) -> zbus::fdo::Result<FanCurve> { fn active_fan_curve_data(&mut self) -> zbus::fdo::Result<FanCurveSet> {
if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() { ctrl.config.read();
cfg.read(); if let Some(curves) = &ctrl.config.fan_curves {
if let Some(curves) = &cfg.fan_curves { return Ok((*curves.get_active_fan_curves()).clone());
return Ok((*curves.get_active_fan_curves()).clone());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
} }
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
} }
Err(Error::Failed("Failed to get fan curve data".to_string())) Err(Error::Failed("Failed to get fan curve data".to_string()))
} }
/// Get fan-curve data for each Profile as an array of objects /// Get fan-curve data for each Profile as an array of objects
fn fan_curves(&self) -> zbus::fdo::Result<Vec<FanCurve>> { fn fan_curves(&self) -> zbus::fdo::Result<Vec<FanCurveSet>> {
if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() { ctrl.config.read();
cfg.read(); if let Some(curves) = &ctrl.config.fan_curves {
if let Some(curves) = &cfg.fan_curves { return Ok(curves.get_all_fan_curves());
return Ok(curves.get_all_fan_curves());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
} }
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
} }
Err(Error::Failed("Failed to get all fan curves".to_string())) Err(Error::Failed("Failed to get all fan curves".to_string()))
} }
/// Set this fan-curve data /// Set this fan-curve data
fn set_fan_curve(&self, curve: FanCurve) -> zbus::fdo::Result<()> { fn set_fan_curve(&self, curve: FanCurveSet) -> zbus::fdo::Result<()> {
if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() { ctrl.config.read();
cfg.read(); if let Some(mut device) = ctrl.get_device() {
if let Some(curves) = &mut cfg.fan_curves { if let Some(curves) = &mut ctrl.config.fan_curves {
curves.set_fan_curve(curve); curves.set_fan_curve(curve, &mut device);
} }
} else {
return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
} }
ctrl.save_config(); ctrl.save_config();
} }
@@ -139,10 +132,8 @@ impl ProfileZbus {
impl ProfileZbus { impl ProfileZbus {
fn do_notification(&self) { fn do_notification(&self) {
if let Ok(ctrl) = self.inner.try_lock() { if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(cfg) = ctrl.config.clone().try_lock() { self.notify_profile(&ctrl.config.active)
self.notify_profile(&cfg.active) .unwrap_or_else(|err| warn!("{}", err));
.unwrap_or_else(|err| warn!("{}", err));
}
} }
} }
} }

View File

@@ -7,7 +7,6 @@ use daemon::ctrl_aura::controller::{
}; };
use daemon::ctrl_charge::CtrlCharge; use daemon::ctrl_charge::CtrlCharge;
use daemon::ctrl_profiles::config::ProfileConfig; use daemon::ctrl_profiles::config::ProfileConfig;
use daemon::ctrl_profiles::controller::CtrlPlatformTask;
use daemon::{ use daemon::{
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
}; };
@@ -21,6 +20,7 @@ use daemon::{CtrlTask, Reloadable, ZbusAdd};
use log::LevelFilter; use log::LevelFilter;
use log::{error, info, warn}; use log::{error, info, warn};
use rog_dbus::DBUS_NAME; use rog_dbus::DBUS_NAME;
use rog_profiles::fan_curves::FanCurveSet;
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::io::Write; use std::io::Write;
@@ -108,16 +108,19 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
} }
} }
let profile_config = Arc::new(Mutex::new(ProfileConfig::load(PROFILE_CONFIG_PATH.into()))); let fan_device = if let Ok(res) = FanCurveSet::get_device() {
match CtrlPlatformProfile::new(profile_config.clone()) { Some(res)
} else {
None
};
let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into());
match CtrlPlatformProfile::new(profile_config, fan_device) {
Ok(mut ctrl) => { Ok(mut ctrl) => {
ctrl.reload() ctrl.reload()
.unwrap_or_else(|err| warn!("Profile control: {}", err)); .unwrap_or_else(|err| warn!("Profile control: {}", err));
let tmp = Arc::new(Mutex::new(ctrl)); let tmp = Arc::new(Mutex::new(ctrl));
ProfileZbus::new(tmp).add_to_server(&mut object_server); ProfileZbus::new(tmp).add_to_server(&mut object_server);
tasks.push(Box::new(CtrlPlatformTask::new(profile_config)));
} }
Err(err) => { Err(err) => {
error!("Profile control: {}", err); error!("Profile control: {}", err);

View File

@@ -21,7 +21,7 @@
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use rog_profiles::{FanCurve, Profile}; use rog_profiles::{Profile, fan_curves::FanCurveSet};
use zbus::{dbus_proxy, Connection, Result}; use zbus::{dbus_proxy, Connection, Result};
#[dbus_proxy( #[dbus_proxy(
@@ -45,13 +45,13 @@ trait Daemon {
fn enabled_fan_profiles(&self) -> zbus::Result<Vec<Profile>>; fn enabled_fan_profiles(&self) -> zbus::Result<Vec<Profile>>;
/// Get the active `Profile` data /// Get the active `Profile` data
fn active_fan_data(&self) -> zbus::Result<FanCurve>; fn active_fan_data(&self) -> zbus::Result<FanCurveSet>;
/// Get all fan curve data /// Get all fan curve data
fn fan_curves(&self) -> zbus::Result<Vec<FanCurve>>; fn fan_curves(&self) -> zbus::Result<Vec<FanCurveSet>>;
/// Set a fan curve. If a field is empty then the exisiting saved curve is used /// Set a fan curve. If a field is empty then the exisiting saved curve is used
fn set_fan_curve(&self, curve: FanCurve) -> zbus::Result<()>; fn set_fan_curve(&self, curve: FanCurveSet) -> zbus::Result<()>;
/// NotifyProfile signal /// NotifyProfile signal
#[dbus_proxy(signal)] #[dbus_proxy(signal)]

View File

@@ -9,6 +9,7 @@ default = ["dbus"]
dbus = ["zvariant", "zvariant_derive"] dbus = ["zvariant", "zvariant_derive"]
[dependencies] [dependencies]
udev = "^0.6"
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"

View File

@@ -0,0 +1,147 @@
use serde_derive::{Deserialize, Serialize};
use udev::Device;
#[cfg(feature = "dbus")]
use zvariant_derive::Type;
use crate::error::ProfileError;
pub fn pwm_str(fan: char, index: char) -> String {
let mut buf = "pwm1_auto_point1_pwm".to_string();
unsafe {
let tmp = buf.as_bytes_mut();
tmp[3] = fan as u8;
tmp[15] = index as u8;
}
buf
}
pub fn temp_str(fan: char, index: char) -> String {
let mut buf = "pwm1_auto_point1_temp".to_string();
unsafe {
let tmp = buf.as_bytes_mut();
tmp[3] = fan as u8;
tmp[15] = index as u8;
}
buf
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub struct CurveData {
pub pwm: [u8; 8],
pub temp: [u8; 8],
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub struct FanCurveSet {
pub cpu: CurveData,
pub gpu: CurveData,
}
impl FanCurveSet {
pub fn get_device() -> Result<Device, ProfileError> {
let mut enumerator = udev::Enumerator::new()?;
enumerator.match_subsystem("hwmon")?;
for device in enumerator.scan_devices().unwrap() {
if device.parent_with_subsystem("platform").unwrap().is_some() {
if let Some(name) = device.attribute_value("name") {
if name == "asus_custom_fan_curve" {
return Ok(device);
}
}
}
}
Err(ProfileError::NotSupported)
}
pub fn new() -> Result<(Self, Device), ProfileError> {
if let Ok(device) = Self::get_device() {
let mut fans = Self {
cpu: CurveData::default(),
gpu: CurveData::default(),
};
fans.init_from_device(&device);
return Ok((fans, device));
}
Err(ProfileError::NotSupported)
}
pub fn is_supported() -> Result<bool, ProfileError> {
if Self::get_device().is_ok() {
return Ok(true);
}
Ok(false)
}
pub fn update_from_device(&mut self, device: &Device) {
self.init_from_device(device);
}
fn set_val_from_attr(tmp: &str, device: &Device, buf: &mut [u8; 8]) {
if let Some(n) = tmp.chars().nth(15) {
let i = n.to_digit(10).unwrap() as usize;
let d = device.attribute_value(tmp).unwrap();
let d: u8 = d.to_string_lossy().parse().unwrap();
buf[i - 1] = d;
}
}
pub fn init_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") {
Self::set_val_from_attr(tmp.as_ref(), device, &mut self.cpu.temp)
}
if tmp.starts_with("pwm1") && tmp.ends_with("_pwm") {
Self::set_val_from_attr(tmp.as_ref(), device, &mut self.cpu.pwm)
}
if tmp.starts_with("pwm2") && tmp.ends_with("_temp") {
Self::set_val_from_attr(tmp.as_ref(), device, &mut self.gpu.temp)
}
if tmp.starts_with("pwm2") && tmp.ends_with("_pwm") {
Self::set_val_from_attr(tmp.as_ref(), device, &mut self.gpu.pwm)
}
}
}
fn write_to_fan(curve: &CurveData, pwm_num: char, device: &mut Device) {
let mut pwm = "pwmN_auto_pointN_pwm".to_string();
for (index,out) in curve.pwm.iter().enumerate() {
unsafe {
let buf = pwm.as_bytes_mut();
buf[3] = pwm_num as u8;
// Should be quite safe to unwrap as we're not going over 8
buf[15] = char::from_digit(index as u32, 10).unwrap() as u8;
}
let out = out.to_string();
device.set_attribute_value(&pwm, &out).unwrap();
}
let mut pwm = "pwmN_auto_pointN_temp".to_string();
for (index,out) in curve.temp.iter().enumerate() {
unsafe {
let buf = pwm.as_bytes_mut();
buf[3] = pwm_num as u8;
// Should be quite safe to unwrap as we're not going over 8
buf[15] = char::from_digit(index as u32, 10).unwrap() as u8;
}
let out = out.to_string();
device.set_attribute_value(&pwm, &out).unwrap();
}
}
pub fn write_cpu_fan(&self, device: &mut Device) {
Self::write_to_fan(&self.cpu, '1', device);
}
pub fn write_gpu_fan(&self, device: &mut Device) {
Self::write_to_fan(&self.gpu, '2', device);
}
}

View File

@@ -1,26 +1,42 @@
pub mod error; pub mod error;
pub mod fan_curves;
use std::{ use std::{
fs::OpenOptions, fs::OpenOptions,
io::{Read, Write}, io::{Read, Write},
path::{Path, PathBuf}, path::{Path},
}; };
use error::ProfileError; use error::ProfileError;
use fan_curves::{CurveData, FanCurveSet};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use udev::Device;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zvariant_derive::Type; use zvariant_derive::Type;
pub static PLATFORM_PROFILE: &str = "/sys/firmware/acpi/platform_profile"; pub static PLATFORM_PROFILE: &str = "/sys/firmware/acpi/platform_profile";
pub static PLATFORM_PROFILES: &str = "/sys/firmware/acpi/platform_profile_choices"; pub static PLATFORM_PROFILES: &str = "/sys/firmware/acpi/platform_profile_choices";
pub static FAN_CURVE_BASE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/";
pub static FAN_CURVE_ACTIVE_FILE: &str = "enabled_fan_curve_profiles";
pub static FAN_CURVE_FILENAME_PART: &str = "_fan_curve_";
pub static VERSION: &str = env!("CARGO_PKG_VERSION"); pub static VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn find_fan_curve_node() -> Result<Option<Device>, ProfileError> {
let mut enumerator = udev::Enumerator::new()?;
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" {
return Ok(Some(device));
}
}
}
}
Err(ProfileError::NotSupported)
}
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)] #[derive(Deserialize, Serialize, Debug, Clone, Copy)]
pub enum Profile { pub enum Profile {
@@ -116,82 +132,44 @@ impl Default for FanCurvePU {
} }
} }
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub struct FanCurve {
pub profile: Profile,
pub cpu: String,
pub gpu: String,
}
/// Main purpose of `FanCurves` is to enable retoring state on system boot /// Main purpose of `FanCurves` is to enable retoring state on system boot
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurves { pub struct FanCurves {
enabled: Vec<Profile>, enabled: Vec<Profile>,
balanced: FanCurve, balanced: FanCurveSet,
performance: FanCurve, performance: FanCurveSet,
quiet: FanCurve, quiet: FanCurveSet,
} }
impl Default for FanCurves {
fn default() -> Self {
let mut curves = Self {
enabled: Default::default(),
balanced: Default::default(),
performance: Default::default(),
quiet: Default::default(),
};
curves.balanced.profile = Profile::Balanced;
curves.performance.profile = Profile::Performance;
curves.quiet.profile = Profile::Quiet;
curves
}
}
impl FanCurves { impl FanCurves {
pub fn is_fan_curves_supported() -> bool { ///
let mut path = PathBuf::new(); pub fn init_from_platform(&mut self, profile: Profile, device: &Device) {
path.push(FAN_CURVE_BASE_PATH); let mut tmp = FanCurveSet::default();
path.push(FAN_CURVE_ACTIVE_FILE); tmp.init_from_device(device);
path.exists() match profile {
Profile::Balanced => self.balanced = tmp,
Profile::Performance => self.performance = tmp,
Profile::Quiet => self.quiet = tmp,
}
} }
pub fn update_from_platform(&mut self) { pub fn write_to_platform(&self, profile: Profile, device: &mut Device) {
self.balanced.cpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::CPU); let fans = match profile {
self.balanced.gpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::GPU); Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance,
self.performance.cpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::CPU); Profile::Quiet => &self.quiet,
self.performance.gpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::GPU); };
fans.write_cpu_fan(device);
self.quiet.cpu = Self::get_fan_curve_from_file(Profile::Quiet, FanCurvePU::CPU); fans.write_gpu_fan(device);
self.quiet.gpu = Self::get_fan_curve_from_file(Profile::Quiet, FanCurvePU::GPU);
}
pub fn update_platform(&self) {
Self::set_fan_curve_for_platform(Profile::Balanced, FanCurvePU::CPU, &self.balanced.cpu);
Self::set_fan_curve_for_platform(Profile::Balanced, FanCurvePU::GPU, &self.balanced.gpu);
Self::set_fan_curve_for_platform(
Profile::Performance,
FanCurvePU::CPU,
&self.performance.cpu,
);
Self::set_fan_curve_for_platform(
Profile::Performance,
FanCurvePU::GPU,
&self.performance.gpu,
);
Self::set_fan_curve_for_platform(Profile::Quiet, FanCurvePU::CPU, &self.quiet.cpu);
Self::set_fan_curve_for_platform(Profile::Quiet, FanCurvePU::GPU, &self.quiet.gpu);
} }
pub fn get_enabled_curve_names(&self) -> &[Profile] { pub fn get_enabled_curve_names(&self) -> &[Profile] {
&self.enabled &self.enabled
} }
pub fn get_all_fan_curves(&self) -> Vec<FanCurve> { pub fn get_all_fan_curves(&self) -> Vec<FanCurveSet> {
vec![ vec![
self.balanced.clone(), self.balanced.clone(),
self.performance.clone(), self.performance.clone(),
@@ -199,7 +177,7 @@ impl FanCurves {
] ]
} }
pub fn get_active_fan_curves(&self) -> &FanCurve { pub fn get_active_fan_curves(&self) -> &FanCurveSet {
match Profile::get_active_profile().unwrap() { match Profile::get_active_profile().unwrap() {
Profile::Balanced => &self.balanced, Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance, Profile::Performance => &self.performance,
@@ -207,7 +185,7 @@ impl FanCurves {
} }
} }
pub fn get_fan_curves_for(&self, name: Profile) -> &FanCurve { pub fn get_fan_curves_for(&self, name: Profile) -> &FanCurveSet {
match name { match name {
Profile::Balanced => &self.balanced, Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance, Profile::Performance => &self.performance,
@@ -215,23 +193,7 @@ impl FanCurves {
} }
} }
fn get_fan_curve_from_file(name: Profile, pu: FanCurvePU) -> String { pub fn get_fan_curve_for(&self, name: &Profile, pu: &FanCurvePU) -> &CurveData {
let mut file: String = FAN_CURVE_BASE_PATH.into();
file.push_str(pu.into());
file.push_str(FAN_CURVE_FILENAME_PART);
file.push_str(name.into());
let mut file = OpenOptions::new()
.read(true)
.open(&file)
.unwrap_or_else(|_| panic!("{} not found", &file));
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
buf.trim().to_string()
}
pub fn get_fan_curve_for(&self, name: &Profile, pu: &FanCurvePU) -> &str {
match name { match name {
Profile::Balanced => match pu { Profile::Balanced => match pu {
FanCurvePU::CPU => &self.balanced.cpu, FanCurvePU::CPU => &self.balanced.cpu,
@@ -248,38 +210,8 @@ impl FanCurves {
} }
} }
fn set_fan_curve_for_platform(name: Profile, pu: FanCurvePU, curve: &str) { pub fn set_fan_curve(&self, curve: FanCurveSet, device: &mut Device) {
let mut file: String = FAN_CURVE_BASE_PATH.into(); curve.write_cpu_fan(device);
file.push_str(pu.into()); curve.write_gpu_fan(device);
file.push_str(FAN_CURVE_FILENAME_PART);
file.push_str(name.into());
let mut file = OpenOptions::new()
.write(true)
.open(&file)
.unwrap_or_else(|_| panic!("{} not found", &file));
file.write_all(curve.as_bytes()).unwrap();
}
pub fn set_fan_curve(&mut self, curve: FanCurve) {
// First, set the profiles.
Self::set_fan_curve_for_platform(curve.profile, FanCurvePU::CPU, &curve.cpu);
match curve.profile {
Profile::Balanced => self.balanced.cpu = curve.cpu,
Profile::Performance => self.performance.cpu = curve.cpu,
Profile::Quiet => self.quiet.cpu = curve.cpu,
};
Self::set_fan_curve_for_platform(curve.profile, FanCurvePU::GPU, &curve.gpu);
match curve.profile {
Profile::Balanced => self.balanced.gpu = curve.gpu,
Profile::Performance => self.performance.gpu = curve.gpu,
Profile::Quiet => self.quiet.cpu = curve.gpu,
};
// Any curve that was blank will have been reset, so repopulate the settings
// Note: successfully set curves will just be re-read in.
self.update_from_platform();
} }
} }