Skip to main content

starnix_modules_wakeup_test/
device.rs

1// Copyright 2026 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::input::{create_media_buttons_proxy, schedule_wakeup_power_button};
6use crate::ioctl::{CommandCode, WakeupMethod, WakeupTestType, WakeupTimerInfo};
7use crate::tracing;
8use anyhow::{Result, anyhow};
9use starnix_core::device::DeviceOps;
10use starnix_core::mm::MemoryAccessorExt;
11use starnix_core::perf::TraceEventQueueList;
12use starnix_core::task::{CurrentTask, Kernel};
13use starnix_core::vfs::{CloseFreeSafe, FileObject, FileOps, NamespaceNode};
14use starnix_core::{fileops_impl_dataless, fileops_impl_noop_sync, fileops_impl_seekless};
15use starnix_logging::{log_error, log_info};
16
17use starnix_syscalls::{SUCCESS, SyscallResult};
18use starnix_uapi::errors::Errno;
19use starnix_uapi::open_flags::OpenFlags;
20use starnix_uapi::user_address::UserRef;
21use starnix_uapi::{device_id, error};
22use std::sync::{Arc, Weak};
23use zx;
24
25#[derive(Clone)]
26pub struct WakeupTestDevice {
27    commands: Commands,
28}
29
30impl CloseFreeSafe for WakeupTestDevice {}
31
32impl WakeupTestDevice {
33    pub fn new(kernel: &Arc<Kernel>) -> Self {
34        Self { commands: Commands { kernel: Arc::downgrade(kernel) } }
35    }
36}
37
38impl DeviceOps for WakeupTestDevice {
39    fn open(
40        &self,
41        current_task: &CurrentTask,
42        _id: device_id::DeviceId,
43        _node: &NamespaceNode,
44        _flags: OpenFlags,
45    ) -> Result<Box<dyn FileOps>, Errno> {
46        Ok(Box::new(WakeupTestDevice::new(current_task.kernel())))
47    }
48}
49
50#[derive(Clone)]
51struct Commands {
52    kernel: Weak<Kernel>,
53}
54
55impl Commands {
56    fn schedule_wakeup(&self, time_ns: i64) -> Result<()> {
57        log_info!("WakeupTestDevice::schedule_wakeup creating async task for time_ns: {time_ns}");
58        let kernel = self.kernel.upgrade().expect("kernel should exist");
59
60        kernel.kthreads.spawn_future(
61            move || async move {
62                let media_button_proxy = match create_media_buttons_proxy().await {
63                    Ok(proxy) => proxy,
64                    Err(e) => {
65                        log_error!("Failed to create media buttons proxy: {:?}", e);
66                        return;
67                    }
68                };
69                schedule_wakeup_power_button(
70                    &media_button_proxy,
71                    zx::Duration::from_nanos(time_ns),
72                )
73                .await;
74                // Keep the device around until the timer goes off.
75                // Since this is called from via ioctl, it is not feasible to return an EventPair or
76                // some other handle that could be used to make the lifetime more event driven.
77                // This may be something to revisit if the test is flaky because of the media_button_proxy
78                // being dropped before the timer is goes off and the input sent.
79
80                // Sleep for 5 seconds plus the timer duration to ensure the event is sent before the proxy is dropped.
81                let deadline = fuchsia_async::MonotonicInstant::after(
82                    zx::Duration::from_seconds(5) + zx::Duration::from_nanos(time_ns),
83                );
84                fuchsia_async::Timer::new(deadline).await;
85                log_info!("media_button proxy dropped.")
86            },
87            "wakeup_test",
88        );
89        Ok(())
90    }
91
92    fn run_wakeup_set_timers(
93        &self,
94        current_task: &CurrentTask,
95        timer_info: WakeupTimerInfo,
96    ) -> Result<()> {
97        let method = WakeupMethod::from(timer_info.method);
98
99        // TODO(https://fxbug.dev/458389823): Use other input events to wakeup the system.
100        match method {
101            WakeupMethod::PowerButton => (),
102            _ => {
103                return Err(anyhow!(
104                    "Only PowerButton wakeup method is currently supported: b/458389823"
105                ));
106            }
107        };
108
109        let test_type = WakeupTestType::from(timer_info.test_type);
110        log_info!(
111            "WakeupTestDevice::WakeupSetTimers test_type: {test_type:?} Setting {} timers for {:?}, interval {} ns, starting {} ns",
112            timer_info.num_events,
113            method,
114            timer_info.interval,
115            timer_info.offset
116        );
117        tracing::trace_wakeup_test_type(
118            self.get_trace_event_queues(),
119            current_task.get_tid(),
120            test_type,
121        );
122        for index in 0..timer_info.num_events {
123            let time = (timer_info.interval * (index as i64)) + timer_info.offset;
124            log_info!("WakeupTestDevice::set_timer i: {index} for {time}");
125            self.schedule_wakeup(time)?;
126        }
127        Ok(())
128    }
129
130    /// Gets the trace event queue if available to emit trace events.
131    fn get_trace_event_queues(&self) -> Option<Arc<TraceEventQueueList>> {
132        if let Some(k) = self.kernel.upgrade() {
133            let queues = TraceEventQueueList::from(&k);
134            if queues.is_enabled() {
135                Some(TraceEventQueueList::from(&k))
136            } else {
137                log_error!("Trace is not enabled");
138                None
139            }
140        } else {
141            None
142        }
143    }
144}
145
146impl FileOps for WakeupTestDevice {
147    fileops_impl_seekless!();
148    fileops_impl_dataless!();
149    fileops_impl_noop_sync!();
150
151    fn ioctl(
152        &self,
153        _file: &FileObject,
154        current_task: &CurrentTask,
155        request: u32,
156        arg: starnix_syscalls::SyscallArg,
157    ) -> Result<SyscallResult, Errno> {
158        let cmd_num = CommandCode::from(request);
159        log_info!("WakeupTestDevice::ioctl cmd_num {cmd_num:?}, arg: {arg:?}");
160
161        match cmd_num {
162            CommandCode::WakeupSetTimers => {
163                let timer_ref = UserRef::<WakeupTimerInfo>::new(arg.into());
164                let timer_info = current_task.read_object(timer_ref)?;
165                log_info!("WakeupTestDevice::WakeupSetTimers {timer_info:?}");
166                log_info!("WakeupTestDevice::WakeupSetTimers version 0x{:x}", timer_info.version);
167
168                match self.commands.run_wakeup_set_timers(current_task, timer_info) {
169                    Ok(_) => Ok(SUCCESS),
170                    Err(e) => {
171                        log_error!("WakeupTestDevice::WakeupSetTimers failed: {:?}", e);
172                        return error!(EINVAL);
173                    }
174                }
175            }
176            CommandCode::WakeupTest => error!(ENOSYS),
177            CommandCode::WakeupHowManyTimers => error!(ENOSYS),
178            CommandCode::WakeupCancelTimers => error!(ENOSYS),
179            _ => error!(ENOTTY),
180        }
181    }
182}