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 pub pwm_min: Option<u32>,
20 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 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 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}