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