Skip to main content

starnix_modules_input/
uinput.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 crate::uinput::vfs::{CloseFreeSafe, NamespaceNode};
6use crate::{InputEventsRelayHandle, InputFile, OpenedFiles};
7use bit_vec::BitVec;
8use fidl_fuchsia_ui_test_input::{
9    self as futinput, CoordinateUnit, DisplayDimensions, KeyboardSimulateKeyEventRequest,
10    RegistryRegisterKeyboardAndGetDeviceInfoRequest,
11    RegistryRegisterTouchScreenAndGetDeviceInfoRequest,
12};
13use starnix_core::device::kobject::{Device, DeviceMetadata};
14use starnix_core::device::{DeviceMode, DeviceOps};
15use starnix_core::fileops_impl_seekless;
16use starnix_core::mm::MemoryAccessorExt;
17use starnix_core::task::CurrentTask;
18use starnix_core::vfs::{
19    self, FileObject, FileOps, FsString, default_ioctl, fileops_impl_noop_sync,
20};
21use starnix_logging::log_warn;
22use starnix_modules_input_event_conversion::key_linux_to_fuchsia::LinuxKeyboardEventParser;
23use starnix_modules_input_event_conversion::touch_linux_to_fuchsia::LinuxTouchEventParser;
24use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex, Unlocked};
25use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
26use starnix_uapi::device_type::INPUT_MAJOR;
27use starnix_uapi::errors::Errno;
28use starnix_uapi::open_flags::OpenFlags;
29use starnix_uapi::user_address::{MultiArchUserRef, UserRef};
30use starnix_uapi::{device_type, errno, error, uapi};
31use std::sync::Arc;
32use std::sync::atomic::{AtomicI32, Ordering};
33
34// Return the current uinput API version 5, it also told caller this uinput
35// supports UI_DEV_SETUP.
36const UINPUT_VERSION: u32 = 5;
37
38type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
39
40#[derive(Clone)]
41enum DeviceType {
42    Keyboard,
43    Touchscreen(i32, i32),
44}
45
46pub fn register_uinput_device(
47    locked: &mut Locked<Unlocked>,
48    system_task: &CurrentTask,
49    input_event_relay_handle: Arc<InputEventsRelayHandle>,
50) -> Result<(), Errno> {
51    let kernel = system_task.kernel();
52    let registry = &kernel.device_registry;
53    let misc_class = registry.objects.misc_class();
54    let device = UinputDevice::new(input_event_relay_handle);
55    registry.register_device(
56        locked,
57        system_task,
58        "uinput".into(),
59        DeviceMetadata::new("uinput".into(), device_type::DeviceType::UINPUT, DeviceMode::Char),
60        misc_class,
61        device,
62    )?;
63    Ok(())
64}
65
66fn add_and_register_input_device<L>(
67    locked: &mut Locked<L>,
68    system_task: &CurrentTask,
69    dev_ops: impl DeviceOps,
70    device_id: u32,
71) -> Result<Device, Errno>
72where
73    L: LockEqualOrBefore<FileOpsCore>,
74{
75    let kernel = system_task.kernel();
76    let registry = &kernel.device_registry;
77
78    let input_class = registry.objects.input_class();
79
80    registry.register_device(
81        locked,
82        system_task,
83        FsString::from(format!("event{}", device_id)).as_ref(),
84        DeviceMetadata::new(
85            format!("input/event{}", device_id).into(),
86            starnix_uapi::device_type::DeviceType::new(INPUT_MAJOR, device_id),
87            DeviceMode::Char,
88        ),
89        input_class,
90        dev_ops,
91    )
92}
93
94#[derive(Clone)]
95struct UinputDevice {
96    input_event_relay: Arc<InputEventsRelayHandle>,
97}
98
99impl UinputDevice {
100    pub fn new(input_event_relay: Arc<InputEventsRelayHandle>) -> Self {
101        Self { input_event_relay }
102    }
103}
104
105impl DeviceOps for UinputDevice {
106    fn open(
107        &self,
108        _locked: &mut Locked<FileOpsCore>,
109        _current_task: &CurrentTask,
110        _id: device_type::DeviceType,
111        _node: &NamespaceNode,
112        _flags: OpenFlags,
113    ) -> Result<Box<dyn FileOps>, Errno> {
114        Ok(Box::new(UinputDeviceFile::new(self.input_event_relay.clone())))
115    }
116}
117
118enum CreatedDevice {
119    None,
120    Keyboard(futinput::KeyboardSynchronousProxy, LinuxKeyboardEventParser),
121    // LinuxTouchEventParser need to boxed to avoid warning: large-enum-variant.
122    Touchscreen(futinput::TouchScreenSynchronousProxy, Box<LinuxTouchEventParser>),
123}
124
125struct Range {
126    min: i32,
127    max: i32,
128}
129struct UinputDeviceMutableState {
130    enabled_evbits: BitVec,
131    input_id: Option<uapi::input_id>,
132    created_device: CreatedDevice,
133    k_device: Option<Device>,
134    device_id: Option<u32>,
135    x_range: Option<Range>,
136    y_range: Option<Range>,
137}
138
139impl UinputDeviceMutableState {
140    fn get_id_and_device_type(&self) -> Option<(uapi::input_id, DeviceType)> {
141        let input_id = match self.input_id {
142            Some(input_id) => input_id,
143            None => return None,
144        };
145        // Currently only support Keyboard and Touchscreen, if evbits contains
146        // EV_ABS, consider it is Touchscreen. This need to be revisit when we
147        // want to support more device types.
148        let device_type = match self.enabled_evbits.clone().get(uapi::EV_ABS as usize) {
149            Some(true) => {
150                // TODO(b/304595635): Check if screen size is required.
151                let mut touchscreen_width = 1000;
152                let mut touchscreen_height = 1000;
153
154                if self.x_range.is_some() && self.y_range.is_some() {
155                    let x_range = self.x_range.as_ref().unwrap();
156                    let y_range = self.y_range.as_ref().unwrap();
157                    touchscreen_width = x_range.max - x_range.min;
158                    touchscreen_height = y_range.max - y_range.min;
159                }
160                DeviceType::Touchscreen(touchscreen_width, touchscreen_height)
161            }
162            Some(false) | None => DeviceType::Keyboard,
163        };
164
165        Some((input_id, device_type))
166    }
167}
168
169struct UinputDeviceFile {
170    input_event_relay: Arc<InputEventsRelayHandle>,
171    inner: Mutex<UinputDeviceMutableState>,
172}
173
174impl UinputDeviceFile {
175    pub fn new(input_event_relay: Arc<InputEventsRelayHandle>) -> Self {
176        Self {
177            input_event_relay,
178            inner: Mutex::new(UinputDeviceMutableState {
179                enabled_evbits: BitVec::from_elem(uapi::EV_CNT as usize, false),
180                input_id: None,
181                created_device: CreatedDevice::None,
182                k_device: None,
183                device_id: None,
184                x_range: None,
185                y_range: None,
186            }),
187        }
188    }
189
190    /// UI_SET_EVBIT caller pass a u32 as the event type "EV_*" to set this
191    /// uinput device may handle events with the given event type.
192    fn ui_set_evbit(&self, arg: SyscallArg) -> Result<SyscallResult, Errno> {
193        let evbit: u32 = arg.into();
194        match evbit {
195            uapi::EV_KEY | uapi::EV_ABS => {
196                self.inner.lock().enabled_evbits.set(evbit as usize, true);
197                Ok(SUCCESS)
198            }
199            _ => {
200                log_warn!("UI_SET_EVBIT with unsupported evbit {}", evbit);
201                error!(EPERM)
202            }
203        }
204    }
205
206    /// UI_ABS_SETUP caller pass in event codes and min and max value for
207    /// the event code.
208    fn ui_abs_setup(
209        &self,
210        current_task: &CurrentTask,
211        abs_setup: UserRef<starnix_uapi::uinput_abs_setup>,
212    ) -> Result<SyscallResult, Errno> {
213        let setup: starnix_uapi::uinput_abs_setup = current_task.read_object(abs_setup)?;
214        let code: u32 = setup.code.into();
215        match code {
216            uapi::ABS_MT_POSITION_X => {
217                self.inner.lock().x_range =
218                    Some(Range { min: setup.absinfo.minimum, max: setup.absinfo.maximum });
219            }
220            uapi::ABS_MT_POSITION_Y => {
221                self.inner.lock().y_range =
222                    Some(Range { min: setup.absinfo.minimum, max: setup.absinfo.maximum });
223            }
224            _ => {
225                log_warn!("UI_ABS_SETUP ignore event code {}", setup.code);
226            }
227        }
228        Ok(SUCCESS)
229    }
230
231    /// UI_GET_VERSION caller pass a address for u32 to `arg` to receive the
232    /// uinput version. ioctl returns SUCCESS(0) for success calls, and
233    /// EFAULT(14) for given address is null.
234    fn ui_get_version(
235        &self,
236        current_task: &CurrentTask,
237        user_version: UserRef<u32>,
238    ) -> Result<SyscallResult, Errno> {
239        let response: u32 = UINPUT_VERSION;
240        current_task.write_object(user_version, &response)?;
241        Ok(SUCCESS)
242    }
243
244    /// UI_DEV_SETUP set the name of device and input_id (bustype, vendor id,
245    /// product id, version) to the uinput device.
246    fn ui_dev_setup(
247        &self,
248        current_task: &CurrentTask,
249        user_uinput_setup: UserRef<uapi::uinput_setup>,
250    ) -> Result<SyscallResult, Errno> {
251        let uinput_setup = current_task.read_object(user_uinput_setup)?;
252        self.inner.lock().input_id = Some(uinput_setup.id);
253        Ok(SUCCESS)
254    }
255
256    fn ui_dev_create<L>(
257        &self,
258        locked: &mut Locked<L>,
259        current_task: &CurrentTask,
260    ) -> Result<SyscallResult, Errno>
261    where
262        L: LockEqualOrBefore<FileOpsCore>,
263    {
264        // Only eng and userdebug builds include the `fuchsia.ui.test.input` service.
265        let registry = match fuchsia_component::client::connect_to_protocol_sync::<
266            futinput::RegistryMarker,
267        >() {
268            Ok(proxy) => Some(proxy),
269            Err(_) => {
270                log_warn!("Could not connect to fuchsia.ui.test.input/Registry");
271                None
272            }
273        };
274        self.ui_dev_create_inner(locked, current_task, registry)
275    }
276
277    /// UI_DEV_CREATE calls create the uinput device with given information
278    /// from previous ioctl() calls.
279    fn ui_dev_create_inner<L>(
280        &self,
281        locked: &mut Locked<L>,
282        current_task: &CurrentTask,
283        // Takes `registry` arg so we can manually inject a mock registry in unit tests.
284        registry: Option<futinput::RegistrySynchronousProxy>,
285    ) -> Result<SyscallResult, Errno>
286    where
287        L: LockEqualOrBefore<FileOpsCore>,
288    {
289        match registry {
290            Some(proxy) => {
291                let mut inner = self.inner.lock();
292                let (input_id, device_type) = match inner.get_id_and_device_type() {
293                    Some((id, dev)) => (id, dev),
294                    None => return error!(EINVAL),
295                };
296
297                let open_files: OpenedFiles = Default::default();
298
299                let registered_device_id = match device_type {
300                    DeviceType::Keyboard => {
301                        let (key_client, key_server) =
302                            fidl::endpoints::create_sync_proxy::<futinput::KeyboardMarker>();
303                        inner.created_device =
304                            CreatedDevice::Keyboard(key_client, LinuxKeyboardEventParser::create());
305
306                        // Register a keyboard
307                        let register_res = proxy.register_keyboard_and_get_device_info(
308                            RegistryRegisterKeyboardAndGetDeviceInfoRequest {
309                                device: Some(key_server),
310                                ..Default::default()
311                            },
312                            zx::MonotonicInstant::INFINITE,
313                        );
314
315                        match register_res {
316                            Ok(resp) => match resp.device_id {
317                                Some(device_id) => {
318                                    inner.device_id = Some(device_id);
319                                    self.input_event_relay.add_keyboard_device(
320                                        device_id,
321                                        open_files.clone(),
322                                        None,
323                                    );
324                                    device_id
325                                }
326                                None => {
327                                    log_warn!(
328                                        "register_keyboard_and_get_device_info response does not include a device_id"
329                                    );
330                                    return error!(EPERM);
331                                }
332                            },
333                            Err(e) => {
334                                log_warn!(
335                                    "Uinput could not register Keyboard device to Registry: {:?}",
336                                    e
337                                );
338                                return error!(EPERM);
339                            }
340                        }
341                    }
342                    DeviceType::Touchscreen(_width, _height) => {
343                        let (touch_client, touch_server) =
344                            fidl::endpoints::create_sync_proxy::<futinput::TouchScreenMarker>();
345                        inner.created_device = CreatedDevice::Touchscreen(
346                            touch_client,
347                            Box::new(LinuxTouchEventParser::create()),
348                        );
349
350                        let mut request = RegistryRegisterTouchScreenAndGetDeviceInfoRequest {
351                            device: Some(touch_server),
352                            coordinate_unit: Some(CoordinateUnit::PhysicalPixels),
353                            ..Default::default()
354                        };
355
356                        if inner.x_range.is_some() && inner.y_range.is_some() {
357                            request.coordinate_unit = Some(CoordinateUnit::RegisteredDimensions);
358                            let x_range = inner.x_range.as_ref().unwrap();
359                            let y_range = inner.y_range.as_ref().unwrap();
360                            request.display_dimensions = Some(DisplayDimensions {
361                                min_x: x_range.min.into(),
362                                max_x: x_range.max.into(),
363                                min_y: y_range.min.into(),
364                                max_y: y_range.max.into(),
365                            });
366                        }
367
368                        // Register a touchscreen
369                        let register_res = proxy.register_touch_screen_and_get_device_info(
370                            request,
371                            zx::Instant::INFINITE,
372                        );
373
374                        match register_res {
375                            Ok(resp) => match resp.device_id {
376                                Some(device_id) => {
377                                    inner.device_id = Some(device_id);
378                                    self.input_event_relay.add_touch_device(
379                                        device_id,
380                                        open_files.clone(),
381                                        None,
382                                    );
383                                    device_id
384                                }
385                                None => {
386                                    log_warn!(
387                                        "register_touch_screen_and_get_device_info response does not include a device_id"
388                                    );
389                                    return error!(EPERM);
390                                }
391                            },
392                            Err(e) => {
393                                log_warn!(
394                                    "Uinput could not register Keyboard device to Registry: {:?}",
395                                    e
396                                );
397                                return error!(EPERM);
398                            }
399                        }
400                    }
401                };
402
403                let device = add_and_register_input_device(
404                    locked,
405                    current_task,
406                    VirtualDevice { input_id, device_type, open_files },
407                    registered_device_id,
408                )?;
409                inner.k_device = Some(device);
410
411                new_device();
412
413                Ok(SUCCESS)
414            }
415            None => {
416                log_warn!("No Registry available for Uinput.");
417                error!(EPERM)
418            }
419        }
420    }
421
422    fn ui_dev_destroy<L>(
423        &self,
424        locked: &mut Locked<L>,
425        current_task: &CurrentTask,
426    ) -> Result<SyscallResult, Errno>
427    where
428        L: LockEqualOrBefore<FileOpsCore>,
429    {
430        let mut inner = self.inner.lock();
431        match inner.device_id {
432            Some(device_id) => {
433                self.input_event_relay.remove_device(device_id);
434            }
435            None => {
436                // This is possible if caller does not call create device but calls destroy.
437                // No cleanup is needed for input event relay in this case.
438            }
439        }
440
441        match inner.k_device.clone() {
442            Some(device) => {
443                let kernel = current_task.kernel();
444                kernel.device_registry.remove_device(locked, current_task, device);
445            }
446            None => {
447                log_warn!("UI_DEV_DESTROY kHandle not found");
448                return error!(EPERM);
449            }
450        }
451        inner.k_device = None;
452        inner.created_device = CreatedDevice::None;
453
454        destroy_device();
455
456        Ok(SUCCESS)
457    }
458}
459
460// TODO(b/312467059): Remove once ESC -> Power workaround can be remove.
461static COUNT_OF_UINPUT_DEVICE: AtomicI32 = AtomicI32::new(0);
462
463fn new_device() {
464    let _ = COUNT_OF_UINPUT_DEVICE.fetch_add(1, Ordering::SeqCst);
465}
466
467fn destroy_device() {
468    let _ = COUNT_OF_UINPUT_DEVICE.fetch_sub(1, Ordering::SeqCst);
469}
470
471pub fn uinput_running() -> bool {
472    COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst) > 0
473}
474
475/// `UinputDeviceFile` doesn't implement the `close` method.
476impl CloseFreeSafe for UinputDeviceFile {}
477impl FileOps for UinputDeviceFile {
478    fileops_impl_seekless!();
479    fileops_impl_noop_sync!();
480
481    fn ioctl(
482        &self,
483        locked: &mut Locked<Unlocked>,
484        file: &FileObject,
485        current_task: &CurrentTask,
486        request: u32,
487        arg: SyscallArg,
488    ) -> Result<SyscallResult, Errno> {
489        match request {
490            uapi::UI_GET_VERSION => self.ui_get_version(current_task, arg.into()),
491            uapi::UI_SET_EVBIT => self.ui_set_evbit(arg),
492            uapi::UI_ABS_SETUP => self.ui_abs_setup(current_task, arg.into()),
493            // `fuchsia.ui.test.input.Registry` does not use some uinput ioctl
494            // request, just ignore the request and return SUCCESS, even args
495            // is invalid.
496            uapi::UI_SET_KEYBIT
497            | uapi::UI_SET_ABSBIT
498            | uapi::UI_SET_PHYS
499            | uapi::UI_SET_PROPBIT => Ok(SUCCESS),
500            uapi::UI_DEV_SETUP => self.ui_dev_setup(current_task, arg.into()),
501            uapi::UI_DEV_CREATE => self.ui_dev_create(locked, current_task),
502            uapi::UI_DEV_DESTROY => self.ui_dev_destroy(locked, current_task),
503            // default_ioctl() handles file system related requests and reject
504            // others.
505            _ => {
506                log_warn!("receive unknown ioctl request: {:?}", request);
507                default_ioctl(file, locked, current_task, request, arg)
508            }
509        }
510    }
511
512    fn write(
513        &self,
514        _locked: &mut Locked<FileOpsCore>,
515        _file: &vfs::FileObject,
516        current_task: &starnix_core::task::CurrentTask,
517        _offset: usize,
518        data: &mut dyn vfs::buffers::InputBuffer,
519    ) -> Result<usize, Errno> {
520        let content = data.read_all()?;
521        let event =
522            InputEventPtr::read_from_prefix(current_task, &content).map_err(|_| errno!(EINVAL))?;
523        let mut inner = self.inner.lock();
524
525        match &mut inner.created_device {
526            CreatedDevice::Keyboard(proxy, parser) => {
527                let input_report = parser.handle(event);
528                match input_report {
529                    Ok(Some(report)) => {
530                        if let Some(keyboard_report) = report.keyboard {
531                            let res = proxy.simulate_key_event(
532                                &KeyboardSimulateKeyEventRequest {
533                                    report: Some(keyboard_report),
534                                    ..Default::default()
535                                },
536                                zx::MonotonicInstant::INFINITE,
537                            );
538                            if res.is_err() {
539                                return error!(EIO);
540                            }
541                        }
542                    }
543                    Ok(None) => (),
544                    Err(e) => return Err(e),
545                }
546            }
547            CreatedDevice::Touchscreen(proxy, parser) => {
548                let input_report = parser.handle(event);
549                match input_report {
550                    Ok(Some(report)) => {
551                        if let Some(touch_report) = report.touch {
552                            let res = proxy.simulate_touch_event(
553                                &touch_report,
554                                zx::MonotonicInstant::INFINITE,
555                            );
556                            if res.is_err() {
557                                return error!(EIO);
558                            }
559                        }
560                    }
561                    Ok(None) => (),
562                    Err(e) => return Err(e),
563                }
564            }
565            CreatedDevice::None => return error!(EINVAL),
566        }
567
568        Ok(content.len())
569    }
570
571    fn read(
572        &self,
573        _locked: &mut Locked<FileOpsCore>,
574        _file: &vfs::FileObject,
575        _current_task: &starnix_core::task::CurrentTask,
576        _offset: usize,
577        _data: &mut dyn vfs::buffers::OutputBuffer,
578    ) -> Result<usize, Errno> {
579        log_warn!("uinput FD does not support read().");
580        error!(EINVAL)
581    }
582}
583
584#[derive(Clone)]
585pub struct VirtualDevice {
586    input_id: uapi::input_id,
587    device_type: DeviceType,
588    open_files: OpenedFiles,
589}
590
591impl DeviceOps for VirtualDevice {
592    fn open(
593        &self,
594        _locked: &mut Locked<FileOpsCore>,
595        _current_task: &CurrentTask,
596        _id: device_type::DeviceType,
597        _node: &NamespaceNode,
598        _flags: OpenFlags,
599    ) -> Result<Box<dyn FileOps>, Errno> {
600        let input_file = match &self.device_type {
601            DeviceType::Keyboard => Arc::new(InputFile::new_keyboard(self.input_id, None)),
602            DeviceType::Touchscreen(width, height) => {
603                Arc::new(InputFile::new_touch(self.input_id, width.clone(), height.clone(), None))
604            }
605        };
606
607        self.open_files.lock().push(Arc::downgrade(&input_file));
608
609        Ok(Box::new(input_file))
610    }
611}
612
613#[cfg(test)]
614mod test {
615    use super::*;
616    use crate::{EventProxyMode, start_input_relays_for_test};
617    use starnix_core::task::Kernel;
618    #[allow(deprecated, reason = "pre-existing usage")]
619    use starnix_core::testing::{AutoReleasableTask, create_kernel_task_and_unlocked};
620    use starnix_core::vfs::FileHandle;
621    use std::sync::Arc;
622    use test_case::test_case;
623
624    async fn new_kernel_objects() -> (
625        Arc<UinputDeviceFile>,
626        Arc<Kernel>,
627        AutoReleasableTask,
628        FileHandle,
629        &'static mut Locked<Unlocked>,
630    ) {
631        #[allow(deprecated, reason = "pre-existing usage")]
632        let (kernel, current_task, locked) = create_kernel_task_and_unlocked();
633        let (input_relay_handle, _, _, _, _, _, _, _, _, _, _, _) =
634            start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
635        let dev = Arc::new(UinputDeviceFile::new(input_relay_handle));
636
637        let root_namespace_node = current_task
638            .lookup_path_from_root(locked, ".".into())
639            .expect("failed to get namespace node for root");
640
641        let file_object = FileObject::new(
642            locked,
643            &current_task,
644            Box::new(dev.clone()),
645            // The input node doesn't really live at the root of the filesystem.
646            // But the test doesn't need to be 100% representative of production.
647            root_namespace_node,
648            OpenFlags::empty(),
649        )
650        .expect("FileObject::new failed");
651        (dev, kernel, current_task, file_object, locked)
652    }
653
654    #[test_case(uapi::EV_KEY, vec![uapi::EV_KEY as usize] => Ok(SUCCESS))]
655    #[test_case(uapi::EV_ABS, vec![uapi::EV_ABS as usize] => Ok(SUCCESS))]
656    #[test_case(uapi::EV_REL, vec![] => error!(EPERM))]
657    #[::fuchsia::test]
658    async fn ui_set_evbit(bit: u32, expected_evbits: Vec<usize>) -> Result<SyscallResult, Errno> {
659        let (dev, _kernel, current_task, file_object, locked) = new_kernel_objects().await;
660        let locked = locked.cast_locked::<Unlocked>();
661        let r = dev.ioctl(
662            locked,
663            &file_object,
664            &current_task,
665            uapi::UI_SET_EVBIT,
666            SyscallArg::from(bit as u64),
667        );
668        for expected_evbit in expected_evbits {
669            assert!(dev.inner.lock().enabled_evbits.get(expected_evbit).unwrap());
670        }
671        r
672    }
673
674    #[::fuchsia::test]
675    async fn ui_set_evbit_call_multi() {
676        let (dev, _kernel, current_task, file_object, locked) = new_kernel_objects().await;
677        let locked = locked.cast_locked::<Unlocked>();
678        let r = dev.ioctl(
679            locked,
680            &file_object,
681            &current_task,
682            uapi::UI_SET_EVBIT,
683            SyscallArg::from(uapi::EV_KEY as u64),
684        );
685        assert_eq!(r, Ok(SUCCESS));
686        let r = dev.ioctl(
687            locked,
688            &file_object,
689            &current_task,
690            uapi::UI_SET_EVBIT,
691            SyscallArg::from(uapi::EV_ABS as u64),
692        );
693        assert_eq!(r, Ok(SUCCESS));
694        assert!(dev.inner.lock().enabled_evbits.get(uapi::EV_KEY as usize).unwrap());
695        assert!(dev.inner.lock().enabled_evbits.get(uapi::EV_ABS as usize).unwrap());
696    }
697
698    #[::fuchsia::test]
699    async fn ui_set_keybit() {
700        let (dev, _kernel, current_task, file_object, locked) = new_kernel_objects().await;
701        let locked = locked.cast_locked::<Unlocked>();
702        let r = dev.ioctl(
703            locked,
704            &file_object,
705            &current_task,
706            uapi::UI_SET_KEYBIT,
707            SyscallArg::from(uapi::BTN_TOUCH as u64),
708        );
709        assert_eq!(r, Ok(SUCCESS));
710
711        // also test call multi times.
712        let r = dev.ioctl(
713            locked,
714            &file_object,
715            &current_task,
716            uapi::UI_SET_KEYBIT,
717            SyscallArg::from(uapi::KEY_SPACE as u64),
718        );
719        assert_eq!(r, Ok(SUCCESS));
720    }
721
722    #[::fuchsia::test]
723    async fn ui_set_absbit() {
724        let (dev, _kernel, current_task, file_object, locked) = new_kernel_objects().await;
725        let locked = locked.cast_locked::<Unlocked>();
726        let r = dev.ioctl(
727            locked,
728            &file_object,
729            &current_task,
730            uapi::UI_SET_ABSBIT,
731            SyscallArg::from(uapi::ABS_MT_SLOT as u64),
732        );
733        assert_eq!(r, Ok(SUCCESS));
734
735        // also test call multi times.
736        let r = dev.ioctl(
737            locked,
738            &file_object,
739            &current_task,
740            uapi::UI_SET_ABSBIT,
741            SyscallArg::from(uapi::ABS_MT_TOUCH_MAJOR as u64),
742        );
743        assert_eq!(r, Ok(SUCCESS));
744    }
745
746    #[::fuchsia::test]
747    async fn ui_set_propbit() {
748        let (dev, _kernel, current_task, file_object, locked) = new_kernel_objects().await;
749        let locked = locked.cast_locked::<Unlocked>();
750        let r = dev.ioctl(
751            locked,
752            &file_object,
753            &current_task,
754            uapi::UI_SET_PROPBIT,
755            SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
756        );
757        assert_eq!(r, Ok(SUCCESS));
758
759        // also test call multi times.
760        let r = dev.ioctl(
761            locked,
762            &file_object,
763            &current_task,
764            uapi::UI_SET_PROPBIT,
765            SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
766        );
767        assert_eq!(r, Ok(SUCCESS));
768    }
769}