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