Skip to main content

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