1use crate::{
3 error::{ErrorContext, ErrorKind},
4 sysfs::SysFS,
5 Result,
6};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{
10 collections::HashMap,
11 path::{Path, PathBuf},
12};
13
14#[derive(Clone, Debug)]
17pub struct HwMon {
18 path: PathBuf,
19}
20
21impl HwMon {
22 pub fn new_from_path(path: PathBuf) -> Result<Self> {
25 let hw_mon = Self { path };
26 hw_mon.read_file("name")?;
27 Ok(hw_mon)
28 }
29
30 fn read_temp(&self, file: &str) -> Result<f32> {
31 let temp_str = self.read_file(file)?;
32 Ok(temp_str
33 .trim()
34 .parse::<f32>()
35 .context("Invalid temperature value (driver bug?)")?
36 / 1000.0)
37 }
38
39 pub fn get_temps(&self) -> HashMap<String, Temperature> {
41 let mut temps = HashMap::new();
42
43 let mut i = 1;
44
45 while let Ok(current) = self.read_temp(&format!("temp{i}_input")) {
46 let temperature = Temperature {
47 current: Some(current),
48 crit: self.read_temp(&format!("temp{i}_crit")).ok(),
49 crit_hyst: self.read_temp(&format!("temp{i}_crit_hyst")).ok(),
50 };
51
52 match self.read_file(format!("temp{i}_label")) {
53 Ok(label) => {
54 temps.insert(label, temperature);
55 }
56 Err(_) => {
57 temps.insert(i.to_string(), temperature);
58 break;
59 }
60 }
61
62 i += 1;
63 }
64
65 temps
66 }
67
68 fn read_clockspeed(&self, file: &str) -> Result<u64> {
69 let raw_clockspeed = self.read_file(file)?;
70 Ok(raw_clockspeed
71 .parse::<u64>()
72 .context("Unexpected GPU clockspeed (driver bug?)")?
73 / 1000000)
74 }
75
76 pub fn get_gpu_clockspeed(&self) -> Result<u64> {
78 self.read_clockspeed("freq1_input")
79 }
80
81 pub fn get_vram_clockspeed(&self) -> Result<u64> {
83 self.read_clockspeed("freq2_input")
84 }
85
86 fn read_power(&self, file: &str) -> Result<f64> {
87 let raw_power = self.read_file(file)?;
88 Ok(raw_power
89 .parse::<f64>()
90 .context("Unexpected power value (driver bug?)")?
91 / 1000000.0)
92 }
93
94 pub fn get_power_average(&self) -> Result<f64> {
96 self.read_power("power1_average")
97 }
98
99 pub fn get_power_input(&self) -> Result<f64> {
101 self.read_power("power1_input")
102 }
103
104 pub fn get_power_cap(&self) -> Result<f64> {
106 self.read_power("power1_cap")
107 }
108
109 pub fn set_power_cap(&self, cap: f64) -> Result<()> {
111 let value = (cap * 1000000.0).round() as i64;
112 self.write_file("power1_cap", value.to_string())
113 }
114
115 pub fn get_power_cap_max(&self) -> Result<f64> {
117 self.read_power("power1_cap_max")
118 }
119
120 pub fn get_power_cap_min(&self) -> Result<f64> {
122 self.read_power("power1_cap_min")
123 }
124
125 pub fn get_power_cap_default(&self) -> Result<f64> {
127 self.read_power("power1_cap_default")
128 }
129
130 pub fn get_fan_pwm(&self) -> Result<u8> {
132 let pwm = self.read_file("pwm1")?;
133 pwm.parse().context("Unexpected PWM (driver bug?)")
134 }
135
136 pub fn get_fan_min_pwm(&self) -> Result<u8> {
138 let pwm = self.read_file("pwm1_min")?;
139 pwm.parse().context("Unexpected PWM (driver bug?)")
140 }
141
142 pub fn get_fan_max_pwm(&self) -> Result<u8> {
144 let pwm = self.read_file("pwm1_max")?;
145 pwm.parse().context("Unexpected PWM (driver bug?)")
146 }
147
148 pub fn set_fan_pwm(&self, pwm: u8) -> Result<()> {
150 self.write_file("pwm1", pwm.to_string())
151 }
152
153 pub fn get_fan_current(&self) -> Result<u32> {
155 let s = self.read_file("fan1_input")?;
156 s.parse().context("Unexpected fan1_input (driver bug?)")
157 }
158
159 pub fn get_fan_max(&self) -> Result<u32> {
161 let s = self.read_file("fan1_max")?;
162 s.parse().context("Unexpected fan1_max (driver bug?)")
163 }
164
165 pub fn get_fan_min(&self) -> Result<u32> {
167 let s = self.read_file("fan1_min")?;
168 s.parse().context("Unexpected fan1_min (driver bug?)")
169 }
170
171 pub fn get_fan_target(&self) -> Result<u32> {
173 self.read_file("fan1_target")
174 .map(|s| s.parse().expect("Unexpected fan1_target (driver bug?)"))
175 }
176
177 pub fn set_fan_target(&self, target: u32) -> Result<()> {
179 self.write_file("fan1_target", target.to_string())?;
180 Ok(())
181 }
182
183 pub fn get_fan_control_method(&self) -> Result<FanControlMethod> {
185 self.read_file("pwm1_enable").and_then(|pwm1_enable| {
186 let repr = pwm1_enable
187 .parse()
188 .context("Unexpected pwm1_enable (driver bug?)")?;
189 FanControlMethod::from_repr(repr).ok_or_else(|| {
190 ErrorKind::Unsupported(
191 "Unexpected pwm1_enable (driver bug or unsupported?)".to_owned(),
192 )
193 .into()
194 })
195 })
196 }
197
198 pub fn set_fan_control_method(&self, method: FanControlMethod) -> Result<()> {
200 let repr = method as u32;
201 self.write_file("pwm1_enable", repr.to_string())
202 }
203
204 pub fn get_gpu_voltage(&self) -> Result<u64> {
206 self.read_file_parsed("in0_input")
207 }
208
209 pub fn get_northbridge_voltage(&self) -> Result<u64> {
211 self.read_file_parsed("in1_input")
212 }
213}
214
215impl SysFS for HwMon {
216 fn get_path(&self) -> &Path {
217 &self.path
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq)]
223#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
224pub struct Temperature {
225 pub current: Option<f32>,
227 pub crit: Option<f32>,
229 pub crit_hyst: Option<f32>,
231}
232
233#[derive(Debug, Clone, Copy)]
235#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
236#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
237pub enum FanControlMethod {
238 None = 0,
240 Manual = 1,
242 Auto = 2,
244}
245
246impl FanControlMethod {
247 pub fn from_repr(repr: u32) -> Option<Self> {
249 match repr {
250 0 => Some(Self::None),
251 1 => Some(Self::Manual),
252 2 => Some(Self::Auto),
253 _ => None,
254 }
255 }
256}