uconsole_sleep/hardware/
cpu.rs

1//! CPU frequency handling under `hardware` namespace
2use std::path::PathBuf;
3
4use log::debug;
5
6pub const CPU_POLICY_PATH: &str = "/sys/devices/system/cpu/cpufreq/policy0";
7
8#[derive(Clone, Debug)]
9pub struct CpuFreqConfig {
10    pub policy_path: PathBuf,
11    pub default_min: Option<String>,
12    pub default_max: Option<String>,
13    pub saving_min: Option<String>,
14    pub saving_max: Option<String>,
15}
16
17impl CpuFreqConfig {
18    pub fn new(saving_cpu_freq: Option<String>) -> Self {
19        let policy_path = PathBuf::from(CPU_POLICY_PATH);
20        Self::with_policy_path(policy_path, saving_cpu_freq)
21    }
22
23    pub fn with_policy_path(policy_path: PathBuf, saving_cpu_freq: Option<String>) -> Self {
24        let policy_path_clone = policy_path.clone();
25        let default_min = std::fs::read_to_string(policy_path_clone.join("scaling_min_freq"))
26            .ok()
27            .map(|s| s.trim().to_string());
28        let default_max = std::fs::read_to_string(policy_path_clone.join("scaling_max_freq"))
29            .ok()
30            .map(|s| s.trim().to_string());
31
32        let (saving_min, saving_max) = if let Some(s) = saving_cpu_freq {
33            let parts: Vec<&str> = s.split(',').collect();
34            if parts.len() == 2 {
35                let min = format!("{}000", parts[0].trim());
36                let max = format!("{}000", parts[1].trim());
37                (Some(min), Some(max))
38            } else {
39                (None, None)
40            }
41        } else {
42            (None, None)
43        };
44
45        CpuFreqConfig {
46            policy_path,
47            default_min,
48            default_max,
49            saving_min,
50            saving_max,
51        }
52    }
53
54    pub fn apply_saving_mode(&self, dry_run: bool) {
55        if let (Some(min), Some(max)) = (&self.saving_min, &self.saving_max) {
56            if dry_run {
57                debug!(
58                    "DRY-RUN: Would write CPU saving mode {}/{} to {}",
59                    min,
60                    max,
61                    self.policy_path.display()
62                );
63            } else {
64                let _ = std::fs::write(self.policy_path.join("scaling_min_freq"), min);
65                let _ = std::fs::write(self.policy_path.join("scaling_max_freq"), max);
66            }
67            debug!("CPU: saving mode {}/{}", min, max);
68        }
69    }
70
71    pub fn apply_normal_mode(&self, dry_run: bool) {
72        if let (Some(min), Some(max)) = (&self.default_min, &self.default_max) {
73            if dry_run {
74                debug!(
75                    "DRY-RUN: Would write CPU normal mode {}/{} to {}",
76                    min.trim(),
77                    max.trim(),
78                    self.policy_path.display()
79                );
80            } else {
81                let _ = std::fs::write(self.policy_path.join("scaling_min_freq"), min.trim());
82                let _ = std::fs::write(self.policy_path.join("scaling_max_freq"), max.trim());
83            }
84            debug!("CPU: normal mode {}/{}", min.trim(), max.trim());
85        }
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use std::env;
93    use std::fs;
94
95    #[test]
96    fn test_cpu_apply_modes_writes_files() {
97        let tmp = env::temp_dir().join(format!(
98            "uconsole_sleep_test_{}",
99            std::time::SystemTime::now()
100                .duration_since(std::time::UNIX_EPOCH)
101                .unwrap()
102                .as_millis()
103        ));
104        let _ = fs::create_dir_all(&tmp);
105
106        let cpu = CpuFreqConfig::with_policy_path(tmp.clone(), Some(String::from("100,400")));
107        cpu.apply_saving_mode(false);
108        let min = fs::read_to_string(tmp.join("scaling_min_freq")).unwrap();
109        let max = fs::read_to_string(tmp.join("scaling_max_freq")).unwrap();
110        assert_eq!(min, "100000");
111        assert_eq!(max, "400000");
112
113        cpu.apply_normal_mode(false);
114        let min2 = fs::read_to_string(tmp.join("scaling_min_freq")).unwrap();
115        let max2 = fs::read_to_string(tmp.join("scaling_max_freq")).unwrap();
116        assert_eq!(min2.trim(), "100000");
117        assert_eq!(max2.trim(), "400000");
118    }
119}