1use crossbeam::queue::SegQueue;
6use fuchsia_inspect::Inspector;
7use futures::FutureExt;
8use starnix_core::fileops_impl_nonseekable;
9use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt};
10use starnix_core::task::{CurrentTask, EventHandler, WaitCanceler, WaitQueue, Waiter};
11use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
12use starnix_core::vfs::{FileObject, FileOps, fileops_impl_noop_sync};
13use starnix_logging::{log_info, track_stub};
14use starnix_sync::{InputFileInputFileLock, LockDepMutex};
15use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
16use starnix_types::time::duration_from_timeval;
17use starnix_uapi::errors::Errno;
18use starnix_uapi::open_flags::OpenFlags;
19use starnix_uapi::user_address::{ArchSpecific, MultiArchUserRef, UserAddress, UserRef};
20use starnix_uapi::vfs::FdEvents;
21use starnix_uapi::{
22 ABS_CNT, ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_SLOT, ABS_MT_TRACKING_ID, BTN_EXTRA,
23 BTN_LEFT, BTN_MIDDLE, BTN_MISC, BTN_RIGHT, BTN_SIDE, BTN_TOUCH, EV_CNT, FF_CNT, INPUT_PROP_CNT,
24 INPUT_PROP_DIRECT, INPUT_PROP_POINTER, KEY_CNT, KEY_DOWN, KEY_LEFT, KEY_OK, KEY_POWER,
25 KEY_RIGHT, KEY_SLEEP, KEY_UP, KEY_VOLUMEDOWN, KEY_VOLUMEUP, LED_CNT, MSC_CNT, REL_CNT,
26 REL_HWHEEL, REL_WHEEL, REL_X, REL_Y, SW_CNT, errno, error, uapi,
27};
28use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering};
29use std::sync::{Arc, Weak};
30use zerocopy::IntoBytes as _; uapi::check_arch_independent_layout! {
33 input_id {}
34 input_absinfo {}
35}
36
37type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
38
39pub struct InputFileStatus {
40 pub fidl_events_received_count: AtomicU64,
50
51 pub fidl_events_ignored_count: AtomicU64,
53
54 pub fidl_events_unexpected_count: AtomicU64,
58
59 pub fidl_events_converted_count: AtomicU64,
61
62 pub uapi_events_generated_count: AtomicU64,
64
65 pub last_generated_uapi_event_timestamp_ns: AtomicI64,
67
68 pub uapi_events_read_count: AtomicU64,
70
71 pub last_read_uapi_event_timestamp_ns: AtomicI64,
73
74 pub fd_read_count: AtomicU64,
76
77 pub fd_notify_count: AtomicU64,
79
80 pub opened_without_nonblock: AtomicBool,
82
83 pub open_timestamp_ns: AtomicI64,
85
86 pub closed: AtomicBool,
88
89 pub close_timestamp_ns: AtomicI64,
91
92 pub input_file: LockDepMutex<Weak<InputFile>, InputFileInputFileLock>,
94}
95
96impl InputFileStatus {
97 fn new(node: &fuchsia_inspect::Node) -> Arc<Self> {
98 let status = Arc::new(Self {
99 fidl_events_received_count: AtomicU64::new(0),
100 fidl_events_ignored_count: AtomicU64::new(0),
101 fidl_events_unexpected_count: AtomicU64::new(0),
102 fidl_events_converted_count: AtomicU64::new(0),
103 uapi_events_generated_count: AtomicU64::new(0),
104 last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
105 uapi_events_read_count: AtomicU64::new(0),
106 last_read_uapi_event_timestamp_ns: AtomicI64::new(0),
107 fd_read_count: AtomicU64::new(0),
108 fd_notify_count: AtomicU64::new(0),
109 opened_without_nonblock: AtomicBool::new(false),
110 open_timestamp_ns: AtomicI64::new(0),
111 closed: AtomicBool::new(false),
112 close_timestamp_ns: AtomicI64::new(0),
113 input_file: Default::default(),
114 });
115
116 let cloned_status = status.clone();
117 node.record_lazy_values("status", move || {
118 let cloned_cloned_status = cloned_status.clone();
119 async move {
120 let is_dropped = cloned_cloned_status.input_file.lock().upgrade().is_none();
121 if is_dropped && !cloned_cloned_status.closed.load(Ordering::Relaxed) {
122 cloned_cloned_status.closed.store(true, Ordering::Relaxed);
123 }
124
125 let inspector = Inspector::default();
126 let root = inspector.root();
127 root.record_uint(
128 "fd_read_count",
129 cloned_cloned_status.fd_read_count.load(Ordering::Relaxed),
130 );
131 root.record_uint(
132 "fd_notify_count",
133 cloned_cloned_status.fd_notify_count.load(Ordering::Relaxed),
134 );
135 root.record_bool(
136 "opened_without_nonblock",
137 cloned_cloned_status.opened_without_nonblock.load(Ordering::Relaxed),
138 );
139 root.record_int(
140 "open_timestamp_ns",
141 cloned_cloned_status.open_timestamp_ns.load(Ordering::Relaxed),
142 );
143 root.record_bool("closed", cloned_cloned_status.closed.load(Ordering::Relaxed));
144 root.record_int(
145 "close_timestamp_ns",
146 cloned_cloned_status.close_timestamp_ns.load(Ordering::Relaxed),
147 );
148 root.record_uint(
149 "fidl_events_received_count",
150 cloned_cloned_status.fidl_events_received_count.load(Ordering::Relaxed),
151 );
152 root.record_uint(
153 "fidl_events_ignored_count",
154 cloned_cloned_status.fidl_events_ignored_count.load(Ordering::Relaxed),
155 );
156 root.record_uint(
157 "fidl_events_unexpected_count",
158 cloned_cloned_status.fidl_events_unexpected_count.load(Ordering::Relaxed),
159 );
160 root.record_uint(
161 "fidl_events_converted_count",
162 cloned_cloned_status.fidl_events_converted_count.load(Ordering::Relaxed),
163 );
164 root.record_uint(
165 "uapi_events_generated_count",
166 cloned_cloned_status.uapi_events_generated_count.load(Ordering::Relaxed),
167 );
168 root.record_int(
169 "last_generated_uapi_event_timestamp_ns",
170 cloned_cloned_status
171 .last_generated_uapi_event_timestamp_ns
172 .load(Ordering::Relaxed),
173 );
174 root.record_uint(
175 "uapi_events_read_count",
176 cloned_cloned_status.uapi_events_read_count.load(Ordering::Relaxed),
177 );
178 root.record_int(
179 "last_read_uapi_event_timestamp_ns",
180 cloned_cloned_status.last_read_uapi_event_timestamp_ns.load(Ordering::Relaxed),
181 );
182 Ok(inspector)
183 }
184 .boxed()
185 });
186
187 status
188 }
189
190 pub fn count_received_events(&self, count: u64) {
191 self.fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
192 }
193
194 pub fn count_ignored_events(&self, count: u64) {
195 self.fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
196 }
197
198 pub fn count_unexpected_events(&self, count: u64) {
199 self.fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
200 }
201
202 pub fn count_converted_events(&self, count: u64) {
203 self.fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
204 }
205
206 pub fn count_generated_events(&self, count: u64, event_time_ns: i64) {
207 self.uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
208 self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
209 }
210
211 pub fn count_read_events(&self, count: u64, event_time_ns: i64) {
212 self.uapi_events_read_count.fetch_add(count, Ordering::Relaxed);
213 self.last_read_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
214 }
215
216 pub fn count_fd_read_calls(&self) {
217 self.fd_read_count.fetch_add(1, Ordering::Relaxed);
218 }
219
220 pub fn count_fd_notify_calls(&self) {
221 self.fd_notify_count.fetch_add(1, Ordering::Relaxed);
222 }
223
224 pub fn set_opened_without_nonblock(&self) {
225 self.opened_without_nonblock.store(true, Ordering::Relaxed);
226 }
227
228 pub fn set_open_timestamp(&self, timestamp: i64) {
229 self.open_timestamp_ns.store(timestamp, Ordering::Relaxed);
230 }
231
232 pub fn set_closed(&self) {
233 self.closed.store(true, Ordering::Relaxed);
234 }
235
236 pub fn set_closed_timestamp(&self, timestamp: i64) {
237 self.close_timestamp_ns.store(timestamp, Ordering::Relaxed);
238 }
239}
240
241pub struct InputFile {
242 driver_version: u32,
243 input_id: uapi::input_id,
244 supported_event_types: BitSet<{ min_bytes(EV_CNT) }>,
245 supported_keys: BitSet<{ min_bytes(KEY_CNT) }>,
246 supported_position_attributes: BitSet<{ min_bytes(ABS_CNT) }>, supported_motion_attributes: BitSet<{ min_bytes(REL_CNT) }>, supported_switches: BitSet<{ min_bytes(SW_CNT) }>,
249 supported_leds: BitSet<{ min_bytes(LED_CNT) }>,
250 supported_haptics: BitSet<{ min_bytes(FF_CNT) }>, supported_misc_features: BitSet<{ min_bytes(MSC_CNT) }>,
252 properties: BitSet<{ min_bytes(INPUT_PROP_CNT) }>,
253 mt_slot_axis_info: uapi::input_absinfo,
254 mt_tracking_id_axis_info: uapi::input_absinfo,
255 x_axis_info: uapi::input_absinfo,
256 y_axis_info: uapi::input_absinfo,
257 events: SegQueue<LinuxEventWithTraceId>,
258 waiters: WaitQueue,
259 pub inspect_status: Option<Arc<InputFileStatus>>,
262
263 device_name: String,
265}
266
267pub struct LinuxEventWithTraceId {
268 pub event: uapi::input_event,
269 pub trace_id: Option<fuchsia_trace::Id>,
270}
271
272impl LinuxEventWithTraceId {
273 pub fn new(event: uapi::input_event) -> Self {
274 match event.type_ as u32 {
275 uapi::EV_SYN => {
276 let trace_id = fuchsia_trace::Id::new();
277 fuchsia_trace::duration!("input", "linux_event_create");
278 fuchsia_trace::flow_begin!("input", "linux_event", trace_id);
279 LinuxEventWithTraceId { event: event, trace_id: Some(trace_id) }
280 }
281 _ => LinuxEventWithTraceId { event: event, trace_id: None },
284 }
285 }
286}
287
288const fn min_bytes(n_bits: u32) -> usize {
290 ((n_bits as usize) + 7) / 8
291}
292
293fn keyboard_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
295 let mut attrs = BitSet::new();
296 attrs.set(INPUT_PROP_DIRECT);
297 attrs
298}
299
300fn touch_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
302 let mut attrs = BitSet::new();
303 attrs.set(BTN_TOUCH);
304 attrs.set(BTN_MISC); attrs.set(KEY_SLEEP);
306 attrs.set(KEY_UP);
307 attrs.set(KEY_LEFT);
308 attrs.set(KEY_RIGHT);
309 attrs.set(KEY_DOWN);
310
311 attrs
312}
313
314fn touch_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
316 let mut attrs = BitSet::new();
317 attrs.set(ABS_MT_SLOT);
318 attrs.set(ABS_MT_TRACKING_ID);
319 attrs.set(ABS_MT_POSITION_X);
320 attrs.set(ABS_MT_POSITION_Y);
321 attrs
322}
323
324fn touch_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
326 let mut attrs = BitSet::new();
327 attrs.set(INPUT_PROP_DIRECT);
328 attrs
329}
330
331fn keyboard_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
333 let mut attrs = BitSet::new();
334 for keycode in starnix_modules_input_event_conversion::keymap::KEY_MAP.all_linux_keycodes() {
335 if keycode >= BTN_MISC && keycode < KEY_OK {
340 continue;
341 }
342 if (keycode as usize) < KEY_CNT as usize {
343 attrs.set(keycode);
344 }
345 }
346 attrs.set(BTN_MISC);
347 attrs.set(KEY_POWER);
348 attrs.set(KEY_VOLUMEUP);
349 attrs.set(KEY_VOLUMEDOWN);
350 attrs
351}
352
353fn keyboard_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
355 BitSet::new()
356}
357
358fn mouse_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
360 let mut attrs = BitSet::new();
361 attrs.set(BTN_LEFT);
365 attrs.set(BTN_RIGHT);
366 attrs.set(BTN_MIDDLE);
367 attrs.set(BTN_SIDE);
368 attrs.set(BTN_EXTRA);
369 attrs
370}
371
372fn mouse_motion_attributes() -> BitSet<{ min_bytes(REL_CNT) }> {
374 let mut attrs = BitSet::new();
375 attrs.set(REL_X);
376 attrs.set(REL_Y);
377 attrs.set(REL_WHEEL);
378 attrs.set(REL_HWHEEL);
379 attrs
380}
381
382fn mouse_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
384 let mut attrs = BitSet::new();
385 attrs.set(INPUT_PROP_POINTER);
388 attrs
389}
390
391fn get_device_name(name: &str, input_id: &uapi::input_id) -> String {
395 format!("{}_{:04x}_{:04x}_v{}", name, input_id.vendor, input_id.product, input_id.version)
396}
397
398impl InputFile {
399 const DRIVER_VERSION: u32 = 0;
405
406 pub fn new_touch(
414 input_id: uapi::input_id,
415 width: i32,
416 height: i32,
417 node: &fuchsia_inspect::Node,
418 ) -> Self {
419 let device_name = get_device_name("starnix_touch", &input_id);
420 Self {
424 driver_version: Self::DRIVER_VERSION,
425 input_id,
426 supported_event_types: BitSet::list([uapi::EV_ABS]),
427 supported_keys: touch_key_attributes(),
428 supported_position_attributes: touch_position_attributes(),
429 supported_motion_attributes: BitSet::new(), supported_switches: BitSet::new(), supported_leds: BitSet::new(), supported_haptics: BitSet::new(), supported_misc_features: BitSet::new(), properties: touch_properties(),
435 mt_slot_axis_info: uapi::input_absinfo {
436 minimum: 0,
437 maximum: 10,
438 ..uapi::input_absinfo::default()
439 },
440 mt_tracking_id_axis_info: uapi::input_absinfo {
441 minimum: 0,
442 maximum: i32::MAX,
443 ..uapi::input_absinfo::default()
444 },
445 x_axis_info: uapi::input_absinfo {
446 minimum: 0,
447 maximum: i32::from(width),
448 ..uapi::input_absinfo::default()
451 },
452 y_axis_info: uapi::input_absinfo {
453 minimum: 0,
454 maximum: i32::from(height),
455 ..uapi::input_absinfo::default()
458 },
459 events: SegQueue::new(),
460 waiters: WaitQueue::default(),
461 inspect_status: Some(InputFileStatus::new(node)),
462 device_name,
463 }
464 }
465
466 pub fn new_keyboard(input_id: uapi::input_id, node: &fuchsia_inspect::Node) -> Self {
472 let device_name = get_device_name("starnix_buttons", &input_id);
473 Self {
474 driver_version: Self::DRIVER_VERSION,
475 input_id,
476 supported_event_types: BitSet::list([uapi::EV_KEY]),
477 supported_keys: keyboard_key_attributes(),
478 supported_position_attributes: keyboard_position_attributes(),
479 supported_motion_attributes: BitSet::new(), supported_switches: BitSet::new(), supported_leds: BitSet::new(), supported_haptics: BitSet::new(), supported_misc_features: BitSet::new(), properties: keyboard_properties(),
485 mt_slot_axis_info: uapi::input_absinfo::default(),
486 mt_tracking_id_axis_info: uapi::input_absinfo::default(),
487 x_axis_info: uapi::input_absinfo::default(),
488 y_axis_info: uapi::input_absinfo::default(),
489 events: SegQueue::new(),
490 waiters: WaitQueue::default(),
491 inspect_status: Some(InputFileStatus::new(node)),
492 device_name,
493 }
494 }
495
496 pub fn new_mouse(input_id: uapi::input_id, node: &fuchsia_inspect::Node) -> Self {
502 let device_name = get_device_name("starnix_mouse", &input_id);
503 Self {
504 driver_version: Self::DRIVER_VERSION,
505 input_id,
506 supported_event_types: BitSet::list([uapi::EV_KEY, uapi::EV_REL]),
510 supported_keys: mouse_key_attributes(),
511 supported_position_attributes: BitSet::new(), supported_motion_attributes: mouse_motion_attributes(),
513 supported_switches: BitSet::new(), supported_leds: BitSet::new(), supported_haptics: BitSet::new(), supported_misc_features: BitSet::new(), properties: mouse_properties(),
518 mt_slot_axis_info: uapi::input_absinfo::default(),
519 mt_tracking_id_axis_info: uapi::input_absinfo::default(),
520 x_axis_info: uapi::input_absinfo::default(),
521 y_axis_info: uapi::input_absinfo::default(),
522 events: SegQueue::new(),
523 waiters: WaitQueue::default(),
524 inspect_status: Some(InputFileStatus::new(node)),
525 device_name,
526 }
527 }
528
529 pub fn init_inspect_status(self: &Arc<Self>) {
530 if let Some(inspect) = &self.inspect_status {
531 *inspect.input_file.lock() = Arc::downgrade(self);
532 }
533 }
534
535 pub fn add_events(&self, events: Vec<uapi::input_event>) {
536 if events.is_empty() {
537 return;
538 }
539 if let Some(inspect) = &self.inspect_status {
540 inspect.count_fd_notify_calls();
541 }
542 for event in events {
543 self.events.push(LinuxEventWithTraceId::new(event));
544 }
545 self.waiters.notify_fd_events(FdEvents::POLLIN);
546 }
547
548 pub fn read_events(&self, limit: usize) -> Vec<LinuxEventWithTraceId> {
549 if let Some(inspect) = &self.inspect_status {
550 inspect.count_fd_read_calls();
551 }
552 let mut events = vec![];
553 for _ in 0..limit {
554 if let Some(event) = self.events.pop() {
555 events.push(event);
556 } else {
557 break;
558 }
559 }
560 events
564 }
565}
566
567pub(crate) const EVIOC_VAR_LEN_MASK: u32 = !(uapi::_IOC_SIZEMASK << uapi::_IOC_SIZESHIFT);
571
572pub(crate) const EVIOCGNAME_BASE: u32 = uapi::EVIOCGNAME_0 & EVIOC_VAR_LEN_MASK;
573pub(crate) const EVIOCGPHYS_BASE: u32 = uapi::EVIOCGPHYS_0 & EVIOC_VAR_LEN_MASK;
574pub(crate) const EVIOCGUNIQ_BASE: u32 = uapi::EVIOCGUNIQ_0 & EVIOC_VAR_LEN_MASK;
575pub(crate) const EVIOCGKEY_BASE: u32 = uapi::EVIOCGKEY_0 & EVIOC_VAR_LEN_MASK;
576pub(crate) const EVIOCGLED_BASE: u32 = uapi::EVIOCGLED_0 & EVIOC_VAR_LEN_MASK;
577pub(crate) const EVIOCGSND_BASE: u32 = uapi::EVIOCGSND_0 & EVIOC_VAR_LEN_MASK;
578pub(crate) const EVIOCGSW_BASE: u32 = uapi::EVIOCGSW_0 & EVIOC_VAR_LEN_MASK;
579pub(crate) const EVIOCGPROP_BASE: u32 = uapi::EVIOCGPROP & EVIOC_VAR_LEN_MASK;
580pub(crate) const EVIOCGBIT_0_BASE: u32 = uapi::EVIOCGBIT_0 & EVIOC_VAR_LEN_MASK;
581pub(crate) const EVIOCGBIT_EV_KEY_BASE: u32 = uapi::EVIOCGBIT_EV_KEY & EVIOC_VAR_LEN_MASK;
582pub(crate) const EVIOCGBIT_EV_ABS_BASE: u32 = uapi::EVIOCGBIT_EV_ABS & EVIOC_VAR_LEN_MASK;
583pub(crate) const EVIOCGBIT_EV_REL_BASE: u32 = uapi::EVIOCGBIT_EV_REL & EVIOC_VAR_LEN_MASK;
584pub(crate) const EVIOCGBIT_EV_SW_BASE: u32 = uapi::EVIOCGBIT_EV_SW & EVIOC_VAR_LEN_MASK;
585pub(crate) const EVIOCGBIT_EV_LED_BASE: u32 = uapi::EVIOCGBIT_EV_LED & EVIOC_VAR_LEN_MASK;
586pub(crate) const EVIOCGBIT_EV_FF_BASE: u32 = uapi::EVIOCGBIT_EV_FF & EVIOC_VAR_LEN_MASK;
587pub(crate) const EVIOCGBIT_EV_MSC_BASE: u32 = uapi::EVIOCGBIT_EV_MSC & EVIOC_VAR_LEN_MASK;
588pub(crate) const EVIOCGBIT_EV_SND_BASE: u32 = uapi::EVIOCGBIT_EV_SND & EVIOC_VAR_LEN_MASK;
589
590impl FileOps for InputFile {
591 fileops_impl_nonseekable!();
592 fileops_impl_noop_sync!();
593
594 fn open(&self, file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
595 if let Some(inspect) = &self.inspect_status {
596 inspect.set_open_timestamp(zx::MonotonicInstant::get().into_nanos());
597 if (file.flags() & OpenFlags::NONBLOCK) != OpenFlags::NONBLOCK {
598 inspect.set_opened_without_nonblock();
599 }
600 }
601 Ok(())
602 }
603
604 fn close(
605 self: Box<Self>,
606 _file: &starnix_core::vfs::FileObjectState,
607 _current_task: &CurrentTask,
608 ) {
609 if let Some(inspect) = &self.inspect_status {
610 inspect.set_closed();
611 inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
612 }
613 }
614
615 fn ioctl(
616 &self,
617 _file: &FileObject,
618 current_task: &CurrentTask,
619 request: u32,
620 arg: SyscallArg,
621 ) -> Result<SyscallResult, Errno> {
622 let user_addr = UserAddress::from(arg);
623 match request {
624 uapi::EVIOCGVERSION => {
625 current_task.write_object(UserRef::new(user_addr), &self.driver_version)?;
626 Ok(SUCCESS)
627 }
628 uapi::EVIOCGID => {
629 current_task.write_object(UserRef::new(user_addr), &self.input_id)?;
630 Ok(SUCCESS)
631 }
632 uapi::EVIOCGABS_MT_SLOT => {
633 current_task.write_object(UserRef::new(user_addr), &self.mt_slot_axis_info)?;
634 Ok(SUCCESS)
635 }
636 uapi::EVIOCGABS_MT_TRACKING_ID => {
637 current_task
638 .write_object(UserRef::new(user_addr), &self.mt_tracking_id_axis_info)?;
639 Ok(SUCCESS)
640 }
641 uapi::EVIOCGABS_MT_POSITION_X => {
642 current_task.write_object(UserRef::new(user_addr), &self.x_axis_info)?;
643 Ok(SUCCESS)
644 }
645 uapi::EVIOCGABS_MT_POSITION_Y => {
646 current_task.write_object(UserRef::new(user_addr), &self.y_axis_info)?;
647 Ok(SUCCESS)
648 }
649 uapi::EVIOCGRAB => {
650 track_stub!(TODO("https://fxbug.dev/322873200"), "EVIOCGRAB");
652 Ok(SUCCESS)
653 }
654
655 request_with_params => {
656 let buffer_bytes_count =
658 ((request_with_params >> uapi::_IOC_SIZESHIFT) & uapi::_IOC_SIZEMASK) as usize;
659
660 let dir = (request_with_params >> uapi::_IOC_DIRSHIFT) & uapi::_IOC_DIRMASK;
661 let ioc_type = (request_with_params >> uapi::_IOC_TYPESHIFT) & uapi::_IOC_TYPEMASK;
662
663 if dir != uapi::_IOC_READ || ioc_type != (b'E' as u32) {
665 track_stub!(
666 TODO("https://fxbug.dev/322873200"),
667 "input ioctl invalid type or dir",
668 request_with_params
669 );
670 return error!(EINVAL);
671 }
672
673 let write_bits = |bits: &[u8]| -> Result<SyscallResult, Errno> {
677 if buffer_bytes_count == 0 {
678 return Ok(SUCCESS);
679 }
680 current_task.zero(user_addr, buffer_bytes_count)?;
682 let to_copy = std::cmp::min(bits.len(), buffer_bytes_count);
683 current_task.write_memory(user_addr, &bits[..to_copy])?;
684 Ok(SUCCESS)
685 };
686
687 let write_string = |bytes: &[u8]| -> Result<SyscallResult, Errno> {
690 if buffer_bytes_count == 0 {
691 return Ok(SUCCESS);
692 }
693 current_task.zero(user_addr, buffer_bytes_count)?;
695 let to_copy = std::cmp::min(bytes.len(), buffer_bytes_count.saturating_sub(1));
696 current_task.write_memory(user_addr, &bytes[..to_copy])?;
697 Ok((to_copy + 1).into())
700 };
701
702 match request_with_params & EVIOC_VAR_LEN_MASK {
703 EVIOCGBIT_0_BASE => write_bits(&self.supported_event_types.bytes),
704 EVIOCGBIT_EV_KEY_BASE => write_bits(&self.supported_keys.bytes),
705 EVIOCGBIT_EV_ABS_BASE => write_bits(&self.supported_position_attributes.bytes),
706 EVIOCGBIT_EV_REL_BASE => write_bits(&self.supported_motion_attributes.bytes),
707 EVIOCGBIT_EV_SW_BASE => write_bits(&self.supported_switches.bytes),
708 EVIOCGBIT_EV_LED_BASE => write_bits(&self.supported_leds.bytes),
709 EVIOCGBIT_EV_FF_BASE => write_bits(&self.supported_haptics.bytes),
710 EVIOCGBIT_EV_MSC_BASE => write_bits(&self.supported_misc_features.bytes),
711 EVIOCGBIT_EV_SND_BASE => write_bits(&[]),
712 EVIOCGPROP_BASE => write_bits(&self.properties.bytes),
713 EVIOCGNAME_BASE => write_string(self.device_name.as_bytes()),
714 EVIOCGPHYS_BASE => {
715 let phys = format!("starnix/{}", self.device_name);
716 write_string(phys.as_bytes())
717 }
718 EVIOCGUNIQ_BASE => {
719 error!(ENOENT)
722 }
723 EVIOCGKEY_BASE | EVIOCGLED_BASE | EVIOCGSND_BASE | EVIOCGSW_BASE => {
724 track_stub!(
740 TODO("https://fxbug.dev/322873200"),
741 "evdev current-state query",
742 request_with_params
743 );
744 write_bits(&[])
745 }
746 _ => {
747 track_stub!(
748 TODO("https://fxbug.dev/322873200"),
749 "input ioctl",
750 request_with_params
751 );
752 error!(EINVAL)
753 }
754 }
755 }
756 }
757 }
758
759 fn read(
760 &self,
761 _file: &FileObject,
762 current_task: &CurrentTask,
763 offset: usize,
764 data: &mut dyn OutputBuffer,
765 ) -> Result<usize, Errno> {
766 fuchsia_trace::duration!("input", "InputFile::read");
767 debug_assert!(offset == 0);
768 let input_event_size = InputEventPtr::size_of_object_for(current_task);
769
770 let limit = data.available() / input_event_size;
773 let events = self.read_events(limit);
774 if events.is_empty() {
775 log_info!("read() returning EAGAIN");
777 return error!(EAGAIN);
778 }
779
780 let last_event_timeval = events.last().expect("events is nonempty").event.time;
781 let last_event_time_ns = duration_from_timeval::<zx::MonotonicTimeline>(last_event_timeval)
782 .unwrap()
783 .into_nanos();
784 self.inspect_status
785 .clone()
786 .map(|status| status.count_read_events(events.len() as u64, last_event_time_ns));
787
788 for event in &events {
789 if let Some(trace_id) = event.trace_id {
790 fuchsia_trace::duration!("input", "linux_event_read");
791 fuchsia_trace::flow_end!("input", "linux_event", trace_id);
792 }
793 }
794
795 if current_task.is_arch32() {
796 let events: Result<Vec<uapi::arch32::input_event>, _> =
797 events.iter().map(|e| uapi::arch32::input_event::try_from(e.event)).collect();
798 let events = events.map_err(|_| errno!(EINVAL))?;
799 data.write_all(events.as_bytes())
800 } else {
801 let events: Vec<uapi::input_event> = events.iter().map(|e| e.event).collect();
802 data.write_all(events.as_bytes())
803 }
804 }
805
806 fn write(
807 &self,
808 _file: &FileObject,
809 _current_task: &CurrentTask,
810 offset: usize,
811 _data: &mut dyn InputBuffer,
812 ) -> Result<usize, Errno> {
813 debug_assert!(offset == 0);
814 track_stub!(TODO("https://fxbug.dev/322874385"), "write() on input device");
815 error!(EOPNOTSUPP)
816 }
817
818 fn wait_async(
819 &self,
820 _file: &FileObject,
821 _current_task: &CurrentTask,
822 waiter: &Waiter,
823 events: FdEvents,
824 handler: EventHandler,
825 ) -> Option<WaitCanceler> {
826 Some(self.waiters.wait_async_fd_events(waiter, events, handler))
827 }
828
829 fn query_events(
830 &self,
831 _file: &FileObject,
832 _current_task: &CurrentTask,
833 ) -> Result<FdEvents, Errno> {
834 Ok(if self.events.is_empty() { FdEvents::empty() } else { FdEvents::POLLIN })
835 }
836}
837
838pub struct ArcInputFile(pub Arc<InputFile>);
839
840impl FileOps for ArcInputFile {
841 fileops_impl_nonseekable!();
842 fileops_impl_noop_sync!();
843
844 fn open(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
845 self.0.as_ref().open(file, current_task)
846 }
847
848 fn close(
849 self: Box<Self>,
850 _file: &starnix_core::vfs::FileObjectState,
851 _current_task: &CurrentTask,
852 ) {
853 let arc_file = *self;
854 if let Some(inspect) = &arc_file.0.inspect_status {
855 inspect.set_closed();
856 inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
857 }
858 }
859
860 fn ioctl(
861 &self,
862 file: &FileObject,
863 current_task: &CurrentTask,
864 request: u32,
865 arg: SyscallArg,
866 ) -> Result<SyscallResult, Errno> {
867 self.0.as_ref().ioctl(file, current_task, request, arg)
868 }
869
870 fn read(
871 &self,
872 file: &FileObject,
873 current_task: &CurrentTask,
874 offset: usize,
875 data: &mut dyn OutputBuffer,
876 ) -> Result<usize, Errno> {
877 self.0.as_ref().read(file, current_task, offset, data)
878 }
879
880 fn write(
881 &self,
882 file: &FileObject,
883 current_task: &CurrentTask,
884 offset: usize,
885 data: &mut dyn InputBuffer,
886 ) -> Result<usize, Errno> {
887 self.0.as_ref().write(file, current_task, offset, data)
888 }
889
890 fn wait_async(
891 &self,
892 file: &FileObject,
893 current_task: &CurrentTask,
894 waiter: &Waiter,
895 events: FdEvents,
896 handler: EventHandler,
897 ) -> Option<WaitCanceler> {
898 self.0.as_ref().wait_async(file, current_task, waiter, events, handler)
899 }
900
901 fn query_events(
902 &self,
903 file: &FileObject,
904 current_task: &CurrentTask,
905 ) -> Result<FdEvents, Errno> {
906 self.0.as_ref().query_events(file, current_task)
907 }
908}
909
910pub struct BitSet<const NUM_BYTES: usize> {
911 bytes: [u8; NUM_BYTES],
912}
913
914impl<const NUM_BYTES: usize> BitSet<{ NUM_BYTES }> {
915 pub const fn new() -> Self {
916 Self { bytes: [0; NUM_BYTES] }
917 }
918
919 pub const fn list<const N: usize>(bits: [u32; N]) -> Self {
920 let mut bitset = Self::new();
921 let mut i = 0;
922 while i < bits.len() {
923 bitset.set(bits[i]);
924 i += 1;
925 }
926 bitset
927 }
928
929 pub const fn set(&mut self, bitnum: u32) {
930 let bitnum = bitnum as usize;
931 let byte = bitnum / 8;
932 let bit = bitnum % 8;
933 self.bytes[byte] |= 1 << bit;
934 }
935
936 #[cfg(test)]
937 pub fn get(&self, bitnum: u32) -> bool {
938 let bitnum = bitnum as usize;
939 let byte = bitnum / 8;
940 let bit = bitnum % 8;
941 (self.bytes[byte] & (1 << bit)) != 0
942 }
943}
944
945#[cfg(test)]
946mod tests {
947 use super::*;
948 use std::sync::atomic::Ordering;
949
950 #[test]
951 fn test_keyboard_input_file_attributes() {
952 let inspector = fuchsia_inspect::Inspector::default();
953 let node = inspector.root();
954 let keyboard_file = InputFile::new_keyboard(
955 uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
956 node,
957 );
958
959 assert!(keyboard_file.supported_event_types.get(uapi::EV_KEY));
961 assert!(!keyboard_file.supported_event_types.get(uapi::EV_REL));
962 assert!(!keyboard_file.supported_event_types.get(uapi::EV_ABS));
963
964 assert!(keyboard_file.supported_keys.get(uapi::KEY_A));
966 assert!(keyboard_file.supported_keys.get(uapi::KEY_Z));
967 assert!(keyboard_file.supported_keys.get(uapi::KEY_1));
968 assert!(keyboard_file.supported_keys.get(uapi::KEY_ENTER));
969 assert!(keyboard_file.supported_keys.get(uapi::KEY_SPACE));
970 assert!(keyboard_file.supported_keys.get(uapi::KEY_ESC));
971 assert!(keyboard_file.supported_keys.get(uapi::KEY_TAB));
972 assert!(keyboard_file.supported_keys.get(uapi::KEY_BACKSPACE));
973 assert!(keyboard_file.supported_keys.get(uapi::KEY_LEFTSHIFT));
974 assert!(keyboard_file.supported_keys.get(uapi::KEY_LEFTCTRL));
975 assert!(keyboard_file.supported_keys.get(uapi::KEY_LEFTALT));
976 assert!(keyboard_file.supported_keys.get(uapi::KEY_POWER));
977 assert!(keyboard_file.supported_keys.get(uapi::KEY_VOLUMEUP));
978 assert!(keyboard_file.supported_keys.get(uapi::KEY_VOLUMEDOWN));
979 assert!(keyboard_file.supported_keys.get(uapi::BTN_MISC));
980
981 assert!(keyboard_file.properties.get(uapi::INPUT_PROP_DIRECT));
983 assert!(!keyboard_file.properties.get(uapi::INPUT_PROP_POINTER));
984 }
985
986 #[test]
987 fn test_read_events_no_notify_when_buffer_full() {
988 let inspector = fuchsia_inspect::Inspector::default();
989 let node = inspector.root();
990 let input_file = InputFile::new_touch(
991 uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
992 100,
993 100,
994 node,
995 );
996
997 let event1 = uapi::input_event {
999 type_: uapi::EV_KEY as u16,
1000 code: 1,
1001 value: 1,
1002 ..Default::default()
1003 };
1004 let event2 = uapi::input_event {
1005 type_: uapi::EV_KEY as u16,
1006 code: 2,
1007 value: 1,
1008 ..Default::default()
1009 };
1010 input_file.add_events(vec![event1, event2]);
1011
1012 let notify_count =
1014 input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
1015 assert_eq!(notify_count, 1);
1016
1017 let events = input_file.read_events(1);
1019 assert_eq!(events.len(), 1);
1020
1021 let notify_count =
1023 input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
1024 assert_eq!(notify_count, 1);
1025 }
1026
1027 #[test]
1028 fn test_bitset_set_and_get() {
1029 let mut bitset = BitSet::<4>::new();
1030 assert!(!bitset.get(0));
1031 assert!(!bitset.get(15));
1032 assert!(!bitset.get(31));
1033
1034 bitset.set(0);
1035 bitset.set(15);
1036 bitset.set(31);
1037
1038 assert!(bitset.get(0));
1039 assert!(bitset.get(15));
1040 assert!(bitset.get(31));
1041 assert!(!bitset.get(1));
1042 assert!(!bitset.get(14));
1043 assert!(!bitset.get(30));
1044 }
1045
1046 #[test]
1047 fn test_mouse_capabilities() {
1048 let inspector = fuchsia_inspect::Inspector::default();
1049 let node = inspector.root();
1050 let mouse_file = InputFile::new_mouse(
1051 uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1052 node,
1053 );
1054
1055 assert!(mouse_file.supported_event_types.get(uapi::EV_KEY));
1057 assert!(mouse_file.supported_event_types.get(uapi::EV_REL));
1058 assert!(!mouse_file.supported_event_types.get(uapi::EV_ABS));
1059
1060 assert!(mouse_file.supported_keys.get(BTN_LEFT));
1062 assert!(mouse_file.supported_keys.get(BTN_RIGHT));
1063 assert!(mouse_file.supported_keys.get(BTN_MIDDLE));
1064 assert!(mouse_file.supported_keys.get(BTN_SIDE));
1065 assert!(mouse_file.supported_keys.get(BTN_EXTRA));
1066 assert!(!mouse_file.supported_keys.get(BTN_TOUCH));
1067
1068 assert!(mouse_file.supported_motion_attributes.get(REL_X));
1070 assert!(mouse_file.supported_motion_attributes.get(REL_Y));
1071 assert!(mouse_file.supported_motion_attributes.get(REL_WHEEL));
1072 assert!(mouse_file.supported_motion_attributes.get(REL_HWHEEL));
1073
1074 assert!(mouse_file.properties.get(INPUT_PROP_POINTER));
1076 assert!(!mouse_file.properties.get(INPUT_PROP_DIRECT));
1077 }
1078}