⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/sysfs/
power.rs

1//! Sysfs power virtual files for VeridianOS.
2//!
3//! Exposes power management controls as virtual files under `/sys/`:
4//! - `/sys/power/state` -- read supported sleep states, write to trigger
5//! - `/sys/class/backlight/veridian/brightness` -- read/write 0-100
6//! - `/sys/class/backlight/veridian/max_brightness` -- read returns 100
7//! - `/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor` -- read/write
8//! - `/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq` -- read current
9//! - `/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors` -- read
10//! - `/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq` -- read
11//! - `/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq` -- read
12//!
13//! These paths are compatible with Linux sysfs conventions so that
14//! PowerDevil and other desktop tools can interact with the kernel.
15
16#![allow(dead_code)]
17
18extern crate alloc;
19
20use alloc::{format, string::String};
21use core::sync::atomic::{AtomicU32, Ordering};
22
23use crate::{
24    error::{KernelError, KernelResult},
25    sysfs::SysfsNode,
26};
27
28// ---------------------------------------------------------------------------
29// Virtual backlight state
30// ---------------------------------------------------------------------------
31
32/// Current backlight brightness (0-100).
33static BACKLIGHT_BRIGHTNESS: AtomicU32 = AtomicU32::new(100);
34
35/// Maximum backlight brightness.
36const MAX_BRIGHTNESS: u32 = 100;
37
38// ---------------------------------------------------------------------------
39// /sys/power/state
40// ---------------------------------------------------------------------------
41
42/// Read handler for /sys/power/state.
43///
44/// Returns the list of supported sleep states in Linux sysfs format:
45/// "standby mem disk" (space-separated).
46fn power_state_read() -> String {
47    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
48    {
49        if crate::arch::x86_64::acpi_pm::is_initialized() {
50            return String::from(crate::arch::x86_64::acpi_pm::supported_states_string());
51        }
52    }
53    String::from("standby mem disk")
54}
55
56/// Write handler for /sys/power/state.
57///
58/// Accepts "mem" (S3 suspend), "disk" (S4 hibernate), or "standby" (S1).
59fn power_state_write(value: &str) -> KernelResult<()> {
60    let trimmed = value.trim();
61
62    match trimmed {
63        "mem" => {
64            println!("[SYSFS] Triggering S3 suspend via /sys/power/state");
65            #[cfg(all(target_arch = "x86_64", target_os = "none"))]
66            {
67                crate::arch::x86_64::acpi_pm::acpi_suspend_s3()
68            }
69            #[cfg(not(all(target_arch = "x86_64", target_os = "none")))]
70            {
71                Err(KernelError::OperationNotSupported {
72                    operation: "S3 suspend (not x86_64)",
73                })
74            }
75        }
76        "disk" => {
77            println!("[SYSFS] Triggering S4 hibernate via /sys/power/state");
78            #[cfg(all(target_arch = "x86_64", target_os = "none"))]
79            {
80                crate::arch::x86_64::acpi_pm::acpi_hibernate_s4()
81            }
82            #[cfg(not(all(target_arch = "x86_64", target_os = "none")))]
83            {
84                Err(KernelError::OperationNotSupported {
85                    operation: "S4 hibernate (not x86_64)",
86                })
87            }
88        }
89        "standby" => {
90            println!("[SYSFS] Standby requested via /sys/power/state");
91            // S1 standby is a lightweight CPU halt.
92            Ok(())
93        }
94        _ => Err(KernelError::InvalidArgument {
95            name: "power state",
96            value: "expected 'mem', 'disk', or 'standby'",
97        }),
98    }
99}
100
101// ---------------------------------------------------------------------------
102// /sys/class/backlight/veridian/brightness
103// ---------------------------------------------------------------------------
104
105/// Read handler for backlight brightness.
106fn backlight_brightness_read() -> String {
107    format!("{}", BACKLIGHT_BRIGHTNESS.load(Ordering::Acquire))
108}
109
110/// Write handler for backlight brightness.
111///
112/// Accepts a value 0-100. Values outside this range are clamped.
113fn backlight_brightness_write(value: &str) -> KernelResult<()> {
114    let trimmed = value.trim();
115    let brightness: u32 = trimmed.parse().map_err(|_| KernelError::InvalidArgument {
116        name: "brightness",
117        value: "not a valid integer",
118    })?;
119
120    let clamped = brightness.min(MAX_BRIGHTNESS);
121    BACKLIGHT_BRIGHTNESS.store(clamped, Ordering::Release);
122    println!("[SYSFS] Backlight brightness set to {}", clamped);
123    Ok(())
124}
125
126/// Read handler for max_brightness.
127fn backlight_max_brightness_read() -> String {
128    format!("{}", MAX_BRIGHTNESS)
129}
130
131// ---------------------------------------------------------------------------
132// /sys/devices/system/cpu/cpu0/cpufreq/*
133// ---------------------------------------------------------------------------
134
135/// Read handler for scaling_governor.
136fn cpufreq_governor_read() -> String {
137    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
138    {
139        if crate::arch::x86_64::cpufreq::is_initialized() {
140            return String::from(crate::arch::x86_64::cpufreq::cpufreq_get_governor().name());
141        }
142    }
143    String::from("performance")
144}
145
146/// Write handler for scaling_governor.
147fn cpufreq_governor_write(value: &str) -> KernelResult<()> {
148    let trimmed = value.trim();
149
150    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
151    {
152        use crate::arch::x86_64::cpufreq::CpuGovernor;
153        let governor = CpuGovernor::from_name(trimmed).ok_or(KernelError::InvalidArgument {
154            name: "governor",
155            value: "expected 'performance', 'powersave', or 'ondemand'",
156        })?;
157        crate::arch::x86_64::cpufreq::cpufreq_set_governor(governor)
158    }
159
160    #[cfg(not(all(target_arch = "x86_64", target_os = "none")))]
161    {
162        let _ = trimmed;
163        Err(KernelError::OperationNotSupported {
164            operation: "cpufreq governor (not x86_64)",
165        })
166    }
167}
168
169/// Read handler for scaling_cur_freq.
170fn cpufreq_cur_freq_read() -> String {
171    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
172    {
173        if crate::arch::x86_64::cpufreq::is_initialized() {
174            return format!("{}", crate::arch::x86_64::cpufreq::cpufreq_get_frequency());
175        }
176    }
177    String::from("0")
178}
179
180/// Read handler for scaling_available_governors.
181fn cpufreq_available_governors_read() -> String {
182    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
183    {
184        String::from(crate::arch::x86_64::cpufreq::cpufreq_available_governors())
185    }
186    #[cfg(not(all(target_arch = "x86_64", target_os = "none")))]
187    {
188        String::from("performance powersave ondemand")
189    }
190}
191
192/// Read handler for scaling_min_freq.
193fn cpufreq_min_freq_read() -> String {
194    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
195    {
196        if crate::arch::x86_64::cpufreq::is_initialized() {
197            return format!(
198                "{}",
199                crate::arch::x86_64::cpufreq::cpufreq_get_min_frequency()
200            );
201        }
202    }
203    String::from("0")
204}
205
206/// Read handler for scaling_max_freq.
207fn cpufreq_max_freq_read() -> String {
208    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
209    {
210        if crate::arch::x86_64::cpufreq::is_initialized() {
211            return format!(
212                "{}",
213                crate::arch::x86_64::cpufreq::cpufreq_get_max_frequency()
214            );
215        }
216    }
217    String::from("0")
218}
219
220// ---------------------------------------------------------------------------
221// DPMS sysfs node
222// ---------------------------------------------------------------------------
223
224/// Read handler for DPMS idle timeout.
225fn dpms_idle_timeout_read() -> String {
226    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
227    {
228        format!("{}", crate::arch::x86_64::dpms::dpms_get_idle_timeout())
229    }
230    #[cfg(not(all(target_arch = "x86_64", target_os = "none")))]
231    {
232        String::from("300")
233    }
234}
235
236/// Write handler for DPMS idle timeout.
237fn dpms_idle_timeout_write(value: &str) -> KernelResult<()> {
238    let trimmed = value.trim();
239    let seconds: u32 = trimmed.parse().map_err(|_| KernelError::InvalidArgument {
240        name: "idle_timeout",
241        value: "not a valid integer",
242    })?;
243
244    #[cfg(all(target_arch = "x86_64", target_os = "none"))]
245    {
246        crate::arch::x86_64::dpms::dpms_set_idle_timeout(seconds);
247    }
248
249    let _ = seconds;
250    println!("[SYSFS] DPMS idle timeout set to {}s", seconds);
251    Ok(())
252}
253
254// ---------------------------------------------------------------------------
255// Initialization
256// ---------------------------------------------------------------------------
257
258/// Register all power-related sysfs nodes.
259pub(crate) fn sysfs_power_init() -> KernelResult<()> {
260    // /sys/power/state
261    super::register_node(SysfsNode::read_write(
262        "/sys/power/state",
263        "System sleep states (read: list supported, write: trigger)",
264        power_state_read,
265        power_state_write,
266    ))?;
267
268    // /sys/class/backlight/veridian/brightness
269    super::register_node(SysfsNode::read_write(
270        "/sys/class/backlight/veridian/brightness",
271        "Backlight brightness (0-100)",
272        backlight_brightness_read,
273        backlight_brightness_write,
274    ))?;
275
276    // /sys/class/backlight/veridian/max_brightness
277    super::register_node(SysfsNode::read_only(
278        "/sys/class/backlight/veridian/max_brightness",
279        "Maximum backlight brightness",
280        backlight_max_brightness_read,
281    ))?;
282
283    // /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
284    super::register_node(SysfsNode::read_write(
285        "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor",
286        "CPU frequency scaling governor",
287        cpufreq_governor_read,
288        cpufreq_governor_write,
289    ))?;
290
291    // /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
292    super::register_node(SysfsNode::read_only(
293        "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
294        "Current CPU frequency in kHz",
295        cpufreq_cur_freq_read,
296    ))?;
297
298    // /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
299    super::register_node(SysfsNode::read_only(
300        "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors",
301        "Available CPU frequency governors",
302        cpufreq_available_governors_read,
303    ))?;
304
305    // /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
306    super::register_node(SysfsNode::read_only(
307        "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq",
308        "Minimum CPU frequency in kHz",
309        cpufreq_min_freq_read,
310    ))?;
311
312    // /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
313    super::register_node(SysfsNode::read_only(
314        "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq",
315        "Maximum CPU frequency in kHz",
316        cpufreq_max_freq_read,
317    ))?;
318
319    // DPMS idle timeout
320    super::register_node(SysfsNode::read_write(
321        "/sys/class/drm/card0/dpms_idle_timeout",
322        "DPMS idle timeout in seconds",
323        dpms_idle_timeout_read,
324        dpms_idle_timeout_write,
325    ))?;
326
327    Ok(())
328}
329
330// ---------------------------------------------------------------------------
331// Tests
332// ---------------------------------------------------------------------------
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337
338    #[test]
339    fn test_power_state_read() {
340        let states = power_state_read();
341        assert!(states.contains("mem") || states.contains("standby"));
342    }
343
344    #[test]
345    fn test_power_state_write_invalid() {
346        let result = power_state_write("invalid_state");
347        assert!(result.is_err());
348    }
349
350    #[test]
351    fn test_backlight_brightness_read() {
352        BACKLIGHT_BRIGHTNESS.store(75, Ordering::Release);
353        let val = backlight_brightness_read();
354        assert_eq!(val, "75");
355    }
356
357    #[test]
358    fn test_backlight_brightness_write_valid() {
359        let result = backlight_brightness_write("50");
360        assert!(result.is_ok());
361        assert_eq!(BACKLIGHT_BRIGHTNESS.load(Ordering::Acquire), 50);
362    }
363
364    #[test]
365    fn test_backlight_brightness_write_clamp() {
366        let result = backlight_brightness_write("200");
367        assert!(result.is_ok());
368        assert_eq!(BACKLIGHT_BRIGHTNESS.load(Ordering::Acquire), 100);
369    }
370
371    #[test]
372    fn test_backlight_brightness_write_invalid() {
373        let result = backlight_brightness_write("abc");
374        assert!(result.is_err());
375    }
376
377    #[test]
378    fn test_backlight_max_brightness() {
379        let val = backlight_max_brightness_read();
380        assert_eq!(val, "100");
381    }
382
383    #[test]
384    fn test_cpufreq_governor_read() {
385        let val = cpufreq_governor_read();
386        assert!(!val.is_empty());
387    }
388
389    #[test]
390    fn test_cpufreq_available_governors() {
391        let val = cpufreq_available_governors_read();
392        assert!(val.contains("performance"));
393    }
394
395    #[test]
396    fn test_power_state_write_standby() {
397        let result = power_state_write("standby");
398        assert!(result.is_ok());
399    }
400
401    #[test]
402    fn test_dpms_idle_timeout_read() {
403        let val = dpms_idle_timeout_read();
404        assert!(!val.is_empty());
405    }
406
407    #[test]
408    fn test_dpms_idle_timeout_write_valid() {
409        let result = dpms_idle_timeout_write("120");
410        assert!(result.is_ok());
411    }
412
413    #[test]
414    fn test_dpms_idle_timeout_write_invalid() {
415        let result = dpms_idle_timeout_write("not_a_number");
416        assert!(result.is_err());
417    }
418}