From c5f968fcf93639ad81a89627c537cc94bab7be81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Sat, 31 Jul 2021 22:42:58 +0200 Subject: [PATCH] Move hw_mod to own file. --- src/hw_mon.rs | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 213 +------------------------------------------------- 2 files changed, 210 insertions(+), 212 deletions(-) create mode 100644 src/hw_mon.rs diff --git a/src/hw_mon.rs b/src/hw_mon.rs new file mode 100644 index 0000000..39cfab5 --- /dev/null +++ b/src/hw_mon.rs @@ -0,0 +1,209 @@ +use crate::config::Card; +use crate::io_err::{invalid_input, not_found}; +use crate::{linear_map, AmdFanError, HwMon, HW_MON_DIR, ROOT_DIR}; + +/// pulse width modulation fan control minimum level (0) +const PULSE_WIDTH_MODULATION_MIN: &str = "pwm1_min"; + +/// pulse width modulation fan control maximum level (255) +const PULSE_WIDTH_MODULATION_MAX: &str = "pwm1_max"; + +/// pulse width modulation fan level (0-255) +const PULSE_WIDTH_MODULATION: &str = "pwm1"; + +/// pulse width modulation fan control method (0: no fan speed control, 1: manual fan speed control using pwm interface, 2: automatic fan speed control) +const PULSE_WIDTH_MODULATION_MODE: &str = "pwm1_enable"; + +// static PULSE_WIDTH_MODULATION_DISABLED: &str = "0"; +const PULSE_WIDTH_MODULATION_AUTO: &str = "2"; +const PULSE_WIDTH_MODULATION_MANUAL: &str = "1"; + +impl HwMon { + pub fn new(card: &Card, name: &str) -> Self { + Self { + card: card.clone(), + name: String::from(name), + pwm_min: None, + pwm_max: None, + temp_inputs: load_temp_inputs(&card, name), + } + } + + pub fn max_gpu_temp(&self) -> std::io::Result { + let mut results = Vec::with_capacity(self.temp_inputs.len()); + for name in self.temp_inputs.iter() { + results.push(self.read_gpu_temp(name).unwrap_or(0)); + } + results.sort(); + results + .last() + .copied() + .map(|temp| temp as f64 / 1000f64) + .ok_or_else(invalid_input) + } + + pub fn gpu_temp(&self) -> Vec<(String, std::io::Result)> { + self.temp_inputs + .clone() + .into_iter() + .map(|name| { + let temp = self + .read_gpu_temp(name.as_str()) + .map(|temp| temp as f64 / 1000f64); + (name, temp) + }) + .collect() + } + + fn read_gpu_temp(&self, name: &str) -> std::io::Result { + self.read(name)?.parse::().map_err(|e| { + log::warn!("Read from gpu monitor failed. Invalid temperature. {}", e); + invalid_input() + }) + } + + #[inline] + pub(crate) fn name(&self) -> std::io::Result { + self.read("name") + } + + pub fn pwm_min(&mut self) -> u32 { + if self.pwm_min.is_none() { + self.pwm_min = Some(self.value_or(PULSE_WIDTH_MODULATION_MIN, 0)); + }; + self.pwm_min.unwrap_or_default() + } + + pub fn pwm_max(&mut self) -> u32 { + if self.pwm_max.is_none() { + self.pwm_max = Some(self.value_or(PULSE_WIDTH_MODULATION_MAX, 255)); + }; + self.pwm_max.unwrap_or(255) + } + + pub fn pwm(&self) -> std::io::Result { + self.read(PULSE_WIDTH_MODULATION)?.parse().map_err(|_e| { + log::warn!("Read from gpu monitor failed. Invalid pwm value"); + invalid_input() + }) + } + + pub fn is_fan_manual(&self) -> bool { + self.read(PULSE_WIDTH_MODULATION_MODE) + .map(|s| s.as_str() == PULSE_WIDTH_MODULATION_MANUAL) + .unwrap_or_default() + } + + pub fn is_fan_automatic(&self) -> bool { + self.read(PULSE_WIDTH_MODULATION_MODE) + .map(|s| s.as_str() == PULSE_WIDTH_MODULATION_AUTO) + .unwrap_or_default() + } + + #[inline] + pub fn is_amd(&self) -> bool { + std::fs::read_to_string(format!("{}/{}/device/vendor", ROOT_DIR, self.card)) + .map_err(|_| AmdFanError::FailedReadVendor) + .map(|vendor| vendor.trim() == "0x1002") + .unwrap_or_default() + } + + #[inline] + pub fn name_is_amd(&self) -> bool { + self.name().ok().filter(|s| s.trim() == "amdgpu").is_some() + } + + pub fn set_manual(&self) -> std::io::Result<()> { + self.write("pwm1_enable", 1) + } + + pub fn set_automatic(&self) -> std::io::Result<()> { + self.write("pwm1_enable", 2) + } + + pub fn set_pwm(&self, value: u32) -> std::io::Result<()> { + if self.is_fan_automatic() { + self.set_manual()?; + } + self.write("pwm1", value as u64) + } + + pub fn set_speed(&mut self, speed: f64) -> std::io::Result<()> { + let min = self.pwm_min() as f64; + let max = self.pwm_max() as f64; + let pwm = linear_map(speed, 0f64, 100f64, min, max).round() as u32; + self.set_pwm(pwm) + } + + fn read(&self, name: &str) -> std::io::Result { + std::fs::read_to_string(self.path(name)).map(|s| String::from(s.trim())) + } + + fn write(&self, name: &str, value: u64) -> std::io::Result<()> { + std::fs::write(self.path(name), format!("{}", value))?; + Ok(()) + } + + fn path(&self, name: &str) -> std::path::PathBuf { + self.mon_dir().join(name) + } + + fn mon_dir(&self) -> std::path::PathBuf { + hw_mon_dir_path(&self.card, &self.name) + } + + #[inline] + fn value_or(&self, name: &str, fallback: R) -> R { + self.read(name) + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(fallback) + } +} + +fn load_temp_inputs(card: &Card, name: &str) -> Vec { + let dir = match std::fs::read_dir(hw_mon_dir_path(card, name)) { + Ok(d) => d, + _ => return vec![], + }; + dir.filter_map(|f| f.ok()) + .filter_map(|f| { + f.file_name() + .to_str() + .filter(|s| s.starts_with("temp") && s.ends_with("_input")) + .map(String::from) + }) + .collect() +} + +#[inline] +fn hw_mon_dirs_path(card: &Card) -> std::path::PathBuf { + std::path::PathBuf::new() + .join(ROOT_DIR) + .join(card.to_string()) + .join(HW_MON_DIR) +} + +#[inline] +fn hw_mon_dir_path(card: &Card, name: &str) -> std::path::PathBuf { + hw_mon_dirs_path(&card).join(name) +} + +pub(crate) fn open_hw_mon(card: Card) -> std::io::Result { + let read_path = hw_mon_dirs_path(&card); + let entries = std::fs::read_dir(read_path)?; + let name = entries + .filter_map(|entry| entry.ok()) + .filter_map(|entry| { + entry + .file_name() + .as_os_str() + .to_str() + .filter(|name| name.starts_with("hwmon")) + .map(String::from) + }) + .take(1) + .last() + .ok_or_else(not_found)?; + Ok(HwMon::new(&card, &name)) +} diff --git a/src/main.rs b/src/main.rs index 510b647..8a6fcff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod config; +mod hw_mon; mod io_err; mod monitor; @@ -59,218 +60,6 @@ pub struct HwMon { temp_inputs: Vec, } -mod hw_mon { - use crate::config::Card; - use crate::io_err::{invalid_input, not_found}; - use crate::{linear_map, AmdFanError, HwMon, HW_MON_DIR, ROOT_DIR}; - - /// pulse width modulation fan control minimum level (0) - const PULSE_WIDTH_MODULATION_MIN: &str = "pwm1_min"; - - /// pulse width modulation fan control maximum level (255) - const PULSE_WIDTH_MODULATION_MAX: &str = "pwm1_max"; - - /// pulse width modulation fan level (0-255) - const PULSE_WIDTH_MODULATION: &str = "pwm1"; - - /// pulse width modulation fan control method (0: no fan speed control, 1: manual fan speed control using pwm interface, 2: automatic fan speed control) - const PULSE_WIDTH_MODULATION_MODE: &str = "pwm1_enable"; - - // static PULSE_WIDTH_MODULATION_DISABLED: &str = "0"; - const PULSE_WIDTH_MODULATION_AUTO: &str = "2"; - const PULSE_WIDTH_MODULATION_MANUAL: &str = "1"; - - impl HwMon { - pub fn new(card: &Card, name: &str) -> Self { - Self { - card: card.clone(), - name: String::from(name), - pwm_min: None, - pwm_max: None, - temp_inputs: load_temp_inputs(&card, name), - } - } - - pub fn max_gpu_temp(&self) -> std::io::Result { - let mut results = Vec::with_capacity(self.temp_inputs.len()); - for name in self.temp_inputs.iter() { - results.push(self.read_gpu_temp(name).unwrap_or(0)); - } - results.sort(); - results - .last() - .copied() - .map(|temp| temp as f64 / 1000f64) - .ok_or_else(invalid_input) - } - - pub fn gpu_temp(&self) -> Vec<(String, std::io::Result)> { - self.temp_inputs - .clone() - .into_iter() - .map(|name| { - let temp = self - .read_gpu_temp(name.as_str()) - .map(|temp| temp as f64 / 1000f64); - (name, temp) - }) - .collect() - } - - fn read_gpu_temp(&self, name: &str) -> std::io::Result { - self.read(name)?.parse::().map_err(|e| { - log::warn!("Read from gpu monitor failed. Invalid temperature. {}", e); - invalid_input() - }) - } - - #[inline] - pub(crate) fn name(&self) -> std::io::Result { - self.read("name") - } - - pub fn pwm_min(&mut self) -> u32 { - if self.pwm_min.is_none() { - self.pwm_min = Some(self.value_or(PULSE_WIDTH_MODULATION_MIN, 0)); - }; - self.pwm_min.unwrap_or_default() - } - - pub fn pwm_max(&mut self) -> u32 { - if self.pwm_max.is_none() { - self.pwm_max = Some(self.value_or(PULSE_WIDTH_MODULATION_MAX, 255)); - }; - self.pwm_max.unwrap_or(255) - } - - pub fn pwm(&self) -> std::io::Result { - self.read(PULSE_WIDTH_MODULATION)?.parse().map_err(|_e| { - log::warn!("Read from gpu monitor failed. Invalid pwm value"); - invalid_input() - }) - } - - pub fn is_fan_manual(&self) -> bool { - self.read(PULSE_WIDTH_MODULATION_MODE) - .map(|s| s.as_str() == PULSE_WIDTH_MODULATION_MANUAL) - .unwrap_or_default() - } - - pub fn is_fan_automatic(&self) -> bool { - self.read(PULSE_WIDTH_MODULATION_MODE) - .map(|s| s.as_str() == PULSE_WIDTH_MODULATION_AUTO) - .unwrap_or_default() - } - - #[inline] - pub fn is_amd(&self) -> bool { - std::fs::read_to_string(format!("{}/{}/device/vendor", ROOT_DIR, self.card)) - .map_err(|_| AmdFanError::FailedReadVendor) - .map(|vendor| vendor.trim() == "0x1002") - .unwrap_or_default() - } - - #[inline] - pub fn name_is_amd(&self) -> bool { - self.name().ok().filter(|s| s.trim() == "amdgpu").is_some() - } - - pub fn set_manual(&self) -> std::io::Result<()> { - self.write("pwm1_enable", 1) - } - - pub fn set_automatic(&self) -> std::io::Result<()> { - self.write("pwm1_enable", 2) - } - - pub fn set_pwm(&self, value: u32) -> std::io::Result<()> { - if self.is_fan_automatic() { - self.set_manual()?; - } - self.write("pwm1", value as u64) - } - - pub fn set_speed(&mut self, speed: f64) -> std::io::Result<()> { - let min = self.pwm_min() as f64; - let max = self.pwm_max() as f64; - let pwm = linear_map(speed, 0f64, 100f64, min, max).round() as u32; - self.set_pwm(pwm) - } - - fn read(&self, name: &str) -> std::io::Result { - std::fs::read_to_string(self.path(name)).map(|s| String::from(s.trim())) - } - - fn write(&self, name: &str, value: u64) -> std::io::Result<()> { - std::fs::write(self.path(name), format!("{}", value))?; - Ok(()) - } - - fn path(&self, name: &str) -> std::path::PathBuf { - self.mon_dir().join(name) - } - - fn mon_dir(&self) -> std::path::PathBuf { - hw_mon_dir_path(&self.card, &self.name) - } - - #[inline] - fn value_or(&self, name: &str, fallback: R) -> R { - self.read(name) - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(fallback) - } - } - - fn load_temp_inputs(card: &Card, name: &str) -> Vec { - let dir = match std::fs::read_dir(hw_mon_dir_path(card, name)) { - Ok(d) => d, - _ => return vec![], - }; - dir.filter_map(|f| f.ok()) - .filter_map(|f| { - f.file_name() - .to_str() - .filter(|s| s.starts_with("temp") && s.ends_with("_input")) - .map(String::from) - }) - .collect() - } - - #[inline] - fn hw_mon_dirs_path(card: &Card) -> std::path::PathBuf { - std::path::PathBuf::new() - .join(ROOT_DIR) - .join(card.to_string()) - .join(HW_MON_DIR) - } - - #[inline] - fn hw_mon_dir_path(card: &Card, name: &str) -> std::path::PathBuf { - hw_mon_dirs_path(&card).join(name) - } - - pub(crate) fn open_hw_mon(card: Card) -> std::io::Result { - let read_path = hw_mon_dirs_path(&card); - let entries = std::fs::read_dir(read_path)?; - let name = entries - .filter_map(|entry| entry.ok()) - .filter_map(|entry| { - entry - .file_name() - .as_os_str() - .to_str() - .filter(|name| name.starts_with("hwmon")) - .map(String::from) - }) - .take(1) - .last() - .ok_or_else(not_found)?; - Ok(HwMon::new(&card, &name)) - } -} - pub enum FanMode { Manual, Automatic,