veridian_kernel/arch/x86_64/
dpms.rs1#![allow(dead_code)]
12
13use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU8, Ordering};
14
15use crate::error::{KernelError, KernelResult};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[repr(u8)]
24pub enum DpmsState {
25 On = 0,
27 Standby = 1,
29 Suspend = 2,
31 Off = 3,
33}
34
35impl DpmsState {
36 fn from_u8(val: u8) -> Self {
38 match val {
39 0 => Self::On,
40 1 => Self::Standby,
41 2 => Self::Suspend,
42 3 => Self::Off,
43 _ => Self::On,
44 }
45 }
46}
47
48impl core::fmt::Display for DpmsState {
49 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50 match self {
51 Self::On => write!(f, "On"),
52 Self::Standby => write!(f, "Standby"),
53 Self::Suspend => write!(f, "Suspend"),
54 Self::Off => write!(f, "Off"),
55 }
56 }
57}
58
59const VGA_SEQ_INDEX: u16 = 0x03C4;
65
66const VGA_SEQ_DATA: u16 = 0x03C5;
68
69const SEQ_CLOCKING_MODE: u8 = 0x01;
71
72const SCREEN_OFF_BIT: u8 = 1 << 5;
74
75const VGA_CRTC_INDEX: u16 = 0x03D4;
77
78const VGA_CRTC_DATA: u16 = 0x03D5;
80
81const CRTC_MODE_CONTROL: u8 = 0x17;
83
84const CRTC_ENABLE_SYNC: u8 = 1 << 7;
86
87const VGA_ATTR_INDEX: u16 = 0x03C0;
89
90const VGA_INPUT_STATUS_1: u16 = 0x03DA;
92
93static DPMS_INITIALIZED: AtomicBool = AtomicBool::new(false);
98static CURRENT_DPMS_STATE: AtomicU8 = AtomicU8::new(0); static IDLE_TIMEOUT_SECS: AtomicU32 = AtomicU32::new(300); static IDLE_TICKS: AtomicU32 = AtomicU32::new(0);
105
106static FRAMEBUFFER_SUPPRESSED: AtomicBool = AtomicBool::new(false);
108
109pub fn dpms_init() -> KernelResult<()> {
117 if DPMS_INITIALIZED.load(Ordering::Acquire) {
118 return Err(KernelError::AlreadyExists {
119 resource: "DPMS",
120 id: 0,
121 });
122 }
123
124 CURRENT_DPMS_STATE.store(DpmsState::On as u8, Ordering::Release);
125 IDLE_TICKS.store(0, Ordering::Release);
126 FRAMEBUFFER_SUPPRESSED.store(false, Ordering::Release);
127
128 DPMS_INITIALIZED.store(true, Ordering::Release);
129 println!(
130 "[DPMS] Initialized, idle timeout: {}s",
131 IDLE_TIMEOUT_SECS.load(Ordering::Relaxed)
132 );
133
134 Ok(())
135}
136
137pub fn dpms_set_state(state: DpmsState) -> KernelResult<()> {
146 if !DPMS_INITIALIZED.load(Ordering::Acquire) {
147 return Err(KernelError::NotInitialized { subsystem: "DPMS" });
148 }
149
150 let old_state = DpmsState::from_u8(CURRENT_DPMS_STATE.load(Ordering::Acquire));
151 if old_state == state {
152 return Ok(());
153 }
154
155 match state {
156 DpmsState::On => {
157 set_screen_on(true);
159 set_sync_enabled(true);
160 FRAMEBUFFER_SUPPRESSED.store(false, Ordering::Release);
161 }
162 DpmsState::Standby => {
163 set_screen_on(false);
167 FRAMEBUFFER_SUPPRESSED.store(true, Ordering::Release);
168 }
169 DpmsState::Suspend => {
170 set_screen_on(false);
172 FRAMEBUFFER_SUPPRESSED.store(true, Ordering::Release);
173 }
174 DpmsState::Off => {
175 set_screen_on(false);
177 set_sync_enabled(false);
178 FRAMEBUFFER_SUPPRESSED.store(true, Ordering::Release);
179 }
180 }
181
182 CURRENT_DPMS_STATE.store(state as u8, Ordering::Release);
183 println!("[DPMS] State changed: {} -> {}", old_state, state);
184
185 Ok(())
186}
187
188pub fn dpms_get_state() -> DpmsState {
190 DpmsState::from_u8(CURRENT_DPMS_STATE.load(Ordering::Acquire))
191}
192
193pub fn is_framebuffer_suppressed() -> bool {
195 FRAMEBUFFER_SUPPRESSED.load(Ordering::Acquire)
196}
197
198pub fn dpms_set_idle_timeout(seconds: u32) {
207 IDLE_TIMEOUT_SECS.store(seconds, Ordering::Release);
208 println!("[DPMS] Idle timeout set to {}s", seconds);
209}
210
211pub fn dpms_get_idle_timeout() -> u32 {
213 IDLE_TIMEOUT_SECS.load(Ordering::Acquire)
214}
215
216pub fn dpms_reset_idle() {
221 IDLE_TICKS.store(0, Ordering::Release);
222
223 let state = dpms_get_state();
225 if state != DpmsState::On {
226 let _ = dpms_set_state(DpmsState::On);
227 }
228}
229
230pub fn dpms_idle_tick() {
235 if !DPMS_INITIALIZED.load(Ordering::Acquire) {
236 return;
237 }
238
239 let timeout = IDLE_TIMEOUT_SECS.load(Ordering::Acquire);
240 if timeout == 0 {
241 return;
242 }
243
244 if dpms_get_state() != DpmsState::On {
246 return;
247 }
248
249 let ticks = IDLE_TICKS.fetch_add(1, Ordering::AcqRel);
250
251 if ticks >= timeout {
254 let _ = dpms_set_state(DpmsState::Off);
255 }
256}
257
258fn set_screen_on(on: bool) {
264 unsafe {
268 super::outb(VGA_SEQ_INDEX, SEQ_CLOCKING_MODE);
269 let mut val = super::inb(VGA_SEQ_DATA);
270 if on {
271 val &= !SCREEN_OFF_BIT;
272 } else {
273 val |= SCREEN_OFF_BIT;
274 }
275 super::outb(VGA_SEQ_INDEX, SEQ_CLOCKING_MODE);
276 super::outb(VGA_SEQ_DATA, val);
277 }
278}
279
280fn set_sync_enabled(enabled: bool) {
282 unsafe {
286 super::outb(VGA_CRTC_INDEX, CRTC_MODE_CONTROL);
287 let mut val = super::inb(VGA_CRTC_DATA);
288 if enabled {
289 val |= CRTC_ENABLE_SYNC;
290 } else {
291 val &= !CRTC_ENABLE_SYNC;
292 }
293 super::outb(VGA_CRTC_INDEX, CRTC_MODE_CONTROL);
294 super::outb(VGA_CRTC_DATA, val);
295 }
296}
297
298#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_dpms_state_from_u8() {
308 assert_eq!(DpmsState::from_u8(0), DpmsState::On);
309 assert_eq!(DpmsState::from_u8(1), DpmsState::Standby);
310 assert_eq!(DpmsState::from_u8(2), DpmsState::Suspend);
311 assert_eq!(DpmsState::from_u8(3), DpmsState::Off);
312 assert_eq!(DpmsState::from_u8(42), DpmsState::On); }
314
315 #[test]
316 fn test_dpms_state_display() {
317 assert_eq!(alloc::format!("{}", DpmsState::On), "On");
318 assert_eq!(alloc::format!("{}", DpmsState::Standby), "Standby");
319 assert_eq!(alloc::format!("{}", DpmsState::Suspend), "Suspend");
320 assert_eq!(alloc::format!("{}", DpmsState::Off), "Off");
321 }
322
323 #[test]
324 fn test_dpms_state_repr() {
325 assert_eq!(DpmsState::On as u8, 0);
326 assert_eq!(DpmsState::Standby as u8, 1);
327 assert_eq!(DpmsState::Suspend as u8, 2);
328 assert_eq!(DpmsState::Off as u8, 3);
329 }
330
331 #[test]
332 fn test_idle_timeout_default() {
333 let timeout = 300u32;
335 assert_eq!(timeout, 300);
336 }
337
338 #[test]
339 fn test_screen_off_bit() {
340 assert_eq!(SCREEN_OFF_BIT, 0x20);
341 }
342
343 #[test]
344 fn test_crtc_enable_sync() {
345 assert_eq!(CRTC_ENABLE_SYNC, 0x80);
346 }
347}