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