1use tracing::{error, info};
2
3use crate::{
4 acpi,
5 cpu::{self},
6 devices::{keyboard_mouse, Device},
7 fs,
8 io::console,
9 process::scheduler,
10 sync::once::OnceLock,
11};
12
13static CURRENT_CMD: OnceLock<PowerCommand> = OnceLock::new();
14
15#[derive(Debug, Copy, Clone)]
16pub enum PowerCommand {
17 Shutdown,
18 Reboot,
19}
20
21#[derive(Debug)]
28pub struct PowerDevice;
29
30impl Device for PowerDevice {
31 fn name(&self) -> &str {
32 "power"
33 }
34
35 fn set_size(&self, size: u64) -> Result<(), fs::FileSystemError> {
38 if size != 0 {
39 return Err(fs::FileSystemError::EndOfFile);
41 }
42
43 Ok(())
44 }
45
46 fn write(&self, offset: u64, buf: &[u8]) -> Result<u64, fs::FileSystemError> {
48 if offset != 0 {
49 return Err(fs::FileSystemError::EndOfFile);
50 }
51
52 if let Some(rest) = buf.strip_prefix(b"shutdown") {
53 if rest.trim_ascii().is_empty() {
54 start_power_sequence(PowerCommand::Shutdown);
55 Ok(buf.len() as u64)
56 } else {
57 Err(fs::FileSystemError::EndOfFile)
58 }
59 } else if let Some(rest) = buf.strip_prefix(b"reboot") {
60 if rest.trim_ascii().is_empty() {
61 start_power_sequence(PowerCommand::Reboot);
62 Ok(buf.len() as u64)
63 } else {
64 Err(fs::FileSystemError::EndOfFile)
65 }
66 } else {
67 Err(fs::FileSystemError::EndOfFile)
68 }
69 }
70}
71
72pub fn start_power_sequence(cmd: PowerCommand) {
74 if let Err(current_cmd) = CURRENT_CMD.set(cmd) {
75 error!("Power command already set: {current_cmd:?}, ignoring: {cmd:?}",);
76 return;
77 }
78 match cmd {
79 PowerCommand::Shutdown => {
80 info!("Shutting down the system");
81 }
82 PowerCommand::Reboot => {
83 info!("Rebooting the system");
84 }
85 }
86
87 scheduler::stop_scheduler();
90}
91
92pub fn finish_power_sequence() -> ! {
95 let cmd = CURRENT_CMD.try_get().expect("No power command set");
96
97 console::tracing::shutdown_log_file();
98 fs::unmount_all();
100
101 cpu::cpu().push_cli();
102 match cmd {
103 PowerCommand::Shutdown => {
104 acpi::sleep(5).expect("Could not shutdown");
106 }
107 PowerCommand::Reboot => {
108 info!("Rebooting the system using the keyboard controller");
112 keyboard_mouse::reset_system();
113 }
114 }
115
116 loop {
118 unsafe {
119 cpu::halt();
120 }
121 }
122}