starnix_modules_wakeup_test/
device.rs1use 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, default_ioctl};
14use starnix_core::{fileops_impl_dataless, fileops_impl_noop_sync, fileops_impl_seekless};
15use starnix_logging::{log_error, log_info};
16use starnix_sync::{FileOpsCore, Locked, Unlocked};
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(current_task: &CurrentTask) -> Self {
34 Self {
35 commands: Commands {
36 tid: current_task.get_tid(),
37 kernel: Arc::downgrade(current_task.kernel()),
38 },
39 }
40 }
41}
42
43impl DeviceOps for WakeupTestDevice {
44 fn open(
45 &self,
46 _locked: &mut Locked<FileOpsCore>,
47 current_task: &CurrentTask,
48 _id: device_id::DeviceId,
49 _node: &NamespaceNode,
50 _flags: OpenFlags,
51 ) -> Result<Box<dyn FileOps>, Errno> {
52 Ok(Box::new(WakeupTestDevice::new(current_task)))
53 }
54}
55
56#[derive(Clone)]
57struct Commands {
58 kernel: Weak<Kernel>,
59 tid: i32,
60}
61
62impl Commands {
63 fn schedule_wakeup(&self, time_ns: i64) -> Result<()> {
64 log_info!("WakeupTestDevice::schedule_wakeup creating async task for time_ns: {time_ns}");
65 let kernel = self.kernel.upgrade().expect("kernel should exist");
66
67 kernel.kthreads.spawn_future(
68 move || async move {
69 let media_button_proxy = match create_media_buttons_proxy().await {
70 Ok(proxy) => proxy,
71 Err(e) => {
72 log_error!("Failed to create media buttons proxy: {:?}", e);
73 return;
74 }
75 };
76 schedule_wakeup_power_button(
77 &media_button_proxy,
78 zx::Duration::from_nanos(time_ns),
79 )
80 .await;
81 let deadline = fuchsia_async::MonotonicInstant::after(
89 zx::Duration::from_seconds(5) + zx::Duration::from_nanos(time_ns),
90 );
91 fuchsia_async::Timer::new(deadline).await;
92 log_info!("media_button proxy dropped.")
93 },
94 "wakeup_test",
95 );
96 Ok(())
97 }
98
99 fn run_wakeup_set_timers(&self, timer_info: WakeupTimerInfo) -> Result<()> {
100 let method = WakeupMethod::from(timer_info.method);
101
102 match method {
104 WakeupMethod::PowerButton => (),
105 _ => {
106 return Err(anyhow!(
107 "Only PowerButton wakeup method is currently supported: b/458389823"
108 ));
109 }
110 };
111
112 let test_type = WakeupTestType::from(timer_info.test_type);
113 log_info!(
114 "WakeupTestDevice::WakeupSetTimers test_type: {test_type:?} Setting {} timers for {:?}, interval {} ns, starting {} ns",
115 timer_info.num_events,
116 method,
117 timer_info.interval,
118 timer_info.offset
119 );
120 tracing::trace_wakeup_test_type(self.get_trace_event_queues(), self.tid, test_type);
121 for index in 0..timer_info.num_events {
122 let time = (timer_info.interval * (index as i64)) + timer_info.offset;
123 log_info!("WakeupTestDevice::set_timer i: {index} for {time}");
124 self.schedule_wakeup(time)?;
125 }
126 Ok(())
127 }
128
129 fn get_trace_event_queues(&self) -> Option<Arc<TraceEventQueueList>> {
131 if let Some(k) = self.kernel.upgrade() {
132 let queues = TraceEventQueueList::from(&k);
133 if queues.is_enabled() {
134 Some(TraceEventQueueList::from(&k))
135 } else {
136 log_error!("Trace is not enabled");
137 None
138 }
139 } else {
140 None
141 }
142 }
143}
144
145impl FileOps for WakeupTestDevice {
146 fileops_impl_seekless!();
147 fileops_impl_dataless!();
148 fileops_impl_noop_sync!();
149
150 fn ioctl(
151 &self,
152 locked: &mut Locked<Unlocked>,
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(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 _ => default_ioctl(file, locked, current_task, request, arg),
180 }
181 }
182}