1use anyhow::{Result, anyhow};
32use serde::{Deserialize, Serialize};
33use std::{
34 ffi::OsString,
35 fs::{read_dir, read_to_string},
36 path::{Path, PathBuf},
37 str::FromStr,
38};
39
40use crate::traits::ToJson;
41
42#[derive(Debug, Deserialize, Serialize, Clone)]
45pub struct CpuFreq {
46 pub policy: Vec<Policy>,
49
50 pub boost: Option<bool>,
52}
53
54const CPU_FREQ_DIR: &str = "/sys/devices/system/cpu/cpufreq/";
55
56impl CpuFreq {
57 pub fn new() -> Result<Self> {
58 let pth = Path::new(CPU_FREQ_DIR);
59 if !pth.exists() {
60 return Err(anyhow!(
61 "The directory '{CPU_FREQ_DIR}' was not found. Is \
62 your system able to manage CPU frequencies for sure?",
63 ));
64 }
65
66 let boost = read_to_string(pth.join("boost"))
67 .ok()
68 .map(|boost| &boost == "1");
69
70 let mut policy = Vec::new();
71 for dir in read_dir(CPU_FREQ_DIR)? {
72 let dir = dir?;
73 let fname = dir.file_name();
74 if fname.to_string_lossy().contains("policy") {
75 policy.push(Policy::new(fname)?);
76 }
77 }
78
79 Ok(Self { policy, boost })
80 }
81}
82
83impl ToJson for CpuFreq {}
84
85#[derive(Debug, Deserialize, Serialize, Default, Clone)]
88pub struct Policy {
89 pub bios_limit: Option<u32>,
91
92 pub cpb: Option<bool>,
94
95 pub cpu_max_freq: Option<u32>,
97
98 pub cpu_min_freq: Option<u32>,
100
101 pub cpuinfo_transition_latency: Option<bool>,
103
104 pub scaling_available_frequencies: Option<Vec<u32>>,
106
107 pub scaling_available_governors: Option<Vec<String>>,
109
110 pub scaling_cur_freq: Option<u32>,
112
113 pub scaling_driver: Option<String>,
115
116 pub scaling_governor: Option<String>,
118
119 pub scaling_max_freq: Option<u32>,
120 pub scaling_min_freq: Option<u32>,
121 pub scaling_setspeed: Option<String>,
122}
123
124impl Policy {
125 fn get_data<T>(data: Option<String>) -> Option<T>
126 where
127 T: FromStr,
128 {
129 data.and_then(|d| d.trim().parse::<T>().ok())
130 }
131
132 pub fn new(policy: OsString) -> Result<Self> {
133 let dir = Path::new(CPU_FREQ_DIR);
134 let tgt = dir.join(policy);
135 if !dir.exists() || !tgt.exists() {
136 return Err(anyhow!("Directory {} does not exists!", dir.display()));
137 }
138
139 let read = |path: &PathBuf, name: &str| read_to_string(path.join(name));
140 let get_bool = |num: Option<u8>| num.map(|n| n != 0);
141
142 Ok(Self {
143 bios_limit: Self::get_data(read(&tgt, "bios_limit").ok()),
144 cpb: get_bool(Self::get_data(read(&tgt, "cpb").ok())),
145 cpu_max_freq: Self::get_data(read(&tgt, "cpuinfo_max_freq").ok()),
146 cpu_min_freq: Self::get_data(read(&tgt, "cpuinfo_min_freq").ok()),
147 cpuinfo_transition_latency: get_bool(
148 read(&tgt, "cpuinfo_transition_latency")
149 .map(|d| d.trim().parse::<u8>().unwrap_or(0))
150 .ok(),
151 ),
152 scaling_available_frequencies: read(&tgt, "scaling_available_frequencies")
153 .map(|d| {
154 d.trim()
155 .split_whitespace()
156 .map(|freq| freq.parse::<u32>().ok())
157 .filter(|freq| freq.is_some())
158 .map(|freq| freq.unwrap())
159 .collect::<Vec<_>>()
160 })
161 .ok(),
162 scaling_available_governors: read(&tgt, "scaling_available_governors")
163 .map(|d| {
164 d.trim()
165 .split_whitespace()
166 .map(|gov| gov.to_string())
167 .collect::<Vec<_>>()
168 })
169 .ok(),
170 scaling_cur_freq: Self::get_data(read(&tgt, "scaling_cur_freq").ok()),
171 scaling_driver: read(&tgt, "scaling_driver")
172 .ok()
173 .map(|s| s.trim().to_string()),
174 scaling_governor: read(&tgt, "scaling_governor")
175 .ok()
176 .map(|s| s.trim().to_string()),
177 scaling_max_freq: Self::get_data(read(&tgt, "scaling_max_freq").ok()),
178 scaling_min_freq: Self::get_data(read(&tgt, "scaling_min_freq").ok()),
179 scaling_setspeed: read(&tgt, "scaling_setspeed")
180 .ok()
181 .map(|s| s.trim().to_string()),
182 })
183 }
184}