Fan curve enablement

- Add CtrlProfileTask
- Add method to reset active profile curve to platform default
- Wrap the zbus methods for profiles + fan curves
- Enable CLI args for fan curves
- CLI mod and save curves
This commit is contained in:
Luke D. Jones
2021-09-13 11:46:22 +12:00
parent 7041d77256
commit ab195e1d84
22 changed files with 783 additions and 328 deletions

View File

@@ -38,7 +38,7 @@ pub fn find_fan_curve_node() -> Result<Option<Device>, ProfileError> {
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Copy)]
pub enum Profile {
Balanced,
Performance,
@@ -51,10 +51,7 @@ impl Profile {
}
pub fn get_active_profile() -> Result<Profile, ProfileError> {
let mut file = OpenOptions::new()
.read(true)
.open(&PLATFORM_PROFILE)
.unwrap_or_else(|_| panic!("{} not found", &PLATFORM_PROFILE));
let mut file = OpenOptions::new().read(true).open(&PLATFORM_PROFILE)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
@@ -62,10 +59,7 @@ impl Profile {
}
pub fn get_profile_names() -> Result<Vec<Profile>, ProfileError> {
let mut file = OpenOptions::new()
.read(true)
.open(&PLATFORM_PROFILES)
.unwrap_or_else(|_| panic!("{} not found", &PLATFORM_PROFILES));
let mut file = OpenOptions::new().read(true).open(&PLATFORM_PROFILES)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
@@ -73,10 +67,7 @@ impl Profile {
}
pub fn set_profile(profile: Profile) -> Result<(), ProfileError> {
let mut file = OpenOptions::new()
.write(true)
.open(PLATFORM_PROFILE)
.unwrap_or_else(|_| panic!("{} not found", PLATFORM_PROFILE));
let mut file = OpenOptions::new().write(true).open(PLATFORM_PROFILE)?;
file.write_all(<&str>::from(profile).as_bytes())?;
Ok(())
@@ -110,8 +101,21 @@ impl From<&str> for Profile {
}
}
impl std::str::FromStr for Profile {
type Err = ProfileError;
fn from_str(profile: &str) -> Result<Self, Self::Err> {
match profile.to_ascii_lowercase().trim() {
"balanced" => Ok(Profile::Balanced),
"performance" => Ok(Profile::Performance),
"quiet" => Ok(Profile::Quiet),
_ => Err(ProfileError::ParseProfileName),
}
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Copy)]
pub enum FanCurvePU {
CPU,
GPU,
@@ -126,27 +130,63 @@ impl From<FanCurvePU> for &str {
}
}
impl std::str::FromStr for FanCurvePU {
type Err = ProfileError;
fn from_str(fan: &str) -> Result<Self, Self::Err> {
match fan.to_ascii_lowercase().trim() {
"cpu" => Ok(FanCurvePU::CPU),
"gpu" => Ok(FanCurvePU::GPU),
_ => Err(ProfileError::ParseProfileName),
}
}
}
impl Default for FanCurvePU {
fn default() -> Self {
Self::CPU
}
}
/// Main purpose of `FanCurves` is to enable retoring state on system boot
/// Main purpose of `FanCurves` is to enable restoring state on system boot
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveProfiles {
enabled: Vec<Profile>,
balanced: FanCurveSet,
performance: FanCurveSet,
quiet: FanCurveSet,
}
impl FanCurveProfiles {
pub fn get_device() -> Result<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(device);
}
}
}
}
Err(ProfileError::NotSupported)
}
pub fn is_supported() -> Result<bool, ProfileError> {
if Self::get_device().is_ok() {
return Ok(true);
}
Ok(false)
}
///
pub fn read_from_dev_profile(&mut self, profile: Profile, device: &Device) {
let mut tmp = FanCurveSet::default();
tmp.read_from_device(device);
tmp.read_cpu_from_device(device);
tmp.read_gpu_from_device(device);
match profile {
Profile::Balanced => self.balanced = tmp,
Profile::Performance => self.performance = tmp,
@@ -154,22 +194,67 @@ impl FanCurveProfiles {
}
}
pub fn write_to_platform(&self, profile: Profile, device: &mut Device) {
/// 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.
pub fn set_active_curve_to_defaults(
&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,
}
Ok(())
}
/// Write the curves for the selected profile to the device. If the curve is
/// in the enabled list it will become active.
pub fn write_profile_curve_to_platform(
&mut self,
profile: Profile,
device: &mut Device,
) -> std::io::Result<()> {
let fans = match profile {
Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance,
Profile::Quiet => &self.quiet,
Profile::Balanced => &mut self.balanced,
Profile::Performance => &mut self.performance,
Profile::Quiet => &mut self.quiet,
};
fans.write_cpu_fan(device);
fans.write_gpu_fan(device);
fans.write_cpu_fan(device)?;
fans.write_gpu_fan(device)?;
Ok(())
}
pub fn get_enabled_curve_profiles(&self) -> &[Profile] {
&self.enabled
pub fn get_enabled_curve_profiles(&self) -> Vec<Profile> {
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_enabled_curve_profiles(&mut self, profiles: Vec<Profile>) {
self.enabled = profiles
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,
}
}
pub fn get_all_fan_curves(&self) -> Vec<FanCurveSet> {
@@ -180,11 +265,11 @@ impl FanCurveProfiles {
]
}
pub fn get_active_fan_curves(&self) -> &FanCurveSet {
match Profile::get_active_profile().unwrap() {
Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance,
Profile::Quiet => &self.quiet,
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),
}
}
@@ -213,16 +298,7 @@ impl FanCurveProfiles {
}
}
pub fn write_and_set_fan_curve(
&mut self,
curve: CurveData,
profile: Profile,
device: &mut Device,
) {
match curve.fan {
FanCurvePU::CPU => write_to_fan(&curve, '1', device),
FanCurvePU::GPU => write_to_fan(&curve, '2', device),
}
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,
@@ -237,33 +313,6 @@ impl FanCurveProfiles {
FanCurvePU::GPU => self.quiet.gpu = curve,
},
}
}
}
pub 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 + 1, 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 + 1, 10).unwrap() as u8;
}
let out = out.to_string();
device.set_attribute_value(&pwm, &out).unwrap();
Ok(())
}
}