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 fuchsia_inspect::{NumericProperty, Property};
6use starnix_core::fileops_impl_nonseekable;
7use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt};
8use starnix_core::task::{CurrentTask, EventHandler, WaitCanceler, WaitQueue, Waiter};
9use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
10use starnix_core::vfs::{CloseFreeSafe, FileObject, FileOps, fileops_impl_noop_sync};
11use starnix_logging::{log_info, trace_duration, trace_flow_begin, trace_flow_end, track_stub};
12use starnix_sync::{FileOpsCore, Locked, Mutex, Unlocked};
13use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
14use starnix_types::time::duration_from_timeval;
15use starnix_uapi::errors::Errno;
16use starnix_uapi::user_address::{ArchSpecific, MultiArchUserRef, UserAddress, UserRef};
17use starnix_uapi::vfs::FdEvents;
18use starnix_uapi::{
19    ABS_CNT, ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_SLOT, ABS_MT_TRACKING_ID, BTN_MISC,
20    BTN_TOUCH, EV_CNT, FF_CNT, INPUT_PROP_CNT, INPUT_PROP_DIRECT, KEY_CNT, KEY_POWER, KEY_SLEEP,
21    KEY_VOLUMEDOWN, LED_CNT, MSC_CNT, REL_CNT, REL_WHEEL, SW_CNT, errno, error, uapi,
22};
23use std::collections::VecDeque;
24use std::sync::Arc;
25use zerocopy::IntoBytes as _; // for `as_bytes()`
26
27uapi::check_arch_independent_layout! {
28    input_id {}
29    input_absinfo {}
30}
31
32type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
33
34pub struct InputFileStatus {
35    /// The number of FIDL events received by this file from Fuchsia input system.
36    ///
37    /// We expect:
38    /// fidl_events_received_count = fidl_events_ignored_count +
39    ///                              fidl_events_unexpected_count +
40    ///                              fidl_events_converted_count
41    /// otherwise starnix ignored events unexpectedly.
42    ///
43    /// fidl_events_unexpected_count should be 0, if not it hints issues from upstream of ui stack.
44    pub fidl_events_received_count: fuchsia_inspect::UintProperty,
45
46    /// The number of FIDL events ignored to this module’s representation of TouchEvent.
47    pub fidl_events_ignored_count: fuchsia_inspect::UintProperty,
48
49    /// The unexpected number of FIDL events reached to this module should be filtered out
50    /// earlier in the UI stack.
51    /// It maybe unexpected format or unexpected order.
52    pub fidl_events_unexpected_count: fuchsia_inspect::UintProperty,
53
54    /// The number of FIDL events converted to this module’s representation of TouchEvent.
55    pub fidl_events_converted_count: fuchsia_inspect::UintProperty,
56
57    /// The number of uapi::input_events generated from TouchEvents.
58    pub uapi_events_generated_count: fuchsia_inspect::UintProperty,
59
60    /// The event time of the last generated uapi::input_event.
61    pub last_generated_uapi_event_timestamp_ns: fuchsia_inspect::IntProperty,
62
63    /// The number of uapi::input_events read from this input file by external process.
64    pub uapi_events_read_count: fuchsia_inspect::UintProperty,
65
66    /// The event time of the last uapi::input_event read by external process.
67    pub last_read_uapi_event_timestamp_ns: fuchsia_inspect::IntProperty,
68}
69
70impl InputFileStatus {
71    fn new(node: &fuchsia_inspect::Node) -> Self {
72        let fidl_events_received_count = node.create_uint("fidl_events_received_count", 0);
73        let fidl_events_ignored_count = node.create_uint("fidl_events_ignored_count", 0);
74        let fidl_events_unexpected_count = node.create_uint("fidl_events_unexpected_count", 0);
75        let fidl_events_converted_count = node.create_uint("fidl_events_converted_count", 0);
76        let uapi_events_generated_count = node.create_uint("uapi_events_generated_count", 0);
77        let last_generated_uapi_event_timestamp_ns =
78            node.create_int("last_generated_uapi_event_timestamp_ns", 0);
79        let uapi_events_read_count = node.create_uint("uapi_events_read_count", 0);
80        let last_read_uapi_event_timestamp_ns =
81            node.create_int("last_read_uapi_event_timestamp_ns", 0);
82        Self {
83            fidl_events_received_count,
84            fidl_events_ignored_count,
85            fidl_events_unexpected_count,
86            fidl_events_converted_count,
87            uapi_events_generated_count,
88            last_generated_uapi_event_timestamp_ns,
89            uapi_events_read_count,
90            last_read_uapi_event_timestamp_ns,
91        }
92    }
93
94    pub fn count_received_events(&self, count: u64) {
95        self.fidl_events_received_count.add(count);
96    }
97
98    pub fn count_ignored_events(&self, count: u64) {
99        self.fidl_events_ignored_count.add(count);
100    }
101
102    pub fn count_unexpected_events(&self, count: u64) {
103        self.fidl_events_unexpected_count.add(count);
104    }
105
106    pub fn count_converted_events(&self, count: u64) {
107        self.fidl_events_converted_count.add(count);
108    }
109
110    pub fn count_generated_events(&self, count: u64, event_time_ns: i64) {
111        self.uapi_events_generated_count.add(count);
112        self.last_generated_uapi_event_timestamp_ns.set(event_time_ns);
113    }
114
115    pub fn count_read_events(&self, count: u64, event_time_ns: i64) {
116        self.uapi_events_read_count.add(count);
117        self.last_read_uapi_event_timestamp_ns.set(event_time_ns);
118    }
119}
120
121pub struct InputFile {
122    driver_version: u32,
123    input_id: uapi::input_id,
124    supported_event_types: BitSet<{ min_bytes(EV_CNT) }>,
125    supported_keys: BitSet<{ min_bytes(KEY_CNT) }>,
126    supported_position_attributes: BitSet<{ min_bytes(ABS_CNT) }>, // ABSolute position
127    supported_motion_attributes: BitSet<{ min_bytes(REL_CNT) }>,   // RELative motion
128    supported_switches: BitSet<{ min_bytes(SW_CNT) }>,
129    supported_leds: BitSet<{ min_bytes(LED_CNT) }>,
130    supported_haptics: BitSet<{ min_bytes(FF_CNT) }>, // 'F'orce 'F'eedback
131    supported_misc_features: BitSet<{ min_bytes(MSC_CNT) }>,
132    properties: BitSet<{ min_bytes(INPUT_PROP_CNT) }>,
133    mt_slot_axis_info: uapi::input_absinfo,
134    mt_tracking_id_axis_info: uapi::input_absinfo,
135    x_axis_info: uapi::input_absinfo,
136    y_axis_info: uapi::input_absinfo,
137    pub inner: Mutex<InputFileMutableState>,
138    // InputFile will be initialized with an InputFileStatus that holds Inspect data
139    // `None` for Uinput InputFiles
140    pub inspect_status: Option<Arc<InputFileStatus>>,
141
142    // A descriptive device name. Should contain only alphanumerics and `_`.
143    device_name: String,
144}
145
146pub struct LinuxEventWithTraceId {
147    pub event: uapi::input_event,
148    pub trace_id: Option<fuchsia_trace::Id>,
149}
150
151impl LinuxEventWithTraceId {
152    pub fn new(event: uapi::input_event) -> Self {
153        match event.type_ as u32 {
154            uapi::EV_SYN => {
155                let trace_id = fuchsia_trace::Id::random();
156                trace_duration!("input", "linux_event_create");
157                trace_flow_begin!("input", "linux_event", trace_id);
158                LinuxEventWithTraceId { event: event, trace_id: Some(trace_id) }
159            }
160            // EV_SYN marks the end of a complete input event. Other event types are its properties,
161            // so they don't initiate a trace.
162            _ => LinuxEventWithTraceId { event: event, trace_id: None },
163        }
164    }
165}
166
167// Mutable state of `InputFile`
168pub struct InputFileMutableState {
169    pub events: VecDeque<LinuxEventWithTraceId>,
170    pub waiters: WaitQueue,
171}
172
173/// Returns the minimum number of bytes required to store `n_bits` bits.
174const fn min_bytes(n_bits: u32) -> usize {
175    ((n_bits as usize) + 7) / 8
176}
177
178/// Returns appropriate `INPUT_PROP`-erties for a keyboard device.
179fn keyboard_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
180    let mut attrs = BitSet::new();
181    attrs.set(INPUT_PROP_DIRECT);
182    attrs
183}
184
185/// Returns appropriate `KEY`-board related flags for a touchscreen device.
186fn touch_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
187    let mut attrs = BitSet::new();
188    attrs.set(BTN_TOUCH);
189    attrs.set(BTN_MISC); // Include BTN_MISC as a catchall key event.
190    attrs.set(KEY_SLEEP);
191    attrs
192}
193
194/// Returns appropriate `ABS`-olute position related flags for a touchscreen device.
195fn touch_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
196    let mut attrs = BitSet::new();
197    attrs.set(ABS_MT_SLOT);
198    attrs.set(ABS_MT_TRACKING_ID);
199    attrs.set(ABS_MT_POSITION_X);
200    attrs.set(ABS_MT_POSITION_Y);
201    attrs
202}
203
204/// Returns appropriate `INPUT_PROP`-erties for a touchscreen device.
205fn touch_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
206    let mut attrs = BitSet::new();
207    attrs.set(INPUT_PROP_DIRECT);
208    attrs
209}
210
211/// Returns appropriate `KEY`-board related flags for a keyboard device.
212fn keyboard_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
213    let mut attrs = BitSet::new();
214    attrs.set(BTN_MISC);
215    attrs.set(KEY_POWER);
216    attrs.set(KEY_VOLUMEDOWN);
217    attrs
218}
219
220/// Returns appropriate `ABS`-olute position related flags for a keyboard device.
221fn keyboard_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
222    BitSet::new()
223}
224
225fn mouse_wheel_attributes() -> BitSet<{ min_bytes(REL_CNT) }> {
226    let mut attrs = BitSet::new();
227    attrs.set(REL_WHEEL);
228    attrs
229}
230
231/// Makes a device name string from a name and device ID details.
232///
233/// For practical reasons the device name should contain alphanumerics and `_`.
234fn get_device_name(name: &str, input_id: &uapi::input_id) -> String {
235    format!("{}_{:04x}_{:04x}_v{}", name, input_id.vendor, input_id.product, input_id.version)
236}
237
238impl InputFile {
239    // Per https://www.linuxjournal.com/article/6429, the driver version is 32-bits wide,
240    // and interpreted as:
241    // * [31-16]: version
242    // * [15-08]: minor
243    // * [07-00]: patch level
244    const DRIVER_VERSION: u32 = 0;
245
246    /// Creates an `InputFile` instance suitable for emulating a touchscreen.
247    ///
248    /// # Parameters
249    /// - `input_id`: device's bustype, vendor id, product id, and version.
250    /// - `width`: width of screen.
251    /// - `height`: height of screen.
252    /// - `inspect_status`: The inspect status for the parent device of "touch_input_file".
253    pub fn new_touch(
254        input_id: uapi::input_id,
255        width: i32,
256        height: i32,
257        node: Option<&fuchsia_inspect::Node>,
258    ) -> Self {
259        let device_name = get_device_name("starnix_touch", &input_id);
260        // Fuchsia scales the position reported by the touch sensor to fit view coordinates.
261        // Hence, the range of touch positions is exactly the same as the range of view
262        // coordinates.
263        Self {
264            driver_version: Self::DRIVER_VERSION,
265            input_id,
266            supported_event_types: BitSet::list([uapi::EV_ABS]),
267            supported_keys: touch_key_attributes(),
268            supported_position_attributes: touch_position_attributes(),
269            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
270            supported_switches: BitSet::new(),          // None supported
271            supported_leds: BitSet::new(),              // None supported
272            supported_haptics: BitSet::new(),           // None supported
273            supported_misc_features: BitSet::new(),     // None supported
274            properties: touch_properties(),
275            mt_slot_axis_info: uapi::input_absinfo {
276                minimum: 0,
277                maximum: 10,
278                ..uapi::input_absinfo::default()
279            },
280            mt_tracking_id_axis_info: uapi::input_absinfo {
281                minimum: 0,
282                maximum: i32::MAX,
283                ..uapi::input_absinfo::default()
284            },
285            x_axis_info: uapi::input_absinfo {
286                minimum: 0,
287                maximum: i32::from(width),
288                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
289                // X position.
290                ..uapi::input_absinfo::default()
291            },
292            y_axis_info: uapi::input_absinfo {
293                minimum: 0,
294                maximum: i32::from(height),
295                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
296                // Y position.
297                ..uapi::input_absinfo::default()
298            },
299            inner: Mutex::new(InputFileMutableState {
300                events: VecDeque::new(),
301                waiters: WaitQueue::default(),
302            }),
303            inspect_status: node.map(|n| Arc::new(InputFileStatus::new(n))),
304            device_name,
305        }
306    }
307
308    /// Creates an `InputFile` instance suitable for emulating a keyboard.
309    ///
310    /// # Parameters
311    /// - `input_id`: device's bustype, vendor id, product id, and version.
312    /// - `inspect_status`: The inspect status for the parent device of "keyboard_input_file".
313    pub fn new_keyboard(input_id: uapi::input_id, node: Option<&fuchsia_inspect::Node>) -> Self {
314        let device_name = get_device_name("starnix_buttons", &input_id);
315        Self {
316            driver_version: Self::DRIVER_VERSION,
317            input_id,
318            supported_event_types: BitSet::list([uapi::EV_KEY]),
319            supported_keys: keyboard_key_attributes(),
320            supported_position_attributes: keyboard_position_attributes(),
321            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
322            supported_switches: BitSet::new(),          // None supported
323            supported_leds: BitSet::new(),              // None supported
324            supported_haptics: BitSet::new(),           // None supported
325            supported_misc_features: BitSet::new(),     // None supported
326            properties: keyboard_properties(),
327            mt_slot_axis_info: uapi::input_absinfo::default(),
328            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
329            x_axis_info: uapi::input_absinfo::default(),
330            y_axis_info: uapi::input_absinfo::default(),
331            inner: Mutex::new(InputFileMutableState {
332                events: VecDeque::new(),
333                waiters: WaitQueue::default(),
334            }),
335            inspect_status: node.map(|n| Arc::new(InputFileStatus::new(n))),
336            device_name,
337        }
338    }
339
340    /// Creates an `InputFile` instance suitable for emulating a mouse wheel.
341    ///
342    /// # Parameters
343    /// - `input_id`: device's bustype, vendor id, product id, and version.
344    /// - `inspect_status`: The inspect status for the parent device of "mouse_input_file".
345    pub fn new_mouse(input_id: uapi::input_id, node: Option<&fuchsia_inspect::Node>) -> Self {
346        let device_name = get_device_name("starnix_mouse", &input_id);
347        Self {
348            driver_version: Self::DRIVER_VERSION,
349            input_id,
350            supported_event_types: BitSet::list([uapi::EV_REL]),
351            supported_keys: BitSet::new(), // None supported, scroll only
352            supported_position_attributes: BitSet::new(), // None supported, scroll only
353            supported_motion_attributes: mouse_wheel_attributes(),
354            supported_switches: BitSet::new(), // None supported
355            supported_leds: BitSet::new(),     // None supported
356            supported_haptics: BitSet::new(),  // None supported
357            supported_misc_features: BitSet::new(), // None supported
358            properties: BitSet::new(),         // None supported, scroll only
359            mt_slot_axis_info: uapi::input_absinfo::default(),
360            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
361            x_axis_info: uapi::input_absinfo::default(),
362            y_axis_info: uapi::input_absinfo::default(),
363            inner: Mutex::new(InputFileMutableState {
364                events: VecDeque::new(),
365                waiters: WaitQueue::default(),
366            }),
367            inspect_status: node.map(|n| Arc::new(InputFileStatus::new(n))),
368            device_name,
369        }
370    }
371}
372
373// The bit-mask that removes the variable parts of the EVIOCGNAME ioctl
374// request.
375const EVIOCGNAME_MASK: u32 = 0b11_00_0000_0000_0000_1111_1111_1111_1111;
376
377/// `InputFile` doesn't implement the `close` method.
378impl CloseFreeSafe for InputFile {}
379impl FileOps for InputFile {
380    fileops_impl_nonseekable!();
381    fileops_impl_noop_sync!();
382
383    fn ioctl(
384        &self,
385        _locked: &mut Locked<Unlocked>,
386        _file: &FileObject,
387        current_task: &CurrentTask,
388        request: u32,
389        arg: SyscallArg,
390    ) -> Result<SyscallResult, Errno> {
391        let user_addr = UserAddress::from(arg);
392        match request {
393            uapi::EVIOCGVERSION => {
394                current_task.write_object(UserRef::new(user_addr), &self.driver_version)?;
395                Ok(SUCCESS)
396            }
397            uapi::EVIOCGID => {
398                current_task.write_object(UserRef::new(user_addr), &self.input_id)?;
399                Ok(SUCCESS)
400            }
401            uapi::EVIOCGBIT_0 => {
402                current_task
403                    .write_object(UserRef::new(user_addr), &self.supported_event_types.bytes)?;
404                Ok(SUCCESS)
405            }
406            uapi::EVIOCGBIT_EV_KEY => {
407                current_task.write_object(UserRef::new(user_addr), &self.supported_keys.bytes)?;
408                Ok(SUCCESS)
409            }
410            uapi::EVIOCGBIT_EV_ABS => {
411                current_task.write_object(
412                    UserRef::new(user_addr),
413                    &self.supported_position_attributes.bytes,
414                )?;
415                Ok(SUCCESS)
416            }
417            uapi::EVIOCGBIT_EV_REL => {
418                current_task.write_object(
419                    UserRef::new(user_addr),
420                    &self.supported_motion_attributes.bytes,
421                )?;
422                Ok(SUCCESS)
423            }
424            uapi::EVIOCGBIT_EV_SW => {
425                current_task
426                    .write_object(UserRef::new(user_addr), &self.supported_switches.bytes)?;
427                Ok(SUCCESS)
428            }
429            uapi::EVIOCGBIT_EV_LED => {
430                current_task.write_object(UserRef::new(user_addr), &self.supported_leds.bytes)?;
431                Ok(SUCCESS)
432            }
433            uapi::EVIOCGBIT_EV_FF => {
434                current_task
435                    .write_object(UserRef::new(user_addr), &self.supported_haptics.bytes)?;
436                Ok(SUCCESS)
437            }
438            uapi::EVIOCGBIT_EV_MSC => {
439                current_task
440                    .write_object(UserRef::new(user_addr), &self.supported_misc_features.bytes)?;
441                Ok(SUCCESS)
442            }
443            uapi::EVIOCGPROP => {
444                current_task.write_object(UserRef::new(user_addr), &self.properties.bytes)?;
445                Ok(SUCCESS)
446            }
447            uapi::EVIOCGABS_MT_SLOT => {
448                current_task.write_object(UserRef::new(user_addr), &self.mt_slot_axis_info)?;
449                Ok(SUCCESS)
450            }
451            uapi::EVIOCGABS_MT_TRACKING_ID => {
452                current_task
453                    .write_object(UserRef::new(user_addr), &self.mt_tracking_id_axis_info)?;
454                Ok(SUCCESS)
455            }
456            uapi::EVIOCGABS_MT_POSITION_X => {
457                current_task.write_object(UserRef::new(user_addr), &self.x_axis_info)?;
458                Ok(SUCCESS)
459            }
460            uapi::EVIOCGABS_MT_POSITION_Y => {
461                current_task.write_object(UserRef::new(user_addr), &self.y_axis_info)?;
462                Ok(SUCCESS)
463            }
464
465            request_with_params => {
466                // Remove the variable part of the request with params, so
467                // we can identify it.
468                match request_with_params & EVIOCGNAME_MASK {
469                    uapi::EVIOCGNAME_0 => {
470                        // Request to report the device name.
471                        //
472                        // An EVIOCGNAME request comes with the response buffer size encoded in
473                        // bits 29..16 of the request's `u32` code.  This is in contrast to
474                        // most other ioctl request codes in this file, which are fully known
475                        // at compile time, so we need to decode it a bit differently from
476                        // other ioctl codes.
477                        //
478                        // See [here][hh] the macros that do this.
479                        //
480                        // [hh]: https://cs.opensource.google/fuchsia/fuchsia/+/main:third_party/android/platform/bionic/libc/kernel/uapi/linux/input.h;l=82;drc=0f0c18f695543b15b852f68f297744d03d642a26
481                        let device_name = &self.device_name;
482
483                        // The lowest 14 bits of the top 16 bits are the unsigned buffer
484                        // length in bytes.  While we don't use multibyte characters,
485                        // make sure that all sizes below are expressed in terms of
486                        // bytes, not characters.
487                        let buffer_bytes_count =
488                            ((request_with_params >> 16) & ((1 << 14) - 1)) as usize;
489
490                        // Zero out the entire user buffer in case the user reads too much.
491                        // Probably not needed, but I don't think it hurts.
492                        current_task.zero(user_addr, buffer_bytes_count)?;
493                        let device_name_as_bytes = device_name.as_bytes();
494
495                        // Copy all bytes from device name if the buffer is large enough.
496                        // If not, copy one less than the buffer size, to leave space
497                        // for the final NUL.
498                        let to_copy_bytes_count =
499                            std::cmp::min(device_name_as_bytes.len(), buffer_bytes_count - 1);
500                        current_task.write_memory(
501                            user_addr,
502                            &device_name_as_bytes[..to_copy_bytes_count],
503                        )?;
504                        // EVIOCGNAME ioctl returns the number of bytes written.
505                        // Do not forget the trailing NUL.
506                        Ok((to_copy_bytes_count + 1).into())
507                    }
508                    _ => {
509                        track_stub!(
510                            TODO("https://fxbug.dev/322873200"),
511                            "input ioctl",
512                            request_with_params
513                        );
514                        error!(EOPNOTSUPP)
515                    }
516                }
517            }
518        }
519    }
520
521    fn read(
522        &self,
523        _locked: &mut Locked<FileOpsCore>,
524        _file: &FileObject,
525        current_task: &CurrentTask,
526        offset: usize,
527        data: &mut dyn OutputBuffer,
528    ) -> Result<usize, Errno> {
529        trace_duration!("input", "InputFile::read");
530        debug_assert!(offset == 0);
531        let mut inner = self.inner.lock();
532        let num_events = inner.events.len();
533        if num_events == 0 {
534            // TODO(https://fxbug.dev/42075445): `EAGAIN` is only permitted if the file is opened
535            // with `O_NONBLOCK`. Figure out what to do if the file is opened without that flag.
536            log_info!("read() returning EAGAIN");
537            return error!(EAGAIN);
538        }
539
540        let input_event_size = InputEventPtr::size_of_object_for(current_task);
541
542        // The limit of the buffer is determined by taking the available bytes
543        // and using integer division on the size of uapi::input_event in bytes.
544        // This is how many events we can write at a time, up to the amount of
545        // events queued to be written.
546        let limit = std::cmp::min(data.available() / input_event_size, num_events);
547        if num_events > limit {
548            log_info!(
549                "There was only space in the given buffer to read {} of the {} queued events. Sending a notification to prompt another read.",
550                limit,
551                num_events
552            );
553            inner.waiters.notify_fd_events(FdEvents::POLLIN);
554        }
555        let events: Vec<LinuxEventWithTraceId> = inner.events.drain(..limit).collect::<Vec<_>>();
556        let last_event_timeval = events.last().expect("events is nonempty").event.time;
557        let last_event_time_ns = duration_from_timeval::<zx::MonotonicTimeline>(last_event_timeval)
558            .unwrap()
559            .into_nanos();
560        self.inspect_status
561            .clone()
562            .map(|status| status.count_read_events(events.len() as u64, last_event_time_ns));
563
564        for event in &events {
565            if let Some(trace_id) = event.trace_id {
566                trace_duration!("input", "linux_event_read");
567                trace_flow_end!("input", "linux_event", trace_id);
568            }
569        }
570
571        if current_task.is_arch32() {
572            let events: Result<Vec<uapi::arch32::input_event>, _> =
573                events.iter().map(|e| uapi::arch32::input_event::try_from(e.event)).collect();
574            let events = events.map_err(|_| errno!(EINVAL))?;
575            data.write_all(events.as_bytes())
576        } else {
577            let events: Vec<uapi::input_event> = events.iter().map(|e| e.event).collect();
578            data.write_all(events.as_bytes())
579        }
580    }
581
582    fn write(
583        &self,
584        _locked: &mut Locked<FileOpsCore>,
585        _file: &FileObject,
586        _current_task: &CurrentTask,
587        offset: usize,
588        _data: &mut dyn InputBuffer,
589    ) -> Result<usize, Errno> {
590        debug_assert!(offset == 0);
591        track_stub!(TODO("https://fxbug.dev/322874385"), "write() on input device");
592        error!(EOPNOTSUPP)
593    }
594
595    fn wait_async(
596        &self,
597        _locked: &mut Locked<FileOpsCore>,
598        _file: &FileObject,
599        _current_task: &CurrentTask,
600        waiter: &Waiter,
601        events: FdEvents,
602        handler: EventHandler,
603    ) -> Option<WaitCanceler> {
604        Some(self.inner.lock().waiters.wait_async_fd_events(waiter, events, handler))
605    }
606
607    fn query_events(
608        &self,
609        _locked: &mut Locked<FileOpsCore>,
610        _file: &FileObject,
611        _current_task: &CurrentTask,
612    ) -> Result<FdEvents, Errno> {
613        Ok(if self.inner.lock().events.is_empty() { FdEvents::empty() } else { FdEvents::POLLIN })
614    }
615}
616
617pub struct BitSet<const NUM_BYTES: usize> {
618    bytes: [u8; NUM_BYTES],
619}
620
621impl<const NUM_BYTES: usize> BitSet<{ NUM_BYTES }> {
622    pub const fn new() -> Self {
623        Self { bytes: [0; NUM_BYTES] }
624    }
625
626    pub const fn list<const N: usize>(bits: [u32; N]) -> Self {
627        let mut bitset = Self::new();
628        let mut i = 0;
629        while i < bits.len() {
630            bitset.set(bits[i]);
631            i += 1;
632        }
633        bitset
634    }
635
636    pub const fn set(&mut self, bitnum: u32) {
637        let bitnum = bitnum as usize;
638        let byte = bitnum / 8;
639        let bit = bitnum % 8;
640        self.bytes[byte] |= 1 << bit;
641    }
642}