uconsole_sleep/
power_mode.rs

1//! Power mode helper - combines display toggling with CPU frequency changes
2
3use crate::hardware::{backlight, drm_panel, framebuffer};
4use crate::{BTConfig, CpuFreqConfig, WifiConfig};
5use log::{debug, info, warn};
6use std::fs;
7
8fn toggle_display(dry_run: bool) -> Result<(), String> {
9    let backlight_path = match backlight::find_backlight() {
10        Ok(Some(p)) => p,
11        Ok(None) => return Err("backlight not found".to_string()),
12        Err(e) => return Err(format!("failed to find backlight: {}", e)),
13    };
14
15    let framebuffer_path = framebuffer::find_framebuffer().ok().flatten();
16    let drm_path = drm_panel::find_drm_panel().ok().flatten();
17
18    let bl_state =
19        fs::read_to_string(backlight_path.join("bl_power")).unwrap_or_else(|_| "4".to_string());
20    let bl_state_trim = bl_state.trim();
21
22    if bl_state_trim == "4" {
23        // ON
24        info!("Turning display ON");
25        if !dry_run {
26            if let Some(fb) = framebuffer_path {
27                let _ = fs::write(fb.join("blank"), "0");
28            }
29            let _ = fs::write(backlight_path.join("bl_power"), "0");
30            if let Some(drm) = drm_path {
31                let _ = fs::write(drm.join("status"), "detect");
32            }
33        } else {
34            debug!("DRY-RUN: toggle_display ON skipped");
35        }
36    } else {
37        // OFF
38        info!("Turning display OFF");
39        if !dry_run {
40            if let Some(drm) = drm_path {
41                let _ = fs::write(drm.join("status"), "off");
42            }
43            if let Some(fb) = framebuffer_path {
44                let _ = fs::write(fb.join("blank"), "1");
45            }
46            let _ = fs::write(backlight_path.join("bl_power"), "4");
47        } else {
48            debug!("DRY-RUN: toggle_display OFF skipped");
49        }
50    }
51    Ok(())
52}
53
54/// Power saving mode state
55#[derive(Clone, Debug, PartialEq)]
56pub enum PowerMode {
57    Normal,
58    Saving,
59}
60
61pub fn enter_saving_mode(
62    cpu_config: &CpuFreqConfig,
63    dry_run: bool,
64    wifi: Option<&WifiConfig>,
65    bt: Option<&BTConfig>,
66) {
67    info!("Entering power-saving mode");
68    if let Err(e) = toggle_display(dry_run) {
69        warn!("toggle_display failed: {}", e);
70    }
71    cpu_config.apply_saving_mode(dry_run);
72    if let Some(w) = wifi {
73        w.block(dry_run);
74    }
75    if let Some(b) = bt {
76        b.block(dry_run);
77    }
78}
79
80/// Exit power-saving mode: restore CPU then turn display on
81pub fn exit_saving_mode(
82    cpu_config: &CpuFreqConfig,
83    dry_run: bool,
84    wifi: Option<&WifiConfig>,
85    bt: Option<&BTConfig>,
86) {
87    info!("Exiting power-saving mode");
88    cpu_config.apply_normal_mode(dry_run);
89    if let Err(e) = toggle_display(dry_run) {
90        warn!("toggle_display failed: {}", e);
91    }
92    if let Some(w) = wifi {
93        w.unblock(dry_run);
94    }
95    if let Some(b) = bt {
96        b.unblock(dry_run);
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use std::env;
104    use std::fs;
105
106    #[test]
107    fn test_enter_exit_saving_mode_dryrun() {
108        let tmp = env::temp_dir().join(format!(
109            "uconsole_pm_test_{}",
110            std::time::SystemTime::now()
111                .duration_since(std::time::UNIX_EPOCH)
112                .unwrap()
113                .as_millis()
114        ));
115        let _ = fs::create_dir_all(&tmp);
116        let cpu = CpuFreqConfig::with_policy_path(tmp.clone(), Some(String::from("100,200")));
117        // Dry run should not create policy files
118        enter_saving_mode(&cpu, true, None, None);
119        assert!(!tmp.join("scaling_min_freq").exists());
120        assert!(!tmp.join("scaling_max_freq").exists());
121
122        // Non-dry-run should write
123        enter_saving_mode(&cpu, false, None, None);
124        assert!(tmp.join("scaling_min_freq").exists());
125        assert!(tmp.join("scaling_max_freq").exists());
126
127        // exit - verify it doesn't panic
128        exit_saving_mode(&cpu, false, None, None);
129    }
130}