Skip to main content

starnix_modules_input/
input_file.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 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::{FileOpsCore, Locked, Mutex, Unlocked};
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_MISC,
23    BTN_TOUCH, EV_CNT, FF_CNT, INPUT_PROP_CNT, INPUT_PROP_DIRECT, KEY_CNT, KEY_DOWN, KEY_LEFT,
24    KEY_POWER, KEY_RIGHT, KEY_SLEEP, KEY_UP, KEY_VOLUMEDOWN, LED_CNT, MSC_CNT, REL_CNT, REL_WHEEL,
25    SW_CNT, errno, error, uapi,
26};
27use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering};
28use std::sync::{Arc, Weak};
29use zerocopy::IntoBytes as _; // for `as_bytes()`
30
31uapi::check_arch_independent_layout! {
32    input_id {}
33    input_absinfo {}
34}
35
36type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
37
38pub struct InputFileStatus {
39    /// The number of FIDL events received by this file from Fuchsia input system.
40    ///
41    /// We expect:
42    /// fidl_events_received_count = fidl_events_ignored_count +
43    ///                              fidl_events_unexpected_count +
44    ///                              fidl_events_converted_count
45    /// otherwise starnix ignored events unexpectedly.
46    ///
47    /// fidl_events_unexpected_count should be 0, if not it hints issues from upstream of ui stack.
48    pub fidl_events_received_count: AtomicU64,
49
50    /// The number of FIDL events ignored to this module’s representation of TouchEvent.
51    pub fidl_events_ignored_count: AtomicU64,
52
53    /// The unexpected number of FIDL events reached to this module should be filtered out
54    /// earlier in the UI stack.
55    /// It maybe unexpected format or unexpected order.
56    pub fidl_events_unexpected_count: AtomicU64,
57
58    /// The number of FIDL events converted to this module’s representation of TouchEvent.
59    pub fidl_events_converted_count: AtomicU64,
60
61    /// The number of uapi::input_events generated from TouchEvents.
62    pub uapi_events_generated_count: AtomicU64,
63
64    /// The event time of the last generated uapi::input_event.
65    pub last_generated_uapi_event_timestamp_ns: AtomicI64,
66
67    /// The number of uapi::input_events read from this input file by external process.
68    pub uapi_events_read_count: AtomicU64,
69
70    /// The event time of the last uapi::input_event read by external process.
71    pub last_read_uapi_event_timestamp_ns: AtomicI64,
72
73    /// Number of read calls.
74    pub fd_read_count: AtomicU64,
75
76    /// Number of notify calls.
77    pub fd_notify_count: AtomicU64,
78
79    /// Whether the file was opened without the NONBLOCK flag.
80    pub opened_without_nonblock: AtomicBool,
81
82    /// The timestamp when the file was opened.
83    pub open_timestamp_ns: AtomicI64,
84
85    /// Whether the file was closed (dropped).
86    pub closed: AtomicBool,
87
88    /// The timestamp when the file was closed.
89    pub close_timestamp_ns: AtomicI64,
90
91    /// The weak pointer to the InputFile.
92    pub input_file: Mutex<Weak<InputFile>>,
93}
94
95impl InputFileStatus {
96    fn new(node: &fuchsia_inspect::Node) -> Arc<Self> {
97        let status = Arc::new(Self {
98            fidl_events_received_count: AtomicU64::new(0),
99            fidl_events_ignored_count: AtomicU64::new(0),
100            fidl_events_unexpected_count: AtomicU64::new(0),
101            fidl_events_converted_count: AtomicU64::new(0),
102            uapi_events_generated_count: AtomicU64::new(0),
103            last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
104            uapi_events_read_count: AtomicU64::new(0),
105            last_read_uapi_event_timestamp_ns: AtomicI64::new(0),
106            fd_read_count: AtomicU64::new(0),
107            fd_notify_count: AtomicU64::new(0),
108            opened_without_nonblock: AtomicBool::new(false),
109            open_timestamp_ns: AtomicI64::new(0),
110            closed: AtomicBool::new(false),
111            close_timestamp_ns: AtomicI64::new(0),
112            input_file: Mutex::new(Weak::new()),
113        });
114
115        let cloned_status = status.clone();
116        node.record_lazy_values("status", move || {
117            let cloned_cloned_status = cloned_status.clone();
118            async move {
119                let is_dropped = cloned_cloned_status.input_file.lock().upgrade().is_none();
120                if is_dropped && !cloned_cloned_status.closed.load(Ordering::Relaxed) {
121                    cloned_cloned_status.closed.store(true, Ordering::Relaxed);
122                }
123
124                let inspector = Inspector::default();
125                let root = inspector.root();
126                root.record_uint(
127                    "fd_read_count",
128                    cloned_cloned_status.fd_read_count.load(Ordering::Relaxed),
129                );
130                root.record_uint(
131                    "fd_notify_count",
132                    cloned_cloned_status.fd_notify_count.load(Ordering::Relaxed),
133                );
134                root.record_bool(
135                    "opened_without_nonblock",
136                    cloned_cloned_status.opened_without_nonblock.load(Ordering::Relaxed),
137                );
138                root.record_int(
139                    "open_timestamp_ns",
140                    cloned_cloned_status.open_timestamp_ns.load(Ordering::Relaxed),
141                );
142                root.record_bool("closed", cloned_cloned_status.closed.load(Ordering::Relaxed));
143                root.record_int(
144                    "close_timestamp_ns",
145                    cloned_cloned_status.close_timestamp_ns.load(Ordering::Relaxed),
146                );
147                root.record_uint(
148                    "fidl_events_received_count",
149                    cloned_cloned_status.fidl_events_received_count.load(Ordering::Relaxed),
150                );
151                root.record_uint(
152                    "fidl_events_ignored_count",
153                    cloned_cloned_status.fidl_events_ignored_count.load(Ordering::Relaxed),
154                );
155                root.record_uint(
156                    "fidl_events_unexpected_count",
157                    cloned_cloned_status.fidl_events_unexpected_count.load(Ordering::Relaxed),
158                );
159                root.record_uint(
160                    "fidl_events_converted_count",
161                    cloned_cloned_status.fidl_events_converted_count.load(Ordering::Relaxed),
162                );
163                root.record_uint(
164                    "uapi_events_generated_count",
165                    cloned_cloned_status.uapi_events_generated_count.load(Ordering::Relaxed),
166                );
167                root.record_int(
168                    "last_generated_uapi_event_timestamp_ns",
169                    cloned_cloned_status
170                        .last_generated_uapi_event_timestamp_ns
171                        .load(Ordering::Relaxed),
172                );
173                root.record_uint(
174                    "uapi_events_read_count",
175                    cloned_cloned_status.uapi_events_read_count.load(Ordering::Relaxed),
176                );
177                root.record_int(
178                    "last_read_uapi_event_timestamp_ns",
179                    cloned_cloned_status.last_read_uapi_event_timestamp_ns.load(Ordering::Relaxed),
180                );
181                Ok(inspector)
182            }
183            .boxed()
184        });
185
186        status
187    }
188
189    pub fn count_received_events(&self, count: u64) {
190        self.fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
191    }
192
193    pub fn count_ignored_events(&self, count: u64) {
194        self.fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
195    }
196
197    pub fn count_unexpected_events(&self, count: u64) {
198        self.fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
199    }
200
201    pub fn count_converted_events(&self, count: u64) {
202        self.fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
203    }
204
205    pub fn count_generated_events(&self, count: u64, event_time_ns: i64) {
206        self.uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
207        self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
208    }
209
210    pub fn count_read_events(&self, count: u64, event_time_ns: i64) {
211        self.uapi_events_read_count.fetch_add(count, Ordering::Relaxed);
212        self.last_read_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
213    }
214
215    pub fn count_fd_read_calls(&self) {
216        self.fd_read_count.fetch_add(1, Ordering::Relaxed);
217    }
218
219    pub fn count_fd_notify_calls(&self) {
220        self.fd_notify_count.fetch_add(1, Ordering::Relaxed);
221    }
222
223    pub fn set_opened_without_nonblock(&self) {
224        self.opened_without_nonblock.store(true, Ordering::Relaxed);
225    }
226
227    pub fn set_open_timestamp(&self, timestamp: i64) {
228        self.open_timestamp_ns.store(timestamp, Ordering::Relaxed);
229    }
230
231    pub fn set_closed(&self) {
232        self.closed.store(true, Ordering::Relaxed);
233    }
234
235    pub fn set_closed_timestamp(&self, timestamp: i64) {
236        self.close_timestamp_ns.store(timestamp, Ordering::Relaxed);
237    }
238}
239
240pub struct InputFile {
241    driver_version: u32,
242    input_id: uapi::input_id,
243    supported_event_types: BitSet<{ min_bytes(EV_CNT) }>,
244    supported_keys: BitSet<{ min_bytes(KEY_CNT) }>,
245    supported_position_attributes: BitSet<{ min_bytes(ABS_CNT) }>, // ABSolute position
246    supported_motion_attributes: BitSet<{ min_bytes(REL_CNT) }>,   // RELative motion
247    supported_switches: BitSet<{ min_bytes(SW_CNT) }>,
248    supported_leds: BitSet<{ min_bytes(LED_CNT) }>,
249    supported_haptics: BitSet<{ min_bytes(FF_CNT) }>, // 'F'orce 'F'eedback
250    supported_misc_features: BitSet<{ min_bytes(MSC_CNT) }>,
251    properties: BitSet<{ min_bytes(INPUT_PROP_CNT) }>,
252    mt_slot_axis_info: uapi::input_absinfo,
253    mt_tracking_id_axis_info: uapi::input_absinfo,
254    x_axis_info: uapi::input_absinfo,
255    y_axis_info: uapi::input_absinfo,
256    events: SegQueue<LinuxEventWithTraceId>,
257    waiters: WaitQueue,
258    // InputFile will be initialized with an InputFileStatus that holds Inspect data
259    // `None` for Uinput InputFiles
260    pub inspect_status: Option<Arc<InputFileStatus>>,
261
262    // A descriptive device name. Should contain only alphanumerics and `_`.
263    device_name: String,
264}
265
266pub struct LinuxEventWithTraceId {
267    pub event: uapi::input_event,
268    pub trace_id: Option<fuchsia_trace::Id>,
269}
270
271impl LinuxEventWithTraceId {
272    pub fn new(event: uapi::input_event) -> Self {
273        match event.type_ as u32 {
274            uapi::EV_SYN => {
275                let trace_id = fuchsia_trace::Id::new();
276                fuchsia_trace::duration!("input", "linux_event_create");
277                fuchsia_trace::flow_begin!("input", "linux_event", trace_id);
278                LinuxEventWithTraceId { event: event, trace_id: Some(trace_id) }
279            }
280            // EV_SYN marks the end of a complete input event. Other event types are its properties,
281            // so they don't initiate a trace.
282            _ => LinuxEventWithTraceId { event: event, trace_id: None },
283        }
284    }
285}
286
287/// Returns the minimum number of bytes required to store `n_bits` bits.
288const fn min_bytes(n_bits: u32) -> usize {
289    ((n_bits as usize) + 7) / 8
290}
291
292/// Returns appropriate `INPUT_PROP`-erties for a keyboard device.
293fn keyboard_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
294    let mut attrs = BitSet::new();
295    attrs.set(INPUT_PROP_DIRECT);
296    attrs
297}
298
299/// Returns appropriate `KEY`-board related flags for a touchscreen device.
300fn touch_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
301    let mut attrs = BitSet::new();
302    attrs.set(BTN_TOUCH);
303    attrs.set(BTN_MISC); // Include BTN_MISC as a catchall key event.
304    attrs.set(KEY_SLEEP);
305    attrs.set(KEY_UP);
306    attrs.set(KEY_LEFT);
307    attrs.set(KEY_RIGHT);
308    attrs.set(KEY_DOWN);
309
310    attrs
311}
312
313/// Returns appropriate `ABS`-olute position related flags for a touchscreen device.
314fn touch_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
315    let mut attrs = BitSet::new();
316    attrs.set(ABS_MT_SLOT);
317    attrs.set(ABS_MT_TRACKING_ID);
318    attrs.set(ABS_MT_POSITION_X);
319    attrs.set(ABS_MT_POSITION_Y);
320    attrs
321}
322
323/// Returns appropriate `INPUT_PROP`-erties for a touchscreen device.
324fn touch_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
325    let mut attrs = BitSet::new();
326    attrs.set(INPUT_PROP_DIRECT);
327    attrs
328}
329
330/// Returns appropriate `KEY`-board related flags for a keyboard device.
331fn keyboard_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
332    let mut attrs = BitSet::new();
333    attrs.set(BTN_MISC);
334    attrs.set(KEY_POWER);
335    attrs.set(KEY_VOLUMEDOWN);
336    attrs
337}
338
339/// Returns appropriate `ABS`-olute position related flags for a keyboard device.
340fn keyboard_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
341    BitSet::new()
342}
343
344fn mouse_wheel_attributes() -> BitSet<{ min_bytes(REL_CNT) }> {
345    let mut attrs = BitSet::new();
346    attrs.set(REL_WHEEL);
347    attrs
348}
349
350/// Makes a device name string from a name and device ID details.
351///
352/// For practical reasons the device name should contain alphanumerics and `_`.
353fn get_device_name(name: &str, input_id: &uapi::input_id) -> String {
354    format!("{}_{:04x}_{:04x}_v{}", name, input_id.vendor, input_id.product, input_id.version)
355}
356
357impl InputFile {
358    // Per https://www.linuxjournal.com/article/6429, the driver version is 32-bits wide,
359    // and interpreted as:
360    // * [31-16]: version
361    // * [15-08]: minor
362    // * [07-00]: patch level
363    const DRIVER_VERSION: u32 = 0;
364
365    /// Creates an `InputFile` instance suitable for emulating a touchscreen.
366    ///
367    /// # Parameters
368    /// - `input_id`: device's bustype, vendor id, product id, and version.
369    /// - `width`: width of screen.
370    /// - `height`: height of screen.
371    /// - `inspect_status`: The inspect status for the parent device of "touch_input_file".
372    pub fn new_touch(
373        input_id: uapi::input_id,
374        width: i32,
375        height: i32,
376        node: &fuchsia_inspect::Node,
377    ) -> Self {
378        let device_name = get_device_name("starnix_touch", &input_id);
379        // Fuchsia scales the position reported by the touch sensor to fit view coordinates.
380        // Hence, the range of touch positions is exactly the same as the range of view
381        // coordinates.
382        Self {
383            driver_version: Self::DRIVER_VERSION,
384            input_id,
385            supported_event_types: BitSet::list([uapi::EV_ABS]),
386            supported_keys: touch_key_attributes(),
387            supported_position_attributes: touch_position_attributes(),
388            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
389            supported_switches: BitSet::new(),          // None supported
390            supported_leds: BitSet::new(),              // None supported
391            supported_haptics: BitSet::new(),           // None supported
392            supported_misc_features: BitSet::new(),     // None supported
393            properties: touch_properties(),
394            mt_slot_axis_info: uapi::input_absinfo {
395                minimum: 0,
396                maximum: 10,
397                ..uapi::input_absinfo::default()
398            },
399            mt_tracking_id_axis_info: uapi::input_absinfo {
400                minimum: 0,
401                maximum: i32::MAX,
402                ..uapi::input_absinfo::default()
403            },
404            x_axis_info: uapi::input_absinfo {
405                minimum: 0,
406                maximum: i32::from(width),
407                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
408                // X position.
409                ..uapi::input_absinfo::default()
410            },
411            y_axis_info: uapi::input_absinfo {
412                minimum: 0,
413                maximum: i32::from(height),
414                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
415                // Y position.
416                ..uapi::input_absinfo::default()
417            },
418            events: SegQueue::new(),
419            waiters: WaitQueue::default(),
420            inspect_status: Some(InputFileStatus::new(node)),
421            device_name,
422        }
423    }
424
425    /// Creates an `InputFile` instance suitable for emulating a keyboard.
426    ///
427    /// # Parameters
428    /// - `input_id`: device's bustype, vendor id, product id, and version.
429    /// - `inspect_status`: The inspect status for the parent device of "keyboard_input_file".
430    pub fn new_keyboard(input_id: uapi::input_id, node: &fuchsia_inspect::Node) -> Self {
431        let device_name = get_device_name("starnix_buttons", &input_id);
432        Self {
433            driver_version: Self::DRIVER_VERSION,
434            input_id,
435            supported_event_types: BitSet::list([uapi::EV_KEY]),
436            supported_keys: keyboard_key_attributes(),
437            supported_position_attributes: keyboard_position_attributes(),
438            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
439            supported_switches: BitSet::new(),          // None supported
440            supported_leds: BitSet::new(),              // None supported
441            supported_haptics: BitSet::new(),           // None supported
442            supported_misc_features: BitSet::new(),     // None supported
443            properties: keyboard_properties(),
444            mt_slot_axis_info: uapi::input_absinfo::default(),
445            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
446            x_axis_info: uapi::input_absinfo::default(),
447            y_axis_info: uapi::input_absinfo::default(),
448            events: SegQueue::new(),
449            waiters: WaitQueue::default(),
450            inspect_status: Some(InputFileStatus::new(node)),
451            device_name,
452        }
453    }
454
455    /// Creates an `InputFile` instance suitable for emulating a mouse wheel.
456    ///
457    /// # Parameters
458    /// - `input_id`: device's bustype, vendor id, product id, and version.
459    /// - `inspect_status`: The inspect status for the parent device of "mouse_input_file".
460    pub fn new_mouse(input_id: uapi::input_id, node: &fuchsia_inspect::Node) -> Self {
461        let device_name = get_device_name("starnix_mouse", &input_id);
462        Self {
463            driver_version: Self::DRIVER_VERSION,
464            input_id,
465            supported_event_types: BitSet::list([uapi::EV_REL]),
466            supported_keys: BitSet::new(), // None supported, scroll only
467            supported_position_attributes: BitSet::new(), // None supported, scroll only
468            supported_motion_attributes: mouse_wheel_attributes(),
469            supported_switches: BitSet::new(), // None supported
470            supported_leds: BitSet::new(),     // None supported
471            supported_haptics: BitSet::new(),  // None supported
472            supported_misc_features: BitSet::new(), // None supported
473            properties: BitSet::new(),         // None supported, scroll only
474            mt_slot_axis_info: uapi::input_absinfo::default(),
475            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
476            x_axis_info: uapi::input_absinfo::default(),
477            y_axis_info: uapi::input_absinfo::default(),
478            events: SegQueue::new(),
479            waiters: WaitQueue::default(),
480            inspect_status: Some(InputFileStatus::new(node)),
481            device_name,
482        }
483    }
484
485    pub fn init_inspect_status(self: &Arc<Self>) {
486        if let Some(inspect) = &self.inspect_status {
487            *inspect.input_file.lock() = Arc::downgrade(self);
488        }
489    }
490
491    pub fn add_events(&self, events: Vec<uapi::input_event>) {
492        if events.is_empty() {
493            return;
494        }
495        if let Some(inspect) = &self.inspect_status {
496            inspect.count_fd_notify_calls();
497        }
498        for event in events {
499            self.events.push(LinuxEventWithTraceId::new(event));
500        }
501        self.waiters.notify_fd_events(FdEvents::POLLIN);
502    }
503
504    pub fn read_events(&self, limit: usize) -> Vec<LinuxEventWithTraceId> {
505        if let Some(inspect) = &self.inspect_status {
506            inspect.count_fd_read_calls();
507        }
508        let mut events = vec![];
509        for _ in 0..limit {
510            if let Some(event) = self.events.pop() {
511                events.push(event);
512            } else {
513                break;
514            }
515        }
516        // We do not notify if the buffer was not enough to read all events.
517        // `query_events` will still return `FdEvents::POLLIN` if there are remaining events,
518        // so the caller can continue reading or poll again.
519        events
520    }
521}
522
523// The bit-mask that removes the variable parts of the EVIOCGNAME ioctl
524// request.
525const EVIOCGNAME_MASK: u32 = 0b11_00_0000_0000_0000_1111_1111_1111_1111;
526
527impl FileOps for InputFile {
528    fileops_impl_nonseekable!();
529    fileops_impl_noop_sync!();
530
531    fn open(
532        &self,
533        _locked: &mut Locked<FileOpsCore>,
534        file: &FileObject,
535        _current_task: &CurrentTask,
536    ) -> Result<(), Errno> {
537        if let Some(inspect) = &self.inspect_status {
538            inspect.set_open_timestamp(zx::MonotonicInstant::get().into_nanos());
539            if (file.flags() & OpenFlags::NONBLOCK) != OpenFlags::NONBLOCK {
540                inspect.set_opened_without_nonblock();
541            }
542        }
543        Ok(())
544    }
545
546    fn close(
547        self: Box<Self>,
548        _locked: &mut Locked<FileOpsCore>,
549        _file: &starnix_core::vfs::FileObjectState,
550        _current_task: &CurrentTask,
551    ) {
552        if let Some(inspect) = &self.inspect_status {
553            inspect.set_closed();
554            inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
555        }
556    }
557
558    fn ioctl(
559        &self,
560        _locked: &mut Locked<Unlocked>,
561        _file: &FileObject,
562        current_task: &CurrentTask,
563        request: u32,
564        arg: SyscallArg,
565    ) -> Result<SyscallResult, Errno> {
566        let user_addr = UserAddress::from(arg);
567        match request {
568            uapi::EVIOCGVERSION => {
569                current_task.write_object(UserRef::new(user_addr), &self.driver_version)?;
570                Ok(SUCCESS)
571            }
572            uapi::EVIOCGID => {
573                current_task.write_object(UserRef::new(user_addr), &self.input_id)?;
574                Ok(SUCCESS)
575            }
576            uapi::EVIOCGBIT_0 => {
577                current_task
578                    .write_object(UserRef::new(user_addr), &self.supported_event_types.bytes)?;
579                Ok(SUCCESS)
580            }
581            uapi::EVIOCGBIT_EV_KEY => {
582                current_task.write_object(UserRef::new(user_addr), &self.supported_keys.bytes)?;
583                Ok(SUCCESS)
584            }
585            uapi::EVIOCGBIT_EV_ABS => {
586                current_task.write_object(
587                    UserRef::new(user_addr),
588                    &self.supported_position_attributes.bytes,
589                )?;
590                Ok(SUCCESS)
591            }
592            uapi::EVIOCGBIT_EV_REL => {
593                current_task.write_object(
594                    UserRef::new(user_addr),
595                    &self.supported_motion_attributes.bytes,
596                )?;
597                Ok(SUCCESS)
598            }
599            uapi::EVIOCGBIT_EV_SW => {
600                current_task
601                    .write_object(UserRef::new(user_addr), &self.supported_switches.bytes)?;
602                Ok(SUCCESS)
603            }
604            uapi::EVIOCGBIT_EV_LED => {
605                current_task.write_object(UserRef::new(user_addr), &self.supported_leds.bytes)?;
606                Ok(SUCCESS)
607            }
608            uapi::EVIOCGBIT_EV_FF => {
609                current_task
610                    .write_object(UserRef::new(user_addr), &self.supported_haptics.bytes)?;
611                Ok(SUCCESS)
612            }
613            uapi::EVIOCGBIT_EV_MSC => {
614                current_task
615                    .write_object(UserRef::new(user_addr), &self.supported_misc_features.bytes)?;
616                Ok(SUCCESS)
617            }
618            uapi::EVIOCGPROP => {
619                current_task.write_object(UserRef::new(user_addr), &self.properties.bytes)?;
620                Ok(SUCCESS)
621            }
622            uapi::EVIOCGABS_MT_SLOT => {
623                current_task.write_object(UserRef::new(user_addr), &self.mt_slot_axis_info)?;
624                Ok(SUCCESS)
625            }
626            uapi::EVIOCGABS_MT_TRACKING_ID => {
627                current_task
628                    .write_object(UserRef::new(user_addr), &self.mt_tracking_id_axis_info)?;
629                Ok(SUCCESS)
630            }
631            uapi::EVIOCGABS_MT_POSITION_X => {
632                current_task.write_object(UserRef::new(user_addr), &self.x_axis_info)?;
633                Ok(SUCCESS)
634            }
635            uapi::EVIOCGABS_MT_POSITION_Y => {
636                current_task.write_object(UserRef::new(user_addr), &self.y_axis_info)?;
637                Ok(SUCCESS)
638            }
639
640            request_with_params => {
641                // Remove the variable part of the request with params, so
642                // we can identify it.
643                match request_with_params & EVIOCGNAME_MASK {
644                    uapi::EVIOCGNAME_0 => {
645                        // Request to report the device name.
646                        //
647                        // An EVIOCGNAME request comes with the response buffer size encoded in
648                        // bits 29..16 of the request's `u32` code.  This is in contrast to
649                        // most other ioctl request codes in this file, which are fully known
650                        // at compile time, so we need to decode it a bit differently from
651                        // other ioctl codes.
652                        //
653                        // See [here][hh] the macros that do this.
654                        //
655                        // [hh]: https://cs.opensource.google/fuchsia/fuchsia/+/main:third_party/android/platform/bionic/libc/kernel/uapi/linux/input.h;l=82;drc=0f0c18f695543b15b852f68f297744d03d642a26
656                        let device_name = &self.device_name;
657
658                        // The lowest 14 bits of the top 16 bits are the unsigned buffer
659                        // length in bytes.  While we don't use multibyte characters,
660                        // make sure that all sizes below are expressed in terms of
661                        // bytes, not characters.
662                        let buffer_bytes_count =
663                            ((request_with_params >> 16) & ((1 << 14) - 1)) as usize;
664
665                        // Zero out the entire user buffer in case the user reads too much.
666                        // Probably not needed, but I don't think it hurts.
667                        current_task.zero(user_addr, buffer_bytes_count)?;
668                        let device_name_as_bytes = device_name.as_bytes();
669
670                        // Copy all bytes from device name if the buffer is large enough.
671                        // If not, copy one less than the buffer size, to leave space
672                        // for the final NUL.
673                        let to_copy_bytes_count =
674                            std::cmp::min(device_name_as_bytes.len(), buffer_bytes_count - 1);
675                        current_task.write_memory(
676                            user_addr,
677                            &device_name_as_bytes[..to_copy_bytes_count],
678                        )?;
679                        // EVIOCGNAME ioctl returns the number of bytes written.
680                        // Do not forget the trailing NUL.
681                        Ok((to_copy_bytes_count + 1).into())
682                    }
683                    _ => {
684                        track_stub!(
685                            TODO("https://fxbug.dev/322873200"),
686                            "input ioctl",
687                            request_with_params
688                        );
689                        error!(EOPNOTSUPP)
690                    }
691                }
692            }
693        }
694    }
695
696    fn read(
697        &self,
698        _locked: &mut Locked<FileOpsCore>,
699        _file: &FileObject,
700        current_task: &CurrentTask,
701        offset: usize,
702        data: &mut dyn OutputBuffer,
703    ) -> Result<usize, Errno> {
704        fuchsia_trace::duration!("input", "InputFile::read");
705        debug_assert!(offset == 0);
706        let input_event_size = InputEventPtr::size_of_object_for(current_task);
707
708        // The limit of the buffer is determined by taking the available bytes
709        // and using integer division on the size of uapi::input_event in bytes.
710        let limit = data.available() / input_event_size;
711        let events = self.read_events(limit);
712        if events.is_empty() {
713            // Returns `EAGAIN` for file is opened with or without `O_NONBLOCK`.
714            log_info!("read() returning EAGAIN");
715            return error!(EAGAIN);
716        }
717
718        let last_event_timeval = events.last().expect("events is nonempty").event.time;
719        let last_event_time_ns = duration_from_timeval::<zx::MonotonicTimeline>(last_event_timeval)
720            .unwrap()
721            .into_nanos();
722        self.inspect_status
723            .clone()
724            .map(|status| status.count_read_events(events.len() as u64, last_event_time_ns));
725
726        for event in &events {
727            if let Some(trace_id) = event.trace_id {
728                fuchsia_trace::duration!("input", "linux_event_read");
729                fuchsia_trace::flow_end!("input", "linux_event", trace_id);
730            }
731        }
732
733        if current_task.is_arch32() {
734            let events: Result<Vec<uapi::arch32::input_event>, _> =
735                events.iter().map(|e| uapi::arch32::input_event::try_from(e.event)).collect();
736            let events = events.map_err(|_| errno!(EINVAL))?;
737            data.write_all(events.as_bytes())
738        } else {
739            let events: Vec<uapi::input_event> = events.iter().map(|e| e.event).collect();
740            data.write_all(events.as_bytes())
741        }
742    }
743
744    fn write(
745        &self,
746        _locked: &mut Locked<FileOpsCore>,
747        _file: &FileObject,
748        _current_task: &CurrentTask,
749        offset: usize,
750        _data: &mut dyn InputBuffer,
751    ) -> Result<usize, Errno> {
752        debug_assert!(offset == 0);
753        track_stub!(TODO("https://fxbug.dev/322874385"), "write() on input device");
754        error!(EOPNOTSUPP)
755    }
756
757    fn wait_async(
758        &self,
759        _locked: &mut Locked<FileOpsCore>,
760        _file: &FileObject,
761        _current_task: &CurrentTask,
762        waiter: &Waiter,
763        events: FdEvents,
764        handler: EventHandler,
765    ) -> Option<WaitCanceler> {
766        Some(self.waiters.wait_async_fd_events(waiter, events, handler))
767    }
768
769    fn query_events(
770        &self,
771        _locked: &mut Locked<FileOpsCore>,
772        _file: &FileObject,
773        _current_task: &CurrentTask,
774    ) -> Result<FdEvents, Errno> {
775        Ok(if self.events.is_empty() { FdEvents::empty() } else { FdEvents::POLLIN })
776    }
777}
778
779pub struct ArcInputFile(pub Arc<InputFile>);
780
781impl FileOps for ArcInputFile {
782    fileops_impl_nonseekable!();
783    fileops_impl_noop_sync!();
784
785    fn open(
786        &self,
787        locked: &mut Locked<FileOpsCore>,
788        file: &FileObject,
789        current_task: &CurrentTask,
790    ) -> Result<(), Errno> {
791        self.0.as_ref().open(locked, file, current_task)
792    }
793
794    fn close(
795        self: Box<Self>,
796        _locked: &mut Locked<FileOpsCore>,
797        _file: &starnix_core::vfs::FileObjectState,
798        _current_task: &CurrentTask,
799    ) {
800        let arc_file = *self;
801        if let Some(inspect) = &arc_file.0.inspect_status {
802            inspect.set_closed();
803            inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
804        }
805    }
806
807    fn ioctl(
808        &self,
809        locked: &mut Locked<Unlocked>,
810        file: &FileObject,
811        current_task: &CurrentTask,
812        request: u32,
813        arg: SyscallArg,
814    ) -> Result<SyscallResult, Errno> {
815        self.0.as_ref().ioctl(locked, file, current_task, request, arg)
816    }
817
818    fn read(
819        &self,
820        locked: &mut Locked<FileOpsCore>,
821        file: &FileObject,
822        current_task: &CurrentTask,
823        offset: usize,
824        data: &mut dyn OutputBuffer,
825    ) -> Result<usize, Errno> {
826        self.0.as_ref().read(locked, file, current_task, offset, data)
827    }
828
829    fn write(
830        &self,
831        locked: &mut Locked<FileOpsCore>,
832        file: &FileObject,
833        current_task: &CurrentTask,
834        offset: usize,
835        data: &mut dyn InputBuffer,
836    ) -> Result<usize, Errno> {
837        self.0.as_ref().write(locked, file, current_task, offset, data)
838    }
839
840    fn wait_async(
841        &self,
842        locked: &mut Locked<FileOpsCore>,
843        file: &FileObject,
844        current_task: &CurrentTask,
845        waiter: &Waiter,
846        events: FdEvents,
847        handler: EventHandler,
848    ) -> Option<WaitCanceler> {
849        self.0.as_ref().wait_async(locked, file, current_task, waiter, events, handler)
850    }
851
852    fn query_events(
853        &self,
854        locked: &mut Locked<FileOpsCore>,
855        file: &FileObject,
856        current_task: &CurrentTask,
857    ) -> Result<FdEvents, Errno> {
858        self.0.as_ref().query_events(locked, file, current_task)
859    }
860}
861
862pub struct BitSet<const NUM_BYTES: usize> {
863    bytes: [u8; NUM_BYTES],
864}
865
866impl<const NUM_BYTES: usize> BitSet<{ NUM_BYTES }> {
867    pub const fn new() -> Self {
868        Self { bytes: [0; NUM_BYTES] }
869    }
870
871    pub const fn list<const N: usize>(bits: [u32; N]) -> Self {
872        let mut bitset = Self::new();
873        let mut i = 0;
874        while i < bits.len() {
875            bitset.set(bits[i]);
876            i += 1;
877        }
878        bitset
879    }
880
881    pub const fn set(&mut self, bitnum: u32) {
882        let bitnum = bitnum as usize;
883        let byte = bitnum / 8;
884        let bit = bitnum % 8;
885        self.bytes[byte] |= 1 << bit;
886    }
887}
888
889#[cfg(test)]
890mod tests {
891    use super::*;
892    use std::sync::atomic::Ordering;
893
894    #[test]
895    fn test_read_events_no_notify_when_buffer_full() {
896        let inspector = fuchsia_inspect::Inspector::default();
897        let node = inspector.root();
898        let input_file = InputFile::new_touch(
899            uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
900            100,
901            100,
902            node,
903        );
904
905        // Add some events.
906        let event1 = uapi::input_event {
907            type_: uapi::EV_KEY as u16,
908            code: 1,
909            value: 1,
910            ..Default::default()
911        };
912        let event2 = uapi::input_event {
913            type_: uapi::EV_KEY as u16,
914            code: 2,
915            value: 1,
916            ..Default::default()
917        };
918        input_file.add_events(vec![event1, event2]);
919
920        // Verify that adding events triggered a notification.
921        let notify_count =
922            input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
923        assert_eq!(notify_count, 1);
924
925        // Read with a limit of 1.
926        let events = input_file.read_events(1);
927        assert_eq!(events.len(), 1);
928
929        // Verify that no additional notification was sent despite more events remaining.
930        let notify_count =
931            input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
932        assert_eq!(notify_count, 1);
933    }
934}