use crate::{
error::{ErrorContext, ErrorKind},
sysfs::SysFS,
Result,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
#[derive(Clone, Debug)]
pub struct HwMon {
path: PathBuf,
}
impl HwMon {
pub fn new_from_path(path: PathBuf) -> Result<Self> {
let hw_mon = Self { path };
hw_mon.read_file("name")?;
Ok(hw_mon)
}
fn read_temp(&self, file: &str) -> Result<f32> {
let temp_str = self.read_file(file)?;
Ok(temp_str
.trim()
.parse::<f32>()
.context("Invalid temperature value (driver bug?)")?
/ 1000.0)
}
pub fn get_temps(&self) -> HashMap<String, Temperature> {
let mut temps = HashMap::new();
let mut i = 1;
while let Ok(current) = self.read_temp(&format!("temp{i}_input")) {
let temperature = Temperature {
current: Some(current),
crit: self.read_temp(&format!("temp{i}_crit")).ok(),
crit_hyst: self.read_temp(&format!("temp{i}_crit_hyst")).ok(),
};
match self.read_file(&format!("temp{i}_label")) {
Ok(label) => {
temps.insert(label, temperature);
}
Err(_) => {
temps.insert(i.to_string(), temperature);
break;
}
}
i += 1;
}
temps
}
fn read_clockspeed(&self, file: &str) -> Result<u64> {
let raw_clockspeed = self.read_file(file)?;
Ok(raw_clockspeed
.parse::<u64>()
.context("Unexpected GPU clockspeed (driver bug?)")?
/ 1000000)
}
pub fn get_gpu_clockspeed(&self) -> Result<u64> {
self.read_clockspeed("freq1_input")
}
pub fn get_vram_clockspeed(&self) -> Result<u64> {
self.read_clockspeed("freq2_input")
}
fn read_power(&self, file: &str) -> Result<f64> {
let raw_power = self.read_file(file)?;
Ok(raw_power
.parse::<f64>()
.context("Unexpected power value (driver bug?)")?
/ 1000000.0)
}
pub fn get_power_average(&self) -> Result<f64> {
self.read_power("power1_average")
}
pub fn get_power_input(&self) -> Result<f64> {
self.read_power("power1_input")
}
pub fn get_power_cap(&self) -> Result<f64> {
self.read_power("power1_cap")
}
pub fn set_power_cap(&self, cap: f64) -> Result<()> {
let value = (cap * 1000000.0).round() as i64;
self.write_file("power1_cap", value.to_string())
}
pub fn get_power_cap_max(&self) -> Result<f64> {
self.read_power("power1_cap_max")
}
pub fn get_power_cap_min(&self) -> Result<f64> {
self.read_power("power1_cap_min")
}
pub fn get_power_cap_default(&self) -> Result<f64> {
self.read_power("power1_cap_default")
}
pub fn get_fan_pwm(&self) -> Result<u8> {
let pwm = self.read_file("pwm1")?;
pwm.parse().context("Unexpected PWM (driver bug?)")
}
pub fn set_fan_pwm(&self, pwm: u8) -> Result<()> {
self.write_file("pwm1", pwm.to_string())
}
pub fn get_fan_current(&self) -> Result<u32> {
let s = self.read_file("fan1_input")?;
s.parse().context("Unexpected fan1_input (driver bug?)")
}
pub fn get_fan_max(&self) -> Result<u32> {
let s = self.read_file("fan1_max")?;
s.parse().context("Unexpected fan1_max (driver bug?)")
}
pub fn get_fan_min(&self) -> Result<u32> {
let s = self.read_file("fan1_min")?;
s.parse().context("Unexpected fan1_min (driver bug?)")
}
pub fn get_fan_target(&self) -> Result<u32> {
self.read_file("fan1_target")
.map(|s| s.parse().expect("Unexpected fan1_target (driver bug?)"))
}
pub fn set_fan_target(&self, target: u32) -> Result<()> {
self.write_file("fan1_target", target.to_string())?;
Ok(())
}
pub fn get_fan_control_method(&self) -> Result<FanControlMethod> {
self.read_file("pwm1_enable").and_then(|pwm1_enable| {
let repr = pwm1_enable
.parse()
.context("Unexpected pwm1_enable (driver bug?)")?;
FanControlMethod::from_repr(repr).ok_or_else(|| {
ErrorKind::Unsupported(
"Unexpected pwm1_enable (driver bug or unsupported?)".to_owned(),
)
.into()
})
})
}
pub fn set_fan_control_method(&self, method: FanControlMethod) -> Result<()> {
let repr = method as u32;
self.write_file("pwm1_enable", repr.to_string())
}
pub fn get_gpu_voltage(&self) -> Result<u64> {
self.read_file_parsed("in0_input")
}
pub fn get_northbridge_voltage(&self) -> Result<u64> {
self.read_file_parsed("in1_input")
}
}
impl SysFS for HwMon {
fn get_path(&self) -> &Path {
&self.path
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Temperature {
pub current: Option<f32>,
pub crit: Option<f32>,
pub crit_hyst: Option<f32>,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
pub enum FanControlMethod {
None = 0,
Manual = 1,
Auto = 2,
}
impl FanControlMethod {
pub fn from_repr(repr: u32) -> Option<Self> {
match repr {
0 => Some(Self::None),
1 => Some(Self::Manual),
2 => Some(Self::Auto),
_ => None,
}
}
}