veridian_kernel/sysfs/
power.rs1#![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
28static BACKLIGHT_BRIGHTNESS: AtomicU32 = AtomicU32::new(100);
34
35const MAX_BRIGHTNESS: u32 = 100;
37
38fn 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
56fn 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 Ok(())
93 }
94 _ => Err(KernelError::InvalidArgument {
95 name: "power state",
96 value: "expected 'mem', 'disk', or 'standby'",
97 }),
98 }
99}
100
101fn backlight_brightness_read() -> String {
107 format!("{}", BACKLIGHT_BRIGHTNESS.load(Ordering::Acquire))
108}
109
110fn 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
126fn backlight_max_brightness_read() -> String {
128 format!("{}", MAX_BRIGHTNESS)
129}
130
131fn 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
146fn 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
169fn 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
180fn 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
192fn 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
206fn 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
220fn 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
236fn 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
254pub(crate) fn sysfs_power_init() -> KernelResult<()> {
260 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 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 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 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 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 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 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 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 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#[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}