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