starnix_core/power/
wake_lock.rs

1// Copyright 2024 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::power::LockSource;
6use crate::task::CurrentTask;
7use crate::vfs::FsNodeOps;
8use crate::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
9use starnix_uapi::errors::Errno;
10use starnix_uapi::{errno, error};
11use std::borrow::Cow;
12
13pub struct PowerWakeLockFile;
14
15impl PowerWakeLockFile {
16    pub fn new_node() -> impl FsNodeOps {
17        BytesFile::new_node(Self {})
18    }
19}
20
21impl BytesFileOps for PowerWakeLockFile {
22    /// Writing a string activates a "wakeup source" preventing the system from
23    /// entering a low-power state.
24    ///
25    /// 1. Simple string (no whitespace): Activates or creates a wakeup source with that name.
26    /// 2. String with whitespace: The first part (before the whitespace) is the wakeup source name.
27    ///    The second part is a timeout in nanoseconds, after which the wakeup source is
28    ///    automatically deactivated.
29    fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
30        let lock_str = std::str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
31        let clean_str = lock_str.trim_end_matches('\n');
32        let mut clean_str_split = clean_str.split(' ');
33        let Some(clean_lock_str) = clean_str_split.next() else {
34            return error!(EINVAL);
35        };
36
37        // Check if there is a timeout.
38        let target_monotonic = match clean_str_split.next() {
39            Some(timeout_str) => Some(
40                zx::MonotonicInstant::get() // now
41                    + zx::MonotonicDuration::from_nanos(
42                        timeout_str
43                            .parse()
44                            .map_err(|_| errno!(EINVAL, "Failed to parse the timeout string"))?,
45                    ),
46            ),
47            None => None,
48        };
49
50        current_task
51            .kernel()
52            .suspend_resume_manager
53            .add_lock(clean_lock_str, LockSource::WakeLockFile);
54
55        // Set a timer to disable the wake lock when expired.
56        if let Some(target_monotonic) = target_monotonic {
57            let kernel_ref = current_task.kernel().clone();
58            let clean_lock_string = clean_lock_str.to_string();
59            current_task.kernel().kthreads.spawn_future(async move || {
60                fuchsia_async::Timer::new(target_monotonic).await;
61                kernel_ref.suspend_resume_manager.remove_lock(&clean_lock_string);
62            });
63        }
64
65        Ok(())
66    }
67
68    fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
69        let wake_locks = current_task.kernel().suspend_resume_manager.lock().active_wake_locks();
70        let content = wake_locks.join(" ") + "\n";
71        Ok(content.as_bytes().to_owned().into())
72    }
73}
74
75pub struct PowerWakeUnlockFile;
76
77impl PowerWakeUnlockFile {
78    pub fn new_node() -> impl FsNodeOps {
79        BytesFile::new_node(Self {})
80    }
81}
82
83impl BytesFileOps for PowerWakeUnlockFile {
84    /// Writing a string to this file deactivates the wakeup source with that name.
85    fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
86        let lock_str = std::str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
87        let clean_lock_str = lock_str.trim_end_matches('\n');
88        if !current_task.kernel().suspend_resume_manager.remove_lock(clean_lock_str) {
89            return error!(EPERM);
90        }
91        Ok(())
92    }
93
94    /// Returns a space-separated list of inactive wakeup source names previously created
95    /// via `PowerWakeLockFile`.
96    fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
97        let wake_locks = current_task.kernel().suspend_resume_manager.lock().inactive_wake_locks();
98        let content = wake_locks.join(" ") + "\n";
99        Ok(content.as_bytes().to_owned().into())
100    }
101}