starnix_core/power/
state.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::task::CurrentTask;
6use crate::vfs::FsNodeOps;
7use crate::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
8use fidl_fuchsia_power_broker::PowerLevel;
9use starnix_logging::{log_info, log_warn};
10use starnix_sync::{FileOpsCore, Locked};
11use starnix_uapi::errors::Errno;
12use starnix_uapi::{errno, error};
13use std::borrow::Cow;
14
15#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
16pub enum SuspendState {
17    /// Suspend-to-disk
18    ///
19    /// This state offers the greatest energy savings.
20    Disk,
21    /// Suspend-to-Ram
22    ///
23    /// This state, if supported, offers significant power savings as everything in the system is
24    /// put into a low-power state, except for memory.
25    Ram,
26    /// Standby
27    ///
28    /// This state, if supported, offers moderate, but real, energy savings, while providing a
29    /// relatively straightforward transition back to the working state.
30    ///
31    Standby,
32    /// Suspend-To-Idle
33    ///
34    /// This state is a generic, pure software, light-weight, system sleep state.
35    Idle,
36}
37
38impl std::fmt::Display for SuspendState {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            SuspendState::Disk => write!(f, "disk"),
42            SuspendState::Ram => write!(f, "ram"),
43            SuspendState::Standby => write!(f, "standby"),
44            SuspendState::Idle => write!(f, "freeze"),
45        }
46    }
47}
48
49impl From<SuspendState> for PowerLevel {
50    fn from(value: SuspendState) -> Self {
51        match value {
52            SuspendState::Disk => 0,
53            SuspendState::Ram => 1,
54            SuspendState::Standby => 2,
55            SuspendState::Idle => 3,
56        }
57    }
58}
59
60pub struct PowerStateFile;
61
62impl PowerStateFile {
63    pub fn new_node() -> impl FsNodeOps {
64        BytesFile::new_node(Self {})
65    }
66}
67
68impl BytesFileOps for PowerStateFile {
69    fn write_locked(
70        &self,
71        locked: &mut Locked<FileOpsCore>,
72        current_task: &CurrentTask,
73        data: Vec<u8>,
74    ) -> Result<(), Errno> {
75        let state_str = std::str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
76        let clean_state_str = state_str.split('\n').next().unwrap_or("");
77        let state = match clean_state_str {
78            "disk" => SuspendState::Disk,
79            "standby" => SuspendState::Standby,
80            // TODO(https://fxbug.dev/368394556): Check on mem_file to see what suspend state
81            // "mem" represents
82            "freeze" | "mem" => SuspendState::Idle,
83            _ => return error!(EINVAL),
84        };
85        let power_manager = &current_task.kernel().suspend_resume_manager;
86        let supported_states = power_manager.suspend_states();
87        if !supported_states.contains(&state) {
88            return error!(EINVAL);
89        }
90        log_info!(state:?; "Received write to power state file.");
91        // LINT.IfChange
92        fuchsia_trace::duration!("power", "starnix-sysfs:suspend");
93        // LINT.ThenChange(//src/performance/lib/trace_processing/metrics/suspend.py)
94        power_manager.suspend(locked, state).inspect_err(|e| log_warn!("Suspend failed: {e}"))?;
95
96        Ok(())
97    }
98
99    fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
100        let states = current_task.kernel().suspend_resume_manager.suspend_states();
101        let mut states_array: Vec<String> = states.iter().map(SuspendState::to_string).collect();
102        // TODO(https://fxbug.dev/368394556): The “mem” string is interpreted in accordance with
103        // the contents of the mem_sleep file.
104        states_array.push("mem".to_string());
105        states_array.sort();
106        let content = states_array.join(" ") + "\n";
107        Ok(content.as_bytes().to_owned().into())
108    }
109}