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::TraceEventQueue;
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, log_warn};
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_type, 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_type::DeviceType,
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_queue(), 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_queue(&self) -> Option<Arc<TraceEventQueue>> {
131 if let Some(k) = self.kernel.upgrade() {
132 let event_queue = TraceEventQueue::from(&k);
133 if event_queue.is_enabled() {
134 return Some(event_queue);
135 } else {
136 log_warn!("Trace event queue is not enabled");
137 }
138 }
139 None
140 }
141}
142
143impl FileOps for WakeupTestDevice {
144 fileops_impl_seekless!();
145 fileops_impl_dataless!();
146 fileops_impl_noop_sync!();
147
148 fn ioctl(
149 &self,
150 locked: &mut Locked<Unlocked>,
151 file: &FileObject,
152 current_task: &CurrentTask,
153 request: u32,
154 arg: starnix_syscalls::SyscallArg,
155 ) -> Result<SyscallResult, Errno> {
156 let cmd_num = CommandCode::from(request);
157 log_info!("WakeupTestDevice::ioctl cmd_num {cmd_num:?}, arg: {arg:?}");
158
159 match cmd_num {
160 CommandCode::WakeupSetTimers => {
161 let timer_ref = UserRef::<WakeupTimerInfo>::new(arg.into());
162 let timer_info = current_task.read_object(timer_ref)?;
163 log_info!("WakeupTestDevice::WakeupSetTimers {timer_info:?}");
164 log_info!("WakeupTestDevice::WakeupSetTimers version 0x{:x}", timer_info.version);
165
166 match self.commands.run_wakeup_set_timers(timer_info) {
167 Ok(_) => Ok(SUCCESS),
168 Err(e) => {
169 log_error!("WakeupTestDevice::WakeupSetTimers failed: {:?}", e);
170 return error!(EINVAL);
171 }
172 }
173 }
174 CommandCode::WakeupTest => error!(ENOSYS),
175 CommandCode::WakeupHowManyTimers => error!(ENOSYS),
176 CommandCode::WakeupCancelTimers => error!(ENOSYS),
177 _ => default_ioctl(file, locked, current_task, request, arg),
178 }
179 }
180}