Temporary checkpoint

This commit is contained in:
Luke D. Jones
2021-08-22 14:01:05 +12:00
parent 8fcd05c2bb
commit 0ed97db4c1
32 changed files with 553 additions and 2341 deletions

View File

@@ -1,18 +1,12 @@
use std::fmt;
use intel_pstate::PStateError;
use rog_fan_curve::CurveError;
#[derive(Debug)]
pub enum ProfileError {
ParseFanLevel,
Path(String, std::io::Error),
Read(String, std::io::Error),
Write(String, std::io::Error),
NotSupported,
NotFound(String),
IntelPstate(PStateError),
FanCurve(CurveError),
Io(std::io::Error),
//Zbus(zbus::Error),
}
@@ -21,14 +15,11 @@ impl fmt::Display for ProfileError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ProfileError::ParseFanLevel => write!(f, "Parse profile error"),
ProfileError::Path(path, error) => write!(f, "Path {}: {}", path, error),
ProfileError::Read(path, error) => write!(f, "Read {}: {}", path, error),
ProfileError::Write(path, error) => write!(f, "Write {}: {}", path, error),
ProfileError::NotSupported => write!(f, "Not supported"),
ProfileError::NotFound(deets) => write!(f, "Not found: {}", deets),
ProfileError::IntelPstate(err) => write!(f, "Intel pstate error: {}", err),
ProfileError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err),
ProfileError::Io(detail) => write!(f, "std::io error: {}", detail),
//Error::Zbus(detail) => write!(f, "Zbus error: {}", detail),
}
@@ -36,15 +27,3 @@ impl fmt::Display for ProfileError {
}
impl std::error::Error for ProfileError {}
impl From<PStateError> for ProfileError {
fn from(err: PStateError) -> Self {
ProfileError::IntelPstate(err)
}
}
impl From<CurveError> for ProfileError {
fn from(err: CurveError) -> Self {
ProfileError::FanCurve(err)
}
}

View File

@@ -1,8 +1,284 @@
pub mod error;
pub mod profiles;
static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy";
static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode";
static AMD_BOOST_PATH: &str = "/sys/devices/system/cpu/cpufreq/boost";
use std::{
fs::OpenOptions,
io::{Read, Write},
path::{Path, PathBuf},
};
use error::ProfileError;
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zvariant_derive::Type;
pub static PLATFORM_PROFILE: &str = "/sys/firmware/acpi/platform_profile";
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");
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
pub enum Profile {
Balanced,
Performance,
Quiet,
}
impl Profile {
pub fn is_platform_profile_supported() -> bool {
Path::new(PLATFORM_PROFILES).exists()
}
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 buf = String::new();
file.read_to_string(&mut buf).unwrap();
Ok(buf.as_str().into())
}
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 buf = String::new();
file.read_to_string(&mut buf).unwrap();
Ok(buf.rsplit(' ').map(|p| p.into()).collect())
}
pub fn set_profile(profile: Profile) {
let mut file = OpenOptions::new()
.write(true)
.open(PLATFORM_PROFILE)
.unwrap_or_else(|_| panic!("{} not found", PLATFORM_PROFILE));
file.write_all(<&str>::from(profile).as_bytes()).unwrap();
}
}
impl Default for Profile {
fn default() -> Self {
Self::Balanced
}
}
impl From<Profile> for &str {
fn from(profile: Profile) -> &'static str {
match profile {
Profile::Balanced => "balanced",
Profile::Performance => "performance",
Profile::Quiet => "quiet",
}
}
}
impl From<&str> for Profile {
fn from(profile: &str) -> Profile {
match profile.to_ascii_lowercase().trim() {
"balanced" => Profile::Balanced,
"performance" => Profile::Performance,
"quiet" => Profile::Quiet,
_ => Profile::Balanced,
}
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
pub enum FanCurvePU {
CPU,
GPU,
}
impl From<FanCurvePU> for &str {
fn from(pu: FanCurvePU) -> &'static str {
match pu {
FanCurvePU::CPU => "cpu",
FanCurvePU::GPU => "gpu",
}
}
}
impl Default for FanCurvePU {
fn default() -> Self {
Self::CPU
}
}
#[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
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug)]
pub struct FanCurves {
active_curves: Vec<Profile>,
balanced: FanCurve,
performance: FanCurve,
quiet: FanCurve,
}
impl Default for FanCurves {
fn default() -> Self {
let mut curves = Self {
active_curves: 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 {
pub fn is_fan_curves_supported() -> bool {
let mut path = PathBuf::new();
path.push(FAN_CURVE_BASE_PATH);
path.push(FAN_CURVE_ACTIVE_FILE);
path.exists()
}
pub fn update_from_platform(&mut self) {
self.balanced.cpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::CPU);
self.balanced.gpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::GPU);
self.performance.cpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::CPU);
self.performance.gpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::GPU);
self.quiet.cpu = Self::get_fan_curve_from_file(Profile::Quiet, FanCurvePU::CPU);
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] {
&self.active_curves
}
pub fn get_all_fan_curves(&self) -> Vec<FanCurve> {
vec![
self.balanced.clone(),
self.performance.clone(),
self.quiet.clone(),
]
}
pub fn get_active_fan_curves(&self) -> &FanCurve {
match Profile::get_active_profile().unwrap() {
Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance,
Profile::Quiet => &self.quiet,
}
}
pub fn get_fan_curves_for(&self, name: Profile) -> &FanCurve {
match name {
Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance,
Profile::Quiet => &self.quiet,
}
}
fn get_fan_curve_from_file(name: Profile, pu: FanCurvePU) -> String {
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 {
Profile::Balanced => match pu {
FanCurvePU::CPU => &self.balanced.cpu,
FanCurvePU::GPU => &self.balanced.gpu,
},
Profile::Performance => match pu {
FanCurvePU::CPU => &self.balanced.cpu,
FanCurvePU::GPU => &self.balanced.gpu,
},
Profile::Quiet => match pu {
FanCurvePU::CPU => &self.balanced.cpu,
FanCurvePU::GPU => &self.balanced.gpu,
},
}
}
fn set_fan_curve_for_platform(name: Profile, pu: FanCurvePU, curve: &str) {
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()
.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();
}
}

View File

@@ -1,185 +0,0 @@
use rog_fan_curve::{Curve, Fan};
use serde_derive::{Deserialize, Serialize};
use std::io::Write;
use std::{fs::OpenOptions, path::Path, str::FromStr};
#[cfg(feature = "dbus")]
use zvariant_derive::Type;
use crate::{error::ProfileError, AMD_BOOST_PATH, FAN_TYPE_1_PATH, FAN_TYPE_2_PATH};
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Profile {
pub name: String,
pub min_percentage: u8,
pub max_percentage: u8,
pub turbo: bool,
pub fan_preset: FanLevel,
pub fan_curve: String,
}
impl Default for Profile {
fn default() -> Self {
Profile {
name: "new".into(),
min_percentage: 0,
max_percentage: 100,
turbo: false,
fan_preset: FanLevel::Normal,
fan_curve: "".to_string(),
}
}
}
impl Profile {
pub fn new(
name: String,
min_percentage: u8,
max_percentage: u8,
turbo: bool,
fan_preset: FanLevel,
fan_curve: String,
) -> Self {
Profile {
name,
min_percentage,
max_percentage,
turbo,
fan_preset,
fan_curve,
}
}
pub fn get_intel_supported() -> bool {
intel_pstate::PState::new().is_ok()
}
pub fn get_fan_path() -> Result<&'static str, ProfileError> {
if Path::new(FAN_TYPE_1_PATH).exists() {
Ok(FAN_TYPE_1_PATH)
} else if Path::new(FAN_TYPE_2_PATH).exists() {
Ok(FAN_TYPE_2_PATH)
} else {
Err(ProfileError::NotSupported)
}
}
pub fn set_system_pstate(&self) -> Result<(), ProfileError> {
// Set CPU pstate
if let Ok(pstate) = intel_pstate::PState::new() {
pstate.set_min_perf_pct(self.min_percentage)?;
pstate.set_max_perf_pct(self.max_percentage)?;
pstate.set_no_turbo(!self.turbo)?;
} else {
// must be AMD CPU
let mut file = OpenOptions::new()
.write(true)
.open(AMD_BOOST_PATH)
.map_err(|err| ProfileError::Path(AMD_BOOST_PATH.into(), err))?;
let boost = if self.turbo { "1" } else { "0" }; // opposite of Intel
file.write_all(boost.as_bytes())
.map_err(|err| ProfileError::Write(AMD_BOOST_PATH.into(), err))?;
}
Ok(())
}
pub fn set_system_fan_mode(&self) -> Result<(), ProfileError> {
let path = Profile::get_fan_path()?;
let mut fan_ctrl = OpenOptions::new()
.write(true)
.open(path)
.map_err(|err| ProfileError::Path(path.into(), err))?;
fan_ctrl
.write_all(format!("{}\n", <u8>::from(self.fan_preset)).as_bytes())
.map_err(|err| ProfileError::Write(path.into(), err))?;
Ok(())
}
pub fn set_system_fan_curve(&self) -> Result<(), ProfileError> {
if !self.fan_curve.is_empty() {
if let Ok(curve) = Profile::parse_fan_curve(&self.fan_curve) {
use rog_fan_curve::Board;
if let Some(board) = Board::from_board_name() {
curve.apply(board, Fan::Cpu)?;
curve.apply(board, Fan::Gpu)?;
}
}
}
Ok(())
}
pub fn set_system_all(&self) -> Result<(), ProfileError> {
self.set_system_pstate()?;
if self.fan_curve.is_empty() {
self.set_system_fan_mode()?;
} else {
self.set_system_fan_curve()?;
}
Ok(())
}
fn parse_fan_curve(data: &str) -> Result<Curve, String> {
let curve = Curve::from_config_str(data)?;
if let Err(err) = curve.check_safety(Fan::Cpu) {
return Err(format!("Unsafe curve {:?}", err));
}
if let Err(err) = curve.check_safety(Fan::Gpu) {
return Err(format!("Unsafe curve {:?}", err));
}
Ok(curve)
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum FanLevel {
Normal,
Boost,
Silent,
}
impl FromStr for FanLevel {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"normal" => Ok(FanLevel::Normal),
"boost" => Ok(FanLevel::Boost),
"silent" => Ok(FanLevel::Silent),
_ => Err("Invalid fan level"),
}
}
}
impl From<u8> for FanLevel {
fn from(n: u8) -> Self {
match n {
0 => FanLevel::Normal,
1 => FanLevel::Boost,
2 => FanLevel::Silent,
_ => FanLevel::Normal,
}
}
}
impl From<FanLevel> for u8 {
fn from(n: FanLevel) -> Self {
match n {
FanLevel::Normal => 0,
FanLevel::Boost => 1,
FanLevel::Silent => 2,
}
}
}
impl From<&FanLevel> for u8 {
fn from(n: &FanLevel) -> Self {
match n {
FanLevel::Normal => 0,
FanLevel::Boost => 1,
FanLevel::Silent => 2,
}
}
}