amdmond_lib/
lib.rs

1pub mod errors;
2
3use amdgpu::hw_mon::HwMon;
4use amdgpu::utils::load_temp_inputs;
5use amdgpu::{
6    TempInput, PULSE_WIDTH_MODULATION, PULSE_WIDTH_MODULATION_MAX, PULSE_WIDTH_MODULATION_MIN,
7};
8use amdgpu_config::fan;
9
10use crate::errors::AmdMonError;
11
12pub type Result<T> = std::result::Result<T, AmdMonError>;
13
14pub struct AmdMon {
15    temp_input: Option<TempInput>,
16    inputs: Vec<String>,
17    hw_mon: HwMon,
18    /// Minimal modulation (between 0-255)
19    pub pwm_min: Option<u32>,
20    /// Maximal modulation (between 0-255)
21    pub pwm_max: Option<u32>,
22}
23
24impl std::ops::Deref for AmdMon {
25    type Target = HwMon;
26
27    fn deref(&self) -> &Self::Target {
28        &self.hw_mon
29    }
30}
31
32impl AmdMon {
33    pub fn wrap_all(mons: Vec<HwMon>, config: &fan::Config) -> Vec<Self> {
34        mons.into_iter()
35            .map(|hw_mon| Self::wrap(hw_mon, config))
36            .collect()
37    }
38
39    pub fn wrap(hw_mon: HwMon, config: &fan::Config) -> Self {
40        Self {
41            temp_input: config.temp_input().cloned(),
42            inputs: load_temp_inputs(&hw_mon),
43            hw_mon,
44            pwm_min: None,
45            pwm_max: None,
46        }
47    }
48
49    /// Returns name and gpu temperature percent of maximum GPU temperature
50    /// range
51    pub fn gpu_temp(&self) -> Vec<(&String, Result<f64>)> {
52        self.inputs
53            .iter()
54            .map(|name| {
55                let temp = self
56                    .read_gpu_temp(name.as_str())
57                    .map(|temp| temp as f64 / 1000f64);
58                (name, temp)
59            })
60            .collect()
61    }
62
63    /// GPU usage percent
64    pub fn gpu_usage(&self) -> Result<f64> {
65        if let Some(input) = self.temp_input.as_ref() {
66            let value = self.read_gpu_usage(&input.as_string())?;
67            return Ok(value as f64 / 1000f64);
68        }
69        Ok(0.0)
70    }
71
72    pub fn gpu_temp_of(&self, input_idx: usize) -> Option<(&String, Result<f64>)> {
73        self.inputs.get(input_idx).map(|name| {
74            let temp = self
75                .read_gpu_temp(name.as_str())
76                .map(|temp| temp as f64 / 1000f64);
77            (name, temp)
78        })
79    }
80
81    pub fn read_gpu_temp(&self, name: &str) -> Result<u64> {
82        let value = self
83            .hw_mon_read(name)?
84            .parse::<u64>()
85            .map_err(AmdMonError::NonIntTemp)?;
86        Ok(value)
87    }
88
89    pub fn gpu_usage_of(&self, input_idx: usize) -> Option<(&String, Result<f64>)> {
90        self.inputs.get(input_idx).map(|name| {
91            let usage = self.read_gpu_usage(name).map(|usage| usage as f64 / 100f64);
92            (name, usage)
93        })
94    }
95
96    pub fn read_gpu_usage(&self, name: &str) -> Result<u16> {
97        let value = self
98            .hw_mon_read(name)?
99            .parse()
100            .map_err(AmdMonError::NonIntTemp)?;
101        Ok(value)
102    }
103
104    pub fn pwm(&self) -> Result<u32> {
105        let value = self
106            .hw_mon_read(PULSE_WIDTH_MODULATION)?
107            .parse()
108            .map_err(AmdMonError::NonIntPwm)?;
109        Ok(value)
110    }
111
112    pub fn pwm_min(&mut self) -> u32 {
113        if self.pwm_min.is_none() {
114            self.pwm_min = Some(self.value_or(PULSE_WIDTH_MODULATION_MIN, 0));
115        };
116        self.pwm_min.unwrap_or_default()
117    }
118
119    pub fn pwm_max(&mut self) -> u32 {
120        if self.pwm_max.is_none() {
121            self.pwm_max = Some(self.value_or(PULSE_WIDTH_MODULATION_MAX, 255));
122        };
123        self.pwm_max.unwrap_or(255)
124    }
125
126    pub fn max_gpu_temp(&self) -> Result<f64> {
127        if let Some(input) = self.temp_input.as_ref() {
128            let value = self.read_gpu_temp(&input.as_string())?;
129            return Ok(value as f64 / 1000f64);
130        }
131        let mut results = Vec::with_capacity(self.inputs.len());
132        for name in self.inputs.iter() {
133            results.push(self.read_gpu_temp(name).unwrap_or(0));
134        }
135        results.sort_unstable();
136        let value = results
137            .last()
138            .copied()
139            .map(|temp| temp as f64 / 1000f64)
140            .ok_or(AmdMonError::EmptyTempSet)?;
141        Ok(value)
142    }
143}