1use 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
34const 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 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 let device_type = match self.enabled_evbits.clone().get(uapi::EV_ABS as usize) {
149 Some(true) => {
150 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 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 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 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 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 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 fn ui_dev_create_inner<L>(
280 &self,
281 locked: &mut Locked<L>,
282 current_task: &CurrentTask,
283 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 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 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 }
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
460static 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
475impl 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 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 _ => {
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, ¤t_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 ¤t_task,
644 Box::new(dev.clone()),
645 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 ¤t_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 ¤t_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 ¤t_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 ¤t_task,
706 uapi::UI_SET_KEYBIT,
707 SyscallArg::from(uapi::BTN_TOUCH as u64),
708 );
709 assert_eq!(r, Ok(SUCCESS));
710
711 let r = dev.ioctl(
713 locked,
714 &file_object,
715 ¤t_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 ¤t_task,
730 uapi::UI_SET_ABSBIT,
731 SyscallArg::from(uapi::ABS_MT_SLOT as u64),
732 );
733 assert_eq!(r, Ok(SUCCESS));
734
735 let r = dev.ioctl(
737 locked,
738 &file_object,
739 ¤t_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 ¤t_task,
754 uapi::UI_SET_PROPBIT,
755 SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
756 );
757 assert_eq!(r, Ok(SUCCESS));
758
759 let r = dev.ioctl(
761 locked,
762 &file_object,
763 ¤t_task,
764 uapi::UI_SET_PROPBIT,
765 SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
766 );
767 assert_eq!(r, Ok(SUCCESS));
768 }
769}