Skip to main content

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