starnix_core/power/
wakeup_count.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::power::SuspendResumeManager;
6use crate::task::{
7    CurrentTask, EventHandler, SignalHandler, SignalHandlerInner, WaitCanceler, Waiter,
8};
9use crate::vfs::buffers::{InputBuffer, OutputBuffer};
10use crate::vfs::pseudo::simple_file::{SimpleFileNode, parse_unsigned_file, serialize_for_file};
11use crate::vfs::{FileObject, FileOps, FsNodeOps, fileops_impl_noop_sync, fileops_impl_seekable};
12use starnix_sync::{FileOpsCore, Locked};
13use starnix_uapi::error;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::vfs::FdEvents;
16use std::sync::Arc;
17
18/// This file allows user space to put the system into a sleep state while taking into account the
19/// concurrent arrival of wakeup events.
20/// * Reading from it returns the current number of registered wakeup events and it blocks if some
21/// wakeup events are being processed when the file is read from.
22/// * Writing to it will only succeed if the current number of wakeup events is equal to the written
23/// value and, if successful, will make the kernel abort a subsequent transition to a sleep state
24// if any wakeup events are reported after the write has returned.
25pub struct PowerWakeupCountFile {
26    suspend_resume_manager: Arc<SuspendResumeManager>,
27    blocking_event: zx::EventPair,
28}
29
30impl PowerWakeupCountFile {
31    pub fn new_node(suspend_resume_manager: Arc<SuspendResumeManager>) -> impl FsNodeOps {
32        SimpleFileNode::new(move |_, _| {
33            Ok(Self {
34                suspend_resume_manager: suspend_resume_manager.clone(),
35                blocking_event: suspend_resume_manager.duplicate_lock_event(),
36            })
37        })
38    }
39}
40
41impl FileOps for PowerWakeupCountFile {
42    fileops_impl_seekable!();
43    fileops_impl_noop_sync!();
44
45    fn write(
46        &self,
47        _locked: &mut Locked<FileOpsCore>,
48        _file: &FileObject,
49        _current_task: &CurrentTask,
50        _offset: usize,
51        data: &mut dyn InputBuffer,
52    ) -> Result<usize, Errno> {
53        let data = data.read_all()?;
54        let expected_count: u64 = parse_unsigned_file(&data)?;
55        let real_count = self.suspend_resume_manager.total_wakeup_events();
56        if expected_count != real_count {
57            return error!(EINVAL);
58        }
59        Ok(data.len())
60    }
61
62    fn read(
63        &self,
64        locked: &mut Locked<FileOpsCore>,
65        file: &FileObject,
66        current_task: &CurrentTask,
67        offset: usize,
68        data: &mut dyn OutputBuffer,
69    ) -> Result<usize, Errno> {
70        file.blocking_op(locked, current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, |_| {
71            if !self.suspend_resume_manager.lock().can_suspend() {
72                return error!(EAGAIN);
73            }
74            let wakeup_count = self.suspend_resume_manager.total_wakeup_events();
75            let content = serialize_for_file(wakeup_count);
76            if offset >= content.len() {
77                return Ok(0);
78            }
79            data.write(&content[offset..])
80        })
81    }
82
83    fn wait_async(
84        &self,
85        _locked: &mut Locked<FileOpsCore>,
86        _file: &FileObject,
87        _current_task: &CurrentTask,
88        waiter: &Waiter,
89        events: FdEvents,
90        handler: EventHandler,
91    ) -> Option<WaitCanceler> {
92        if events.contains(FdEvents::POLLIN) {
93            let signal_handler = SignalHandler {
94                inner: SignalHandlerInner::ZxHandle(|_signals| FdEvents::POLLIN),
95                event_handler: handler,
96                err_code: None,
97            };
98            return Some(WaitCanceler::new_port(
99                waiter
100                    .wake_on_zircon_signals(
101                        &self.blocking_event,
102                        zx::Signals::USER_0,
103                        signal_handler,
104                    )
105                    .expect("Failed to wait on zircon signals"),
106            ));
107        }
108        None
109    }
110
111    fn query_events(
112        &self,
113        _locked: &mut Locked<FileOpsCore>,
114        _file: &FileObject,
115        _current_task: &CurrentTask,
116    ) -> Result<FdEvents, Errno> {
117        let mut events = FdEvents::POLLOUT;
118        if self.suspend_resume_manager.lock().can_suspend() {
119            events |= FdEvents::POLLIN;
120        }
121        Ok(events)
122    }
123}