ferrix_lib/
cpu_freq.rs

1/* cpu_freq.rs
2 *
3 * Copyright 2025 Michail Krasnov <mskrasnov07@ya.ru>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21//! Get information about CPU frequency
22
23use anyhow::{Result, anyhow};
24use serde::{Deserialize, Serialize};
25use std::{
26    ffi::OsString,
27    fs::{read_dir, read_to_string},
28    path::{Path, PathBuf},
29};
30
31use crate::traits::ToJson;
32
33#[derive(Debug, Deserialize, Serialize, Clone)]
34pub struct CpuFreq {
35    pub policy: Vec<Policy>,
36    pub boost: Option<bool>,
37}
38
39const CPU_FREQ_DIR: &str = "/sys/devices/system/cpu/cpufreq/";
40
41impl CpuFreq {
42    pub fn new() -> Result<Self> {
43        let pth = Path::new(CPU_FREQ_DIR);
44        let boost = match read_to_string(pth.join("boost")).ok() {
45            Some(boost) => Some(&boost == "1"),
46            None => None,
47        };
48
49        let mut policy = Vec::new();
50        for dir in read_dir(CPU_FREQ_DIR)? {
51            let dir = dir?;
52            let fname = dir.file_name();
53            if fname.to_string_lossy().contains("policy") {
54                policy.push(Policy::new(fname)?);
55            }
56        }
57
58        Ok(Self { policy, boost })
59    }
60}
61
62impl ToJson for CpuFreq {}
63
64#[derive(Debug, Deserialize, Serialize, Default, Clone)]
65pub struct Policy {
66    pub bios_limit: Option<u32>,
67    pub boost: Option<bool>,
68    pub cpb: Option<bool>,
69    pub cpu_max_freq: Option<u32>,
70    pub cpu_min_freq: Option<u32>,
71    pub cpuinfo_transition_latency: Option<bool>,
72    pub scaling_available_frequencies: Option<Vec<u32>>,
73    pub scaling_available_governors: Option<String>,
74    pub scaling_cur_freq: Option<u32>,
75    pub scaling_driver: Option<String>,
76    pub scaling_governor: Option<String>,
77    pub scaling_max_freq: Option<u32>,
78    pub scaling_min_freq: Option<u32>,
79    pub scaling_setspeed: Option<String>,
80}
81
82impl Policy {
83    pub fn new(policy: OsString) -> Result<Self> {
84        let dir = Path::new(CPU_FREQ_DIR);
85        let tgt = dir.join(policy);
86        if !dir.exists() || !tgt.exists() {
87            return Err(anyhow!("Directory {} does not exists!", dir.display()));
88        }
89
90        let read = |path: &PathBuf, name: &str| read_to_string(path.join(name));
91        let get_bool = |num: Option<u8>| match num {
92            Some(num) => Some(if num != 0 { true } else { false }),
93            None => None,
94        };
95
96        Ok(Self {
97            bios_limit: read(&tgt, "bios_limit")?.trim().parse().ok(),
98            boost: get_bool(read(&tgt, "boost")?.trim().parse().ok()),
99            cpb: get_bool(read(&tgt, "cpb")?.trim().parse().ok()),
100            cpu_max_freq: read(&tgt, "cpu_max_freq")?.trim().parse().ok(),
101            cpu_min_freq: read(&tgt, "cpu_min_freq")?.trim().parse().ok(),
102            cpuinfo_transition_latency: get_bool(
103                read(&tgt, "cpuinfo_transition_latency")?
104                    .trim()
105                    .parse()
106                    .ok(),
107            ),
108            scaling_available_frequencies: Some(
109                read(&tgt, "scaling_available_frequencies")?
110                    .split_whitespace()
111                    .map(|freq| freq.parse::<u32>().ok())
112                    .filter(|freq| freq.is_some())
113                    .map(|freq| freq.unwrap())
114                    .collect::<Vec<_>>(),
115            ),
116            scaling_available_governors: Some(
117                read(&tgt, "scaling_available_governors")?
118                    .trim()
119                    .split_whitespace()
120                    .map(|gov| gov.to_string())
121                    .collect(),
122            ),
123            scaling_cur_freq: read(&tgt, "scaling_cur_freq")?.trim().parse().ok(),
124            scaling_driver: read(&tgt, "scaling_driver").ok(),
125            scaling_governor: read(&tgt, "scaling_governor").ok(),
126            scaling_max_freq: read(&tgt, "scaling_max_freq")?.trim().parse().ok(),
127            scaling_min_freq: read(&tgt, "scaling_min_freq")?.trim().parse().ok(),
128            scaling_setspeed: read(&tgt, "scaling_setspeed").ok(),
129        })
130    }
131}