1use crate::input_device::InputDeviceInfoHandle;
6use crossbeam::queue::SegQueue;
7use fuchsia_inspect::Inspector;
8use futures::FutureExt;
9use starnix_core::fileops_impl_nonseekable;
10use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt};
11use starnix_core::task::{CurrentTask, EventHandler, WaitCanceler, WaitQueue, Waiter};
12use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
13use starnix_core::vfs::{FileObject, FileOps, fileops_impl_noop_sync};
14use starnix_logging::{log_info, track_stub};
15use starnix_sync::{InputFileInputFileLock, LockDepMutex};
16use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
17use starnix_types::time::duration_from_timeval;
18use starnix_uapi::errors::Errno;
19use starnix_uapi::open_flags::OpenFlags;
20use starnix_uapi::user_address::{ArchSpecific, MultiArchUserRef, UserAddress, UserRef};
21use starnix_uapi::vfs::FdEvents;
22use starnix_uapi::{
23 ABS_CNT, ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_PRESSURE, ABS_MT_SLOT,
24 ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MINOR, ABS_MT_TRACKING_ID, BTN_EXTRA, BTN_LEFT, BTN_MIDDLE,
25 BTN_MISC, BTN_RIGHT, BTN_SIDE, BTN_TOUCH, EV_CNT, FF_CNT, INPUT_PROP_CNT, INPUT_PROP_DIRECT,
26 INPUT_PROP_POINTER, KEY_CNT, KEY_DOWN, KEY_LEFT, KEY_OK, KEY_POWER, KEY_RIGHT, KEY_SLEEP,
27 KEY_UP, KEY_VOLUMEDOWN, KEY_VOLUMEUP, LED_CNT, MSC_CNT, REL_CNT, REL_HWHEEL, REL_WHEEL, REL_X,
28 REL_Y, SW_CNT, errno, error, uapi,
29};
30use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering};
31use std::sync::{Arc, Weak};
32use zerocopy::IntoBytes as _; uapi::check_arch_independent_layout! {
35 input_id {}
36 input_absinfo {}
37}
38
39type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
40
41pub struct InputFileStatus {
42 pub fidl_events_received_count: AtomicU64,
52
53 pub fidl_events_ignored_count: AtomicU64,
55
56 pub fidl_events_unexpected_count: AtomicU64,
60
61 pub fidl_events_converted_count: AtomicU64,
63
64 pub uapi_events_generated_count: AtomicU64,
66
67 pub last_generated_uapi_event_timestamp_ns: AtomicI64,
69
70 pub uapi_events_read_count: AtomicU64,
72
73 pub last_read_uapi_event_timestamp_ns: AtomicI64,
75
76 pub fd_read_count: AtomicU64,
78
79 pub fd_notify_count: AtomicU64,
81
82 pub opened_without_nonblock: AtomicBool,
84
85 pub open_timestamp_ns: AtomicI64,
87
88 pub closed: AtomicBool,
90
91 pub close_timestamp_ns: AtomicI64,
93
94 pub input_file: LockDepMutex<Weak<InputFile>, InputFileInputFileLock>,
96}
97
98impl InputFileStatus {
99 fn new(node: &fuchsia_inspect::Node) -> Arc<Self> {
100 let status = Arc::new(Self {
101 fidl_events_received_count: AtomicU64::new(0),
102 fidl_events_ignored_count: AtomicU64::new(0),
103 fidl_events_unexpected_count: AtomicU64::new(0),
104 fidl_events_converted_count: AtomicU64::new(0),
105 uapi_events_generated_count: AtomicU64::new(0),
106 last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
107 uapi_events_read_count: AtomicU64::new(0),
108 last_read_uapi_event_timestamp_ns: AtomicI64::new(0),
109 fd_read_count: AtomicU64::new(0),
110 fd_notify_count: AtomicU64::new(0),
111 opened_without_nonblock: AtomicBool::new(false),
112 open_timestamp_ns: AtomicI64::new(0),
113 closed: AtomicBool::new(false),
114 close_timestamp_ns: AtomicI64::new(0),
115 input_file: Default::default(),
116 });
117
118 let cloned_status = status.clone();
119 node.record_lazy_values("status", move || {
120 let cloned_cloned_status = cloned_status.clone();
121 async move {
122 let is_dropped = cloned_cloned_status.input_file.lock().upgrade().is_none();
123 if is_dropped && !cloned_cloned_status.closed.load(Ordering::Relaxed) {
124 cloned_cloned_status.closed.store(true, Ordering::Relaxed);
125 }
126
127 let inspector = Inspector::default();
128 let root = inspector.root();
129 root.record_uint(
130 "fd_read_count",
131 cloned_cloned_status.fd_read_count.load(Ordering::Relaxed),
132 );
133 root.record_uint(
134 "fd_notify_count",
135 cloned_cloned_status.fd_notify_count.load(Ordering::Relaxed),
136 );
137 root.record_bool(
138 "opened_without_nonblock",
139 cloned_cloned_status.opened_without_nonblock.load(Ordering::Relaxed),
140 );
141 root.record_int(
142 "open_timestamp_ns",
143 cloned_cloned_status.open_timestamp_ns.load(Ordering::Relaxed),
144 );
145 root.record_bool("closed", cloned_cloned_status.closed.load(Ordering::Relaxed));
146 root.record_int(
147 "close_timestamp_ns",
148 cloned_cloned_status.close_timestamp_ns.load(Ordering::Relaxed),
149 );
150 root.record_uint(
151 "fidl_events_received_count",
152 cloned_cloned_status.fidl_events_received_count.load(Ordering::Relaxed),
153 );
154 root.record_uint(
155 "fidl_events_ignored_count",
156 cloned_cloned_status.fidl_events_ignored_count.load(Ordering::Relaxed),
157 );
158 root.record_uint(
159 "fidl_events_unexpected_count",
160 cloned_cloned_status.fidl_events_unexpected_count.load(Ordering::Relaxed),
161 );
162 root.record_uint(
163 "fidl_events_converted_count",
164 cloned_cloned_status.fidl_events_converted_count.load(Ordering::Relaxed),
165 );
166 root.record_uint(
167 "uapi_events_generated_count",
168 cloned_cloned_status.uapi_events_generated_count.load(Ordering::Relaxed),
169 );
170 root.record_int(
171 "last_generated_uapi_event_timestamp_ns",
172 cloned_cloned_status
173 .last_generated_uapi_event_timestamp_ns
174 .load(Ordering::Relaxed),
175 );
176 root.record_uint(
177 "uapi_events_read_count",
178 cloned_cloned_status.uapi_events_read_count.load(Ordering::Relaxed),
179 );
180 root.record_int(
181 "last_read_uapi_event_timestamp_ns",
182 cloned_cloned_status.last_read_uapi_event_timestamp_ns.load(Ordering::Relaxed),
183 );
184 Ok(inspector)
185 }
186 .boxed()
187 });
188
189 status
190 }
191
192 pub fn count_received_events(&self, count: u64) {
193 self.fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
194 }
195
196 pub fn count_ignored_events(&self, count: u64) {
197 self.fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
198 }
199
200 pub fn count_unexpected_events(&self, count: u64) {
201 self.fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
202 }
203
204 pub fn count_converted_events(&self, count: u64) {
205 self.fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
206 }
207
208 pub fn count_generated_events(&self, count: u64, event_time_ns: i64) {
209 self.uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
210 self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
211 }
212
213 pub fn count_read_events(&self, count: u64, event_time_ns: i64) {
214 self.uapi_events_read_count.fetch_add(count, Ordering::Relaxed);
215 self.last_read_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
216 }
217
218 pub fn count_fd_read_calls(&self) {
219 self.fd_read_count.fetch_add(1, Ordering::Relaxed);
220 }
221
222 pub fn count_fd_notify_calls(&self) {
223 self.fd_notify_count.fetch_add(1, Ordering::Relaxed);
224 }
225
226 pub fn set_opened_without_nonblock(&self) {
227 self.opened_without_nonblock.store(true, Ordering::Relaxed);
228 }
229
230 pub fn set_open_timestamp(&self, timestamp: i64) {
231 self.open_timestamp_ns.store(timestamp, Ordering::Relaxed);
232 }
233
234 pub fn set_closed(&self) {
235 self.closed.store(true, Ordering::Relaxed);
236 }
237
238 pub fn set_closed_timestamp(&self, timestamp: i64) {
239 self.close_timestamp_ns.store(timestamp, Ordering::Relaxed);
240 }
241}
242
243pub struct InputFile {
244 driver_version: u32,
245 info: InputDeviceInfoHandle,
246 pub config: InputFileConfig,
247 events: SegQueue<LinuxEventWithTraceId>,
248 waiters: WaitQueue,
249 pub inspect_status: Option<Arc<InputFileStatus>>,
252}
253
254pub struct LinuxEventWithTraceId {
255 pub event: uapi::input_event,
256 pub trace_id: Option<fuchsia_trace::Id>,
257}
258
259impl LinuxEventWithTraceId {
260 pub fn new(event: uapi::input_event) -> Self {
261 match event.type_ as u32 {
262 uapi::EV_SYN => {
263 let trace_id = fuchsia_trace::Id::new();
264 fuchsia_trace::duration!("input", "linux_event_create");
265 fuchsia_trace::flow_begin!("input", "linux_event", trace_id);
266 LinuxEventWithTraceId { event: event, trace_id: Some(trace_id) }
267 }
268 _ => LinuxEventWithTraceId { event: event, trace_id: None },
271 }
272 }
273}
274
275pub const fn min_bytes(n_bits: u32) -> usize {
277 ((n_bits as usize) + 7) / 8
278}
279
280pub fn parse_axis_info(axis: &fidl_fuchsia_input_report::Axis) -> uapi::input_absinfo {
282 let minimum = axis.range.min.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
283 let maximum = axis.range.max.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
284 let resolution = match axis.unit.type_ {
285 fidl_fuchsia_input_report::UnitType::Meters => {
286 match (-3i32).checked_sub(axis.unit.exponent) {
289 Some(p) if (0..=9).contains(&p) => 10i32.pow(p as u32),
290 _ => 0,
291 }
292 }
293 _ => 0,
294 };
295 uapi::input_absinfo { minimum, maximum, resolution, ..Default::default() }
296}
297
298#[derive(Clone, Debug)]
300pub struct InputFileConfig {
301 pub supported_event_types: BitSet<{ min_bytes(EV_CNT) }>,
302 pub supported_keys: BitSet<{ min_bytes(KEY_CNT) }>,
303 pub supported_position_attributes: BitSet<{ min_bytes(ABS_CNT) }>,
304 pub supported_motion_attributes: BitSet<{ min_bytes(REL_CNT) }>,
305 pub supported_switches: BitSet<{ min_bytes(SW_CNT) }>,
306 pub supported_leds: BitSet<{ min_bytes(LED_CNT) }>,
307 pub supported_haptics: BitSet<{ min_bytes(FF_CNT) }>,
308 pub supported_misc_features: BitSet<{ min_bytes(MSC_CNT) }>,
309 pub properties: BitSet<{ min_bytes(INPUT_PROP_CNT) }>,
310 pub mt_slot_axis_info: uapi::input_absinfo,
311 pub mt_tracking_id_axis_info: uapi::input_absinfo,
312 pub mt_pressure_axis_info: uapi::input_absinfo,
313 pub mt_touch_major_axis_info: uapi::input_absinfo,
314 pub mt_touch_minor_axis_info: uapi::input_absinfo,
315 pub x_axis_info: uapi::input_absinfo,
316 pub y_axis_info: uapi::input_absinfo,
317}
318
319fn absinfo_eq(a: &uapi::input_absinfo, b: &uapi::input_absinfo) -> bool {
320 a.value == b.value
321 && a.minimum == b.minimum
322 && a.maximum == b.maximum
323 && a.fuzz == b.fuzz
324 && a.flat == b.flat
325 && a.resolution == b.resolution
326}
327
328impl PartialEq for InputFileConfig {
329 fn eq(&self, other: &Self) -> bool {
330 self.supported_event_types == other.supported_event_types
331 && self.supported_keys == other.supported_keys
332 && self.supported_position_attributes == other.supported_position_attributes
333 && self.supported_motion_attributes == other.supported_motion_attributes
334 && self.supported_switches == other.supported_switches
335 && self.supported_leds == other.supported_leds
336 && self.supported_haptics == other.supported_haptics
337 && self.supported_misc_features == other.supported_misc_features
338 && self.properties == other.properties
339 && absinfo_eq(&self.mt_slot_axis_info, &other.mt_slot_axis_info)
340 && absinfo_eq(&self.mt_tracking_id_axis_info, &other.mt_tracking_id_axis_info)
341 && absinfo_eq(&self.mt_pressure_axis_info, &other.mt_pressure_axis_info)
342 && absinfo_eq(&self.mt_touch_major_axis_info, &other.mt_touch_major_axis_info)
343 && absinfo_eq(&self.mt_touch_minor_axis_info, &other.mt_touch_minor_axis_info)
344 && absinfo_eq(&self.x_axis_info, &other.x_axis_info)
345 && absinfo_eq(&self.y_axis_info, &other.y_axis_info)
346 }
347}
348
349impl Default for InputFileConfig {
350 fn default() -> Self {
351 Self {
352 supported_event_types: BitSet::new(),
353 supported_keys: BitSet::new(),
354 supported_position_attributes: BitSet::new(),
355 supported_motion_attributes: BitSet::new(),
356 supported_switches: BitSet::new(),
357 supported_leds: BitSet::new(),
358 supported_haptics: BitSet::new(),
359 supported_misc_features: BitSet::new(),
360 properties: BitSet::new(),
361 mt_slot_axis_info: uapi::input_absinfo::default(),
362 mt_tracking_id_axis_info: uapi::input_absinfo::default(),
363 mt_pressure_axis_info: uapi::input_absinfo::default(),
364 mt_touch_major_axis_info: uapi::input_absinfo::default(),
365 mt_touch_minor_axis_info: uapi::input_absinfo::default(),
366 x_axis_info: uapi::input_absinfo::default(),
367 y_axis_info: uapi::input_absinfo::default(),
368 }
369 }
370}
371
372impl InputFileConfig {
373 pub fn from_device_descriptor(
374 descriptor: &fidl_fuchsia_input_report::DeviceDescriptor,
375 ) -> Self {
376 let mut config = Self::default();
377
378 if let Some(touch) = &descriptor.touch {
379 config.supported_event_types.set(uapi::EV_ABS);
380 config.supported_event_types.set(uapi::EV_KEY);
381
382 let touch_type = touch.input.as_ref().and_then(|i| i.touch_type);
383 match touch_type {
384 Some(fidl_fuchsia_input_report::TouchType::Touchpad) => {
385 config.properties.set(INPUT_PROP_POINTER);
386 config.supported_keys.set(uapi::BTN_TOOL_FINGER);
387 }
388 _ => {
389 config.properties.set(INPUT_PROP_DIRECT);
390 }
391 }
392
393 config.supported_keys.set(BTN_TOUCH);
395
396 config.supported_position_attributes = touch_position_attributes();
398
399 let mut max_contacts = 10;
400 if let Some(input) = &touch.input {
401 if let Some(contacts) = &input.contacts {
402 if let Some(first) = contacts.first() {
403 if let Some(x) = &first.position_x {
404 config.x_axis_info = parse_axis_info(x);
405 }
406 if let Some(y) = &first.position_y {
407 config.y_axis_info = parse_axis_info(y);
408 }
409 if let Some(pressure) = &first.pressure {
410 config.supported_position_attributes.set(ABS_MT_PRESSURE);
411 config.mt_pressure_axis_info = parse_axis_info(pressure);
412 }
413 match (&first.contact_width, &first.contact_height) {
414 (Some(w), Some(h)) => {
415 config.supported_position_attributes.set(ABS_MT_TOUCH_MAJOR);
416 config.supported_position_attributes.set(ABS_MT_TOUCH_MINOR);
417 if w.range.max >= h.range.max {
418 config.mt_touch_major_axis_info = parse_axis_info(w);
419 config.mt_touch_minor_axis_info = parse_axis_info(h);
420 } else {
421 config.mt_touch_major_axis_info = parse_axis_info(h);
422 config.mt_touch_minor_axis_info = parse_axis_info(w);
423 }
424 }
425 (Some(w), None) => {
426 config.supported_position_attributes.set(ABS_MT_TOUCH_MAJOR);
427 config.mt_touch_major_axis_info = parse_axis_info(w);
428 }
429 (None, Some(h)) => {
430 config.supported_position_attributes.set(ABS_MT_TOUCH_MAJOR);
431 config.mt_touch_major_axis_info = parse_axis_info(h);
432 }
433 (None, None) => {}
434 }
435 }
436 }
437 if let Some(contacts_limit) = input.max_contacts {
438 if contacts_limit > 0 {
439 max_contacts = contacts_limit;
440 }
441 }
442 if let Some(buttons) = &input.buttons {
443 for &button in buttons {
444 if let Some(key) =
445 starnix_modules_input_report_event_conversion::touch_button_to_linux_key(
446 button,
447 )
448 {
449 config.supported_keys.set(key);
450 }
451 }
452 }
453 }
454
455 config.mt_slot_axis_info = uapi::input_absinfo {
456 minimum: 0,
457 maximum: i32::try_from(
458 max_contacts
459 .min(fidl_fuchsia_input_report::TOUCH_MAX_CONTACTS)
460 .saturating_sub(1),
461 )
462 .unwrap_or(i32::MAX),
463 ..Default::default()
464 };
465 config.mt_tracking_id_axis_info =
466 uapi::input_absinfo { minimum: 0, maximum: i32::MAX, ..Default::default() };
467 }
468
469 if let Some(keyboard) = &descriptor.keyboard {
470 config.supported_event_types.set(uapi::EV_KEY);
471
472 let mut has_keys = false;
473 if let Some(input) = &keyboard.input {
474 if let Some(keys3) = &input.keys3 {
475 for key in keys3 {
476 let keycode = starnix_modules_input_app_event_conversion::keymap::KEY_MAP
477 .fuchsia_input_key_to_linux_keycode(*key);
478 if keycode >= BTN_MISC && keycode < KEY_OK {
479 continue;
480 }
481 if keycode != uapi::KEY_RESERVED && (keycode as usize) < KEY_CNT as usize {
482 config.supported_keys.set(keycode);
483 has_keys = true;
484 }
485 }
486 }
487 }
488 if !has_keys {
489 config.supported_keys |= keyboard_key_attributes();
490 }
491
492 if let Some(output) = &keyboard.output {
493 if let Some(leds) = &output.leds {
494 for led in leds {
495 let led_code = match led {
496 fidl_fuchsia_input_report::LedType::NumLock => Some(uapi::LED_NUML),
497 fidl_fuchsia_input_report::LedType::CapsLock => Some(uapi::LED_CAPSL),
498 fidl_fuchsia_input_report::LedType::ScrollLock => {
499 Some(uapi::LED_SCROLLL)
500 }
501 fidl_fuchsia_input_report::LedType::Compose => Some(uapi::LED_COMPOSE),
502 fidl_fuchsia_input_report::LedType::Kana => Some(uapi::LED_KANA),
503 _ => None,
504 };
505 if let Some(c) = led_code {
506 config.supported_event_types.set(uapi::EV_LED);
507 config.supported_leds.set(c);
508 }
509 }
510 }
511 }
512 }
513
514 if let Some(mouse) = &descriptor.mouse {
515 config.supported_event_types.set(uapi::EV_KEY);
516 config.supported_event_types.set(uapi::EV_REL);
517 if !config.properties.get(INPUT_PROP_DIRECT) {
518 config.properties.set(INPUT_PROP_POINTER);
519 }
520
521 let mut has_buttons = false;
522 let mut has_rel = false;
523 if let Some(input) = &mouse.input {
524 if let Some(buttons) = &input.buttons {
525 for &button in buttons {
526 if let Some(btn) =
527 starnix_modules_input_report_event_conversion::mouse_button_to_linux_button(
528 button,
529 )
530 {
531 config.supported_keys.set(btn);
532 has_buttons = true;
533 }
534 }
535 }
536 if input.movement_x.is_some() {
537 config.supported_motion_attributes.set(REL_X);
538 has_rel = true;
539 }
540 if input.movement_y.is_some() {
541 config.supported_motion_attributes.set(REL_Y);
542 has_rel = true;
543 }
544 if input.scroll_v.is_some() {
545 config.supported_motion_attributes.set(REL_WHEEL);
546 has_rel = true;
547 }
548 if input.scroll_h.is_some() {
549 config.supported_motion_attributes.set(REL_HWHEEL);
550 has_rel = true;
551 }
552 if input.scroll_v.is_some() || input.scroll_h.is_some() {
553 config.supported_motion_attributes.set(REL_X);
554 config.supported_motion_attributes.set(REL_Y);
555 }
556 }
557
558 if !has_buttons {
559 config.supported_keys |= mouse_key_attributes();
560 }
561
562 if !has_rel {
563 config.supported_motion_attributes |= mouse_motion_attributes();
564 }
565 }
566
567 if let Some(consumer_control) = &descriptor.consumer_control {
568 config.supported_event_types.set(uapi::EV_KEY);
569 let mut has_buttons = false;
570 if let Some(input) = &consumer_control.input {
571 if let Some(buttons) = &input.buttons {
572 for button in buttons {
573 if let Some(key) =
574 starnix_modules_input_report_event_conversion::consumer_control_button_to_linux_key(
575 *button,
576 )
577 {
578 config.supported_keys.set(key);
579 has_buttons = true;
580 }
581 }
582 }
583 }
584 if !has_buttons {
585 config.supported_keys.set(KEY_POWER);
586 config.supported_keys.set(KEY_VOLUMEUP);
587 config.supported_keys.set(KEY_VOLUMEDOWN);
588 }
589 }
590
591 config
592 }
593}
594
595fn keyboard_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
597 let mut attrs = BitSet::new();
598 attrs.set(INPUT_PROP_DIRECT);
599 attrs
600}
601
602fn touch_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
604 let mut attrs = BitSet::new();
605 attrs.set(BTN_TOUCH);
606 attrs.set(BTN_MISC); attrs.set(KEY_SLEEP);
608 attrs.set(KEY_UP);
609 attrs.set(KEY_LEFT);
610 attrs.set(KEY_RIGHT);
611 attrs.set(KEY_DOWN);
612
613 attrs
614}
615
616fn touch_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
618 let mut attrs = BitSet::new();
619 attrs.set(ABS_MT_SLOT);
620 attrs.set(ABS_MT_TRACKING_ID);
621 attrs.set(ABS_MT_POSITION_X);
622 attrs.set(ABS_MT_POSITION_Y);
623 attrs
624}
625
626fn touch_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
628 let mut attrs = BitSet::new();
629 attrs.set(INPUT_PROP_DIRECT);
630 attrs
631}
632
633fn keyboard_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
635 let mut attrs = BitSet::new();
636 for keycode in starnix_modules_input_app_event_conversion::keymap::KEY_MAP.all_linux_keycodes()
637 {
638 if keycode >= BTN_MISC && keycode < KEY_OK {
643 continue;
644 }
645 if (keycode as usize) < KEY_CNT as usize {
646 attrs.set(keycode);
647 }
648 }
649 attrs.set(BTN_MISC);
650 attrs.set(KEY_POWER);
651 attrs.set(KEY_VOLUMEUP);
652 attrs.set(KEY_VOLUMEDOWN);
653 attrs
654}
655
656fn keyboard_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
658 BitSet::new()
659}
660
661fn mouse_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
663 let mut attrs = BitSet::new();
664 attrs.set(BTN_LEFT);
668 attrs.set(BTN_RIGHT);
669 attrs.set(BTN_MIDDLE);
670 attrs.set(BTN_SIDE);
671 attrs.set(BTN_EXTRA);
672 attrs
673}
674
675fn mouse_motion_attributes() -> BitSet<{ min_bytes(REL_CNT) }> {
677 let mut attrs = BitSet::new();
678 attrs.set(REL_X);
679 attrs.set(REL_Y);
680 attrs.set(REL_WHEEL);
681 attrs.set(REL_HWHEEL);
682 attrs
683}
684
685fn mouse_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
687 let mut attrs = BitSet::new();
688 attrs.set(INPUT_PROP_POINTER);
691 attrs
692}
693
694impl InputFile {
695 const DRIVER_VERSION: u32 = 0;
701
702 pub fn new_touch(
710 info: InputDeviceInfoHandle,
711 width: i32,
712 height: i32,
713 node: &fuchsia_inspect::Node,
714 ) -> Self {
715 let config = InputFileConfig {
719 supported_event_types: BitSet::list([uapi::EV_ABS]),
720 supported_keys: touch_key_attributes(),
721 supported_position_attributes: touch_position_attributes(),
722 properties: touch_properties(),
723 mt_slot_axis_info: uapi::input_absinfo {
724 minimum: 0,
725 maximum: 10,
726 ..uapi::input_absinfo::default()
727 },
728 mt_tracking_id_axis_info: uapi::input_absinfo {
729 minimum: 0,
730 maximum: i32::MAX,
731 ..uapi::input_absinfo::default()
732 },
733 x_axis_info: uapi::input_absinfo {
734 minimum: 0,
735 maximum: i32::from(width),
736 ..uapi::input_absinfo::default()
739 },
740 y_axis_info: uapi::input_absinfo {
741 minimum: 0,
742 maximum: i32::from(height),
743 ..uapi::input_absinfo::default()
746 },
747 ..Default::default()
748 };
749 Self::new_from_config(info, config, node)
750 }
751
752 pub fn new_keyboard(info: InputDeviceInfoHandle, node: &fuchsia_inspect::Node) -> Self {
758 let config = InputFileConfig {
759 supported_event_types: BitSet::list([uapi::EV_KEY]),
760 supported_keys: keyboard_key_attributes(),
761 supported_position_attributes: keyboard_position_attributes(),
762 properties: keyboard_properties(),
763 ..Default::default()
764 };
765 Self::new_from_config(info, config, node)
766 }
767
768 pub fn new_mouse(info: InputDeviceInfoHandle, node: &fuchsia_inspect::Node) -> Self {
774 let config = InputFileConfig {
775 supported_event_types: BitSet::list([uapi::EV_KEY, uapi::EV_REL]),
779 supported_keys: mouse_key_attributes(),
780 supported_motion_attributes: mouse_motion_attributes(),
781 properties: mouse_properties(),
782 ..Default::default()
783 };
784 Self::new_from_config(info, config, node)
785 }
786
787 pub fn new_from_config(
789 info: InputDeviceInfoHandle,
790 config: InputFileConfig,
791 node: &fuchsia_inspect::Node,
792 ) -> Self {
793 Self {
794 driver_version: Self::DRIVER_VERSION,
795 info,
796 config,
797 events: SegQueue::new(),
798 waiters: WaitQueue::default(),
799 inspect_status: Some(InputFileStatus::new(node)),
800 }
801 }
802
803 #[cfg(test)]
805 pub fn from_device_descriptor(
806 info: InputDeviceInfoHandle,
807 descriptor: &fidl_fuchsia_input_report::DeviceDescriptor,
808 node: &fuchsia_inspect::Node,
809 ) -> Self {
810 let config = InputFileConfig::from_device_descriptor(descriptor);
811 Self::new_from_config(info, config, node)
812 }
813
814 pub fn init_inspect_status(self: &Arc<Self>) {
815 if let Some(inspect) = &self.inspect_status {
816 *inspect.input_file.lock() = Arc::downgrade(self);
817 }
818 }
819
820 pub fn add_events(&self, events: Vec<uapi::input_event>) {
821 if events.is_empty() {
822 return;
823 }
824 if let Some(inspect) = &self.inspect_status {
825 inspect.count_fd_notify_calls();
826 }
827 for event in events {
828 self.events.push(LinuxEventWithTraceId::new(event));
829 }
830 self.waiters.notify_fd_events(FdEvents::POLLIN);
831 }
832
833 pub fn read_events(&self, limit: usize) -> Vec<LinuxEventWithTraceId> {
834 if let Some(inspect) = &self.inspect_status {
835 inspect.count_fd_read_calls();
836 }
837 let mut events = vec![];
838 for _ in 0..limit {
839 if let Some(event) = self.events.pop() {
840 events.push(event);
841 } else {
842 break;
843 }
844 }
845 events
849 }
850}
851
852pub(crate) const EVIOC_VAR_LEN_MASK: u32 = !(uapi::_IOC_SIZEMASK << uapi::_IOC_SIZESHIFT);
856
857pub(crate) const EVIOCGNAME_BASE: u32 = uapi::EVIOCGNAME_0 & EVIOC_VAR_LEN_MASK;
858pub(crate) const EVIOCGPHYS_BASE: u32 = uapi::EVIOCGPHYS_0 & EVIOC_VAR_LEN_MASK;
859pub(crate) const EVIOCGUNIQ_BASE: u32 = uapi::EVIOCGUNIQ_0 & EVIOC_VAR_LEN_MASK;
860pub(crate) const EVIOCGKEY_BASE: u32 = uapi::EVIOCGKEY_0 & EVIOC_VAR_LEN_MASK;
861pub(crate) const EVIOCGLED_BASE: u32 = uapi::EVIOCGLED_0 & EVIOC_VAR_LEN_MASK;
862pub(crate) const EVIOCGSND_BASE: u32 = uapi::EVIOCGSND_0 & EVIOC_VAR_LEN_MASK;
863pub(crate) const EVIOCGSW_BASE: u32 = uapi::EVIOCGSW_0 & EVIOC_VAR_LEN_MASK;
864pub(crate) const EVIOCGPROP_BASE: u32 = uapi::EVIOCGPROP & EVIOC_VAR_LEN_MASK;
865pub(crate) const EVIOCGBIT_0_BASE: u32 = uapi::EVIOCGBIT_0 & EVIOC_VAR_LEN_MASK;
866pub(crate) const EVIOCGBIT_EV_KEY_BASE: u32 = uapi::EVIOCGBIT_EV_KEY & EVIOC_VAR_LEN_MASK;
867pub(crate) const EVIOCGBIT_EV_ABS_BASE: u32 = uapi::EVIOCGBIT_EV_ABS & EVIOC_VAR_LEN_MASK;
868pub(crate) const EVIOCGBIT_EV_REL_BASE: u32 = uapi::EVIOCGBIT_EV_REL & EVIOC_VAR_LEN_MASK;
869pub(crate) const EVIOCGBIT_EV_SW_BASE: u32 = uapi::EVIOCGBIT_EV_SW & EVIOC_VAR_LEN_MASK;
870pub(crate) const EVIOCGBIT_EV_LED_BASE: u32 = uapi::EVIOCGBIT_EV_LED & EVIOC_VAR_LEN_MASK;
871pub(crate) const EVIOCGBIT_EV_FF_BASE: u32 = uapi::EVIOCGBIT_EV_FF & EVIOC_VAR_LEN_MASK;
872pub(crate) const EVIOCGBIT_EV_MSC_BASE: u32 = uapi::EVIOCGBIT_EV_MSC & EVIOC_VAR_LEN_MASK;
873pub(crate) const EVIOCGBIT_EV_SND_BASE: u32 = uapi::EVIOCGBIT_EV_SND & EVIOC_VAR_LEN_MASK;
874
875pub(crate) const fn eviocgabs(axis: u32) -> u32 {
876 uapi::EVIOCGABS_X + axis
877}
878pub(crate) const EVIOCGABS_MT_PRESSURE: u32 = eviocgabs(uapi::ABS_MT_PRESSURE);
879pub(crate) const EVIOCGABS_MT_TOUCH_MAJOR: u32 = eviocgabs(uapi::ABS_MT_TOUCH_MAJOR);
880pub(crate) const EVIOCGABS_MT_TOUCH_MINOR: u32 = eviocgabs(uapi::ABS_MT_TOUCH_MINOR);
881
882impl FileOps for InputFile {
883 fileops_impl_nonseekable!();
884 fileops_impl_noop_sync!();
885
886 fn open(&self, file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
887 if let Some(inspect) = &self.inspect_status {
888 inspect.set_open_timestamp(zx::MonotonicInstant::get().into_nanos());
889 if (file.flags() & OpenFlags::NONBLOCK) != OpenFlags::NONBLOCK {
890 inspect.set_opened_without_nonblock();
891 }
892 }
893 Ok(())
894 }
895
896 fn close(
897 self: Box<Self>,
898 _file: &starnix_core::vfs::FileObjectState,
899 _current_task: &CurrentTask,
900 ) {
901 if let Some(inspect) = &self.inspect_status {
902 inspect.set_closed();
903 inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
904 }
905 }
906
907 fn ioctl(
908 &self,
909 _file: &FileObject,
910 current_task: &CurrentTask,
911 request: u32,
912 arg: SyscallArg,
913 ) -> Result<SyscallResult, Errno> {
914 let user_addr = UserAddress::from(arg);
915 match request {
916 uapi::EVIOCGVERSION => {
917 current_task.write_object(UserRef::new(user_addr), &self.driver_version)?;
918 Ok(SUCCESS)
919 }
920 uapi::EVIOCGID => {
921 let input_id = self.info.lock().input_id;
922 current_task.write_object(UserRef::new(user_addr), &input_id)?;
923 Ok(SUCCESS)
924 }
925 uapi::EVIOCGABS_MT_SLOT => {
926 current_task
927 .write_object(UserRef::new(user_addr), &self.config.mt_slot_axis_info)?;
928 Ok(SUCCESS)
929 }
930 uapi::EVIOCGABS_MT_TRACKING_ID => {
931 current_task
932 .write_object(UserRef::new(user_addr), &self.config.mt_tracking_id_axis_info)?;
933 Ok(SUCCESS)
934 }
935 uapi::EVIOCGABS_X | uapi::EVIOCGABS_MT_POSITION_X => {
936 current_task.write_object(UserRef::new(user_addr), &self.config.x_axis_info)?;
937 Ok(SUCCESS)
938 }
939 uapi::EVIOCGABS_Y | uapi::EVIOCGABS_MT_POSITION_Y => {
940 current_task.write_object(UserRef::new(user_addr), &self.config.y_axis_info)?;
941 Ok(SUCCESS)
942 }
943 EVIOCGABS_MT_PRESSURE => {
944 current_task
945 .write_object(UserRef::new(user_addr), &self.config.mt_pressure_axis_info)?;
946 Ok(SUCCESS)
947 }
948 EVIOCGABS_MT_TOUCH_MAJOR => {
949 current_task
950 .write_object(UserRef::new(user_addr), &self.config.mt_touch_major_axis_info)?;
951 Ok(SUCCESS)
952 }
953 EVIOCGABS_MT_TOUCH_MINOR => {
954 current_task
955 .write_object(UserRef::new(user_addr), &self.config.mt_touch_minor_axis_info)?;
956 Ok(SUCCESS)
957 }
958 uapi::EVIOCGRAB => {
959 track_stub!(TODO("https://fxbug.dev/322873200"), "EVIOCGRAB");
961 Ok(SUCCESS)
962 }
963
964 request_with_params => {
965 let buffer_bytes_count =
967 ((request_with_params >> uapi::_IOC_SIZESHIFT) & uapi::_IOC_SIZEMASK) as usize;
968
969 let dir = (request_with_params >> uapi::_IOC_DIRSHIFT) & uapi::_IOC_DIRMASK;
970 let ioc_type = (request_with_params >> uapi::_IOC_TYPESHIFT) & uapi::_IOC_TYPEMASK;
971
972 if dir != uapi::_IOC_READ || ioc_type != (b'E' as u32) {
974 track_stub!(
975 TODO("https://fxbug.dev/322873200"),
976 "input ioctl invalid type or dir",
977 request_with_params
978 );
979 return error!(EINVAL);
980 }
981
982 let write_bits = |bits: &[u8]| -> Result<SyscallResult, Errno> {
986 if buffer_bytes_count == 0 {
987 return Ok(SUCCESS);
988 }
989 if current_task.zero(user_addr, buffer_bytes_count)? != buffer_bytes_count {
991 return error!(EFAULT);
992 }
993 let to_copy = std::cmp::min(bits.len(), buffer_bytes_count);
994 current_task.write_memory(user_addr, &bits[..to_copy])?;
995 Ok(SUCCESS)
996 };
997
998 let write_string = |bytes: &[u8]| -> Result<SyscallResult, Errno> {
1001 if buffer_bytes_count == 0 {
1002 return Ok(SUCCESS);
1003 }
1004 if current_task.zero(user_addr, buffer_bytes_count)? != buffer_bytes_count {
1006 return error!(EFAULT);
1007 }
1008 let to_copy = std::cmp::min(bytes.len(), buffer_bytes_count.saturating_sub(1));
1009 current_task.write_memory(user_addr, &bytes[..to_copy])?;
1010 Ok((to_copy + 1).into())
1013 };
1014
1015 match request_with_params & EVIOC_VAR_LEN_MASK {
1016 EVIOCGBIT_0_BASE => write_bits(&self.config.supported_event_types.bytes),
1017 EVIOCGBIT_EV_KEY_BASE => write_bits(&self.config.supported_keys.bytes),
1018 EVIOCGBIT_EV_ABS_BASE => {
1019 write_bits(&self.config.supported_position_attributes.bytes)
1020 }
1021 EVIOCGBIT_EV_REL_BASE => {
1022 write_bits(&self.config.supported_motion_attributes.bytes)
1023 }
1024 EVIOCGBIT_EV_SW_BASE => write_bits(&self.config.supported_switches.bytes),
1025 EVIOCGBIT_EV_LED_BASE => write_bits(&self.config.supported_leds.bytes),
1026 EVIOCGBIT_EV_FF_BASE => write_bits(&self.config.supported_haptics.bytes),
1027 EVIOCGBIT_EV_MSC_BASE => write_bits(&self.config.supported_misc_features.bytes),
1028 EVIOCGBIT_EV_SND_BASE => write_bits(&[]),
1029 EVIOCGPROP_BASE => write_bits(&self.config.properties.bytes),
1030 EVIOCGNAME_BASE => {
1031 let name = self.info.lock().name.clone();
1032 write_string(name.as_bytes())
1033 }
1034 EVIOCGPHYS_BASE => {
1035 let phys = format!("starnix/{}", self.info.lock().name);
1036 write_string(phys.as_bytes())
1037 }
1038 EVIOCGUNIQ_BASE => {
1039 error!(ENOENT)
1042 }
1043 EVIOCGKEY_BASE | EVIOCGLED_BASE | EVIOCGSND_BASE | EVIOCGSW_BASE => {
1044 track_stub!(
1060 TODO("https://fxbug.dev/322873200"),
1061 "evdev current-state query",
1062 request_with_params
1063 );
1064 write_bits(&[])
1065 }
1066 _ => {
1067 track_stub!(
1068 TODO("https://fxbug.dev/322873200"),
1069 "input ioctl",
1070 request_with_params
1071 );
1072 error!(EINVAL)
1073 }
1074 }
1075 }
1076 }
1077 }
1078
1079 fn read(
1080 &self,
1081 _file: &FileObject,
1082 current_task: &CurrentTask,
1083 offset: usize,
1084 data: &mut dyn OutputBuffer,
1085 ) -> Result<usize, Errno> {
1086 fuchsia_trace::duration!("input", "InputFile::read");
1087 debug_assert!(offset == 0);
1088 let input_event_size = InputEventPtr::size_of_object_for(current_task);
1089
1090 let limit = data.available() / input_event_size;
1093 let events = self.read_events(limit);
1094 if events.is_empty() {
1095 log_info!("read() returning EAGAIN");
1097 return error!(EAGAIN);
1098 }
1099
1100 let last_event_timeval = events.last().expect("events is nonempty").event.time;
1101 let last_event_time_ns = duration_from_timeval::<zx::MonotonicTimeline>(last_event_timeval)
1102 .unwrap()
1103 .into_nanos();
1104 self.inspect_status
1105 .clone()
1106 .map(|status| status.count_read_events(events.len() as u64, last_event_time_ns));
1107
1108 for event in &events {
1109 if let Some(trace_id) = event.trace_id {
1110 fuchsia_trace::duration!("input", "linux_event_read");
1111 fuchsia_trace::flow_end!("input", "linux_event", trace_id);
1112 }
1113 }
1114
1115 if current_task.is_arch32() {
1116 let events: Result<Vec<uapi::arch32::input_event>, _> =
1117 events.iter().map(|e| uapi::arch32::input_event::try_from(e.event)).collect();
1118 let events = events.map_err(|_| errno!(EINVAL))?;
1119 data.write_all(events.as_bytes())
1120 } else {
1121 let events: Vec<uapi::input_event> = events.iter().map(|e| e.event).collect();
1122 data.write_all(events.as_bytes())
1123 }
1124 }
1125
1126 fn write(
1127 &self,
1128 _file: &FileObject,
1129 _current_task: &CurrentTask,
1130 offset: usize,
1131 _data: &mut dyn InputBuffer,
1132 ) -> Result<usize, Errno> {
1133 debug_assert!(offset == 0);
1134 track_stub!(TODO("https://fxbug.dev/322874385"), "write() on input device");
1135 error!(EOPNOTSUPP)
1136 }
1137
1138 fn wait_async(
1139 &self,
1140 _file: &FileObject,
1141 _current_task: &CurrentTask,
1142 waiter: &Waiter,
1143 events: FdEvents,
1144 handler: EventHandler,
1145 ) -> Option<WaitCanceler> {
1146 Some(self.waiters.wait_async_fd_events(waiter, events, handler))
1147 }
1148
1149 fn query_events(
1150 &self,
1151 _file: &FileObject,
1152 _current_task: &CurrentTask,
1153 ) -> Result<FdEvents, Errno> {
1154 Ok(if self.events.is_empty() { FdEvents::empty() } else { FdEvents::POLLIN })
1155 }
1156}
1157
1158pub struct ArcInputFile(pub Arc<InputFile>);
1159
1160impl FileOps for ArcInputFile {
1161 fileops_impl_nonseekable!();
1162 fileops_impl_noop_sync!();
1163
1164 fn open(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
1165 self.0.as_ref().open(file, current_task)
1166 }
1167
1168 fn close(
1169 self: Box<Self>,
1170 _file: &starnix_core::vfs::FileObjectState,
1171 _current_task: &CurrentTask,
1172 ) {
1173 let arc_file = *self;
1174 if let Some(inspect) = &arc_file.0.inspect_status {
1175 inspect.set_closed();
1176 inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
1177 }
1178 }
1179
1180 fn ioctl(
1181 &self,
1182 file: &FileObject,
1183 current_task: &CurrentTask,
1184 request: u32,
1185 arg: SyscallArg,
1186 ) -> Result<SyscallResult, Errno> {
1187 self.0.as_ref().ioctl(file, current_task, request, arg)
1188 }
1189
1190 fn read(
1191 &self,
1192 file: &FileObject,
1193 current_task: &CurrentTask,
1194 offset: usize,
1195 data: &mut dyn OutputBuffer,
1196 ) -> Result<usize, Errno> {
1197 self.0.as_ref().read(file, current_task, offset, data)
1198 }
1199
1200 fn write(
1201 &self,
1202 file: &FileObject,
1203 current_task: &CurrentTask,
1204 offset: usize,
1205 data: &mut dyn InputBuffer,
1206 ) -> Result<usize, Errno> {
1207 self.0.as_ref().write(file, current_task, offset, data)
1208 }
1209
1210 fn wait_async(
1211 &self,
1212 file: &FileObject,
1213 current_task: &CurrentTask,
1214 waiter: &Waiter,
1215 events: FdEvents,
1216 handler: EventHandler,
1217 ) -> Option<WaitCanceler> {
1218 self.0.as_ref().wait_async(file, current_task, waiter, events, handler)
1219 }
1220
1221 fn query_events(
1222 &self,
1223 file: &FileObject,
1224 current_task: &CurrentTask,
1225 ) -> Result<FdEvents, Errno> {
1226 self.0.as_ref().query_events(file, current_task)
1227 }
1228}
1229
1230#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1231pub struct BitSet<const NUM_BYTES: usize> {
1232 bytes: [u8; NUM_BYTES],
1233}
1234
1235impl<const NUM_BYTES: usize> BitSet<{ NUM_BYTES }> {
1236 pub const fn new() -> Self {
1237 Self { bytes: [0; NUM_BYTES] }
1238 }
1239
1240 pub const fn list<const N: usize>(bits: [u32; N]) -> Self {
1241 let mut bitset = Self::new();
1242 let mut i = 0;
1243 while i < bits.len() {
1244 bitset.set(bits[i]);
1245 i += 1;
1246 }
1247 bitset
1248 }
1249
1250 pub const fn set(&mut self, bitnum: u32) {
1251 let bitnum = bitnum as usize;
1252 let byte = bitnum / 8;
1253 let bit = bitnum % 8;
1254 self.bytes[byte] |= 1 << bit;
1255 }
1256
1257 pub fn get(&self, bitnum: u32) -> bool {
1258 let bitnum = bitnum as usize;
1259 let byte = bitnum / 8;
1260 let bit = bitnum % 8;
1261 (self.bytes[byte] & (1 << bit)) != 0
1262 }
1263}
1264
1265impl<const NUM_BYTES: usize> std::ops::BitOrAssign for BitSet<{ NUM_BYTES }> {
1266 fn bitor_assign(&mut self, rhs: Self) {
1267 for i in 0..NUM_BYTES {
1268 self.bytes[i] |= rhs.bytes[i];
1269 }
1270 }
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275 use super::*;
1276 use crate::input_device::InputDeviceInfo;
1277 use std::sync::atomic::Ordering;
1278
1279 #[test]
1280 fn test_keyboard_input_file_attributes() {
1281 let inspector = fuchsia_inspect::Inspector::default();
1282 let node = inspector.root();
1283 let keyboard_file = InputFile::new_keyboard(
1284 InputDeviceInfo::new(
1285 uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1286 "keyboard_test".to_string(),
1287 ),
1288 node,
1289 );
1290
1291 assert!(keyboard_file.config.supported_event_types.get(uapi::EV_KEY));
1293 assert!(!keyboard_file.config.supported_event_types.get(uapi::EV_REL));
1294 assert!(!keyboard_file.config.supported_event_types.get(uapi::EV_ABS));
1295
1296 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_A));
1298 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_Z));
1299 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_1));
1300 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_ENTER));
1301 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_SPACE));
1302 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_ESC));
1303 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_TAB));
1304 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_BACKSPACE));
1305 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_LEFTSHIFT));
1306 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_LEFTCTRL));
1307 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_LEFTALT));
1308 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_POWER));
1309 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_VOLUMEUP));
1310 assert!(keyboard_file.config.supported_keys.get(uapi::KEY_VOLUMEDOWN));
1311 assert!(keyboard_file.config.supported_keys.get(uapi::BTN_MISC));
1312
1313 assert!(keyboard_file.config.properties.get(uapi::INPUT_PROP_DIRECT));
1315 assert!(!keyboard_file.config.properties.get(uapi::INPUT_PROP_POINTER));
1316 }
1317
1318 #[test]
1319 fn test_read_events_no_notify_when_buffer_full() {
1320 let inspector = fuchsia_inspect::Inspector::default();
1321 let node = inspector.root();
1322 let input_file = InputFile::new_touch(
1323 InputDeviceInfo::new(
1324 uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1325 "touch_test".to_string(),
1326 ),
1327 100,
1328 100,
1329 node,
1330 );
1331
1332 let event1 = uapi::input_event {
1334 type_: uapi::EV_KEY as u16,
1335 code: 1,
1336 value: 1,
1337 ..Default::default()
1338 };
1339 let event2 = uapi::input_event {
1340 type_: uapi::EV_KEY as u16,
1341 code: 2,
1342 value: 1,
1343 ..Default::default()
1344 };
1345 input_file.add_events(vec![event1, event2]);
1346
1347 let notify_count =
1349 input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
1350 assert_eq!(notify_count, 1);
1351
1352 let events = input_file.read_events(1);
1354 assert_eq!(events.len(), 1);
1355
1356 let notify_count =
1358 input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
1359 assert_eq!(notify_count, 1);
1360 }
1361
1362 #[test]
1363 fn test_bitset_set_and_get() {
1364 let mut bitset = BitSet::<4>::new();
1365 assert!(!bitset.get(0));
1366 assert!(!bitset.get(15));
1367 assert!(!bitset.get(31));
1368
1369 bitset.set(0);
1370 bitset.set(15);
1371 bitset.set(31);
1372
1373 assert!(bitset.get(0));
1374 assert!(bitset.get(15));
1375 assert!(bitset.get(31));
1376 assert!(!bitset.get(1));
1377 assert!(!bitset.get(14));
1378 assert!(!bitset.get(30));
1379 }
1380
1381 #[test]
1382 fn test_mouse_capabilities() {
1383 let inspector = fuchsia_inspect::Inspector::default();
1384 let node = inspector.root();
1385 let mouse_file = InputFile::new_mouse(
1386 InputDeviceInfo::new(
1387 uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1388 "mouse_test".to_string(),
1389 ),
1390 node,
1391 );
1392
1393 assert!(mouse_file.config.supported_event_types.get(uapi::EV_KEY));
1395 assert!(mouse_file.config.supported_event_types.get(uapi::EV_REL));
1396 assert!(!mouse_file.config.supported_event_types.get(uapi::EV_ABS));
1397
1398 assert!(mouse_file.config.supported_keys.get(BTN_LEFT));
1400 assert!(mouse_file.config.supported_keys.get(BTN_RIGHT));
1401 assert!(mouse_file.config.supported_keys.get(BTN_MIDDLE));
1402 assert!(mouse_file.config.supported_keys.get(BTN_SIDE));
1403 assert!(mouse_file.config.supported_keys.get(BTN_EXTRA));
1404 assert!(!mouse_file.config.supported_keys.get(BTN_TOUCH));
1405
1406 assert!(mouse_file.config.supported_motion_attributes.get(REL_X));
1408 assert!(mouse_file.config.supported_motion_attributes.get(REL_Y));
1409 assert!(mouse_file.config.supported_motion_attributes.get(REL_WHEEL));
1410 assert!(mouse_file.config.supported_motion_attributes.get(REL_HWHEEL));
1411
1412 assert!(mouse_file.config.properties.get(INPUT_PROP_POINTER));
1414 assert!(!mouse_file.config.properties.get(INPUT_PROP_DIRECT));
1415 }
1416
1417 #[::fuchsia::test]
1418 fn test_parse_axis_info() {
1419 let axis_mm = fidl_fuchsia_input_report::Axis {
1420 range: fidl_fuchsia_input_report::Range { min: 0, max: 1080 },
1421 unit: fidl_fuchsia_input_report::Unit {
1422 type_: fidl_fuchsia_input_report::UnitType::Meters,
1423 exponent: -3,
1424 },
1425 };
1426 let absinfo = parse_axis_info(&axis_mm);
1427 assert_eq!(absinfo.minimum, 0);
1428 assert_eq!(absinfo.maximum, 1080);
1429 assert_eq!(absinfo.resolution, 1);
1430
1431 let axis_um = fidl_fuchsia_input_report::Axis {
1432 range: fidl_fuchsia_input_report::Range { min: 0, max: 2400 },
1433 unit: fidl_fuchsia_input_report::Unit {
1434 type_: fidl_fuchsia_input_report::UnitType::Meters,
1435 exponent: -6,
1436 },
1437 };
1438 let absinfo_um = parse_axis_info(&axis_um);
1439 assert_eq!(absinfo_um.minimum, 0);
1440 assert_eq!(absinfo_um.maximum, 2400);
1441 assert_eq!(absinfo_um.resolution, 1000);
1442
1443 let axis_none = fidl_fuchsia_input_report::Axis {
1444 range: fidl_fuchsia_input_report::Range { min: -100, max: 100 },
1445 unit: fidl_fuchsia_input_report::Unit {
1446 type_: fidl_fuchsia_input_report::UnitType::None,
1447 exponent: 0,
1448 },
1449 };
1450 let absinfo_none = parse_axis_info(&axis_none);
1451 assert_eq!(absinfo_none.minimum, -100);
1452 assert_eq!(absinfo_none.maximum, 100);
1453 assert_eq!(absinfo_none.resolution, 0);
1454
1455 let axis_clamp = fidl_fuchsia_input_report::Axis {
1456 range: fidl_fuchsia_input_report::Range { min: i64::MIN, max: i64::MAX },
1457 unit: fidl_fuchsia_input_report::Unit {
1458 type_: fidl_fuchsia_input_report::UnitType::None,
1459 exponent: 0,
1460 },
1461 };
1462 let absinfo_clamp = parse_axis_info(&axis_clamp);
1463 assert_eq!(absinfo_clamp.minimum, i32::MIN);
1464 assert_eq!(absinfo_clamp.maximum, i32::MAX);
1465
1466 let axis_extreme_max = fidl_fuchsia_input_report::Axis {
1467 range: fidl_fuchsia_input_report::Range { min: 0, max: 100 },
1468 unit: fidl_fuchsia_input_report::Unit {
1469 type_: fidl_fuchsia_input_report::UnitType::Meters,
1470 exponent: i32::MAX,
1471 },
1472 };
1473 let absinfo_extreme_max = parse_axis_info(&axis_extreme_max);
1474 assert_eq!(absinfo_extreme_max.resolution, 0);
1475
1476 let axis_extreme_min = fidl_fuchsia_input_report::Axis {
1477 range: fidl_fuchsia_input_report::Range { min: 0, max: 100 },
1478 unit: fidl_fuchsia_input_report::Unit {
1479 type_: fidl_fuchsia_input_report::UnitType::Meters,
1480 exponent: i32::MIN,
1481 },
1482 };
1483 let absinfo_extreme_min = parse_axis_info(&axis_extreme_min);
1484 assert_eq!(absinfo_extreme_min.resolution, 0);
1485 }
1486
1487 #[::fuchsia::test]
1488 fn test_input_file_from_touch_descriptor() {
1489 let inspector = fuchsia_inspect::Inspector::default();
1490 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1491 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
1492 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
1493 contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
1494 position_x: Some(fidl_fuchsia_input_report::Axis {
1495 range: fidl_fuchsia_input_report::Range { min: 0, max: 1080 },
1496 unit: fidl_fuchsia_input_report::Unit {
1497 type_: fidl_fuchsia_input_report::UnitType::Meters,
1498 exponent: -3,
1499 },
1500 }),
1501 position_y: Some(fidl_fuchsia_input_report::Axis {
1502 range: fidl_fuchsia_input_report::Range { min: 0, max: 2400 },
1503 unit: fidl_fuchsia_input_report::Unit {
1504 type_: fidl_fuchsia_input_report::UnitType::Meters,
1505 exponent: -3,
1506 },
1507 }),
1508 pressure: Some(fidl_fuchsia_input_report::Axis {
1509 range: fidl_fuchsia_input_report::Range { min: 0, max: 255 },
1510 unit: fidl_fuchsia_input_report::Unit {
1511 type_: fidl_fuchsia_input_report::UnitType::None,
1512 exponent: 0,
1513 },
1514 }),
1515 contact_width: Some(fidl_fuchsia_input_report::Axis {
1516 range: fidl_fuchsia_input_report::Range { min: 0, max: 100 },
1517 unit: fidl_fuchsia_input_report::Unit {
1518 type_: fidl_fuchsia_input_report::UnitType::None,
1519 exponent: 0,
1520 },
1521 }),
1522 contact_height: Some(fidl_fuchsia_input_report::Axis {
1523 range: fidl_fuchsia_input_report::Range { min: 0, max: 100 },
1524 unit: fidl_fuchsia_input_report::Unit {
1525 type_: fidl_fuchsia_input_report::UnitType::None,
1526 exponent: 0,
1527 },
1528 }),
1529 ..Default::default()
1530 }]),
1531 max_contacts: Some(5),
1532 touch_type: Some(fidl_fuchsia_input_report::TouchType::Touchscreen),
1533 buttons: Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1534 ..Default::default()
1535 }),
1536 ..Default::default()
1537 }),
1538 ..Default::default()
1539 };
1540
1541 let info = InputDeviceInfo::new(
1542 uapi::input_id { bustype: 3, vendor: 0x18d1, product: 0x5678, version: 1 },
1543 "test_touch".to_string(),
1544 );
1545 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1546
1547 assert!(file.config.supported_event_types.get(uapi::EV_ABS));
1548 assert!(file.config.supported_event_types.get(uapi::EV_KEY));
1549 assert!(file.config.properties.get(uapi::INPUT_PROP_DIRECT));
1550 assert!(!file.config.properties.get(uapi::INPUT_PROP_POINTER));
1551
1552 assert_eq!(file.config.x_axis_info.minimum, 0);
1553 assert_eq!(file.config.x_axis_info.maximum, 1080);
1554 assert_eq!(file.config.x_axis_info.resolution, 1);
1555
1556 assert_eq!(file.config.y_axis_info.minimum, 0);
1557 assert_eq!(file.config.y_axis_info.maximum, 2400);
1558 assert_eq!(file.config.y_axis_info.resolution, 1);
1559
1560 assert_eq!(file.config.mt_slot_axis_info.minimum, 0);
1561 assert_eq!(file.config.mt_slot_axis_info.maximum, 4);
1562 assert_eq!(file.config.mt_tracking_id_axis_info.maximum, i32::MAX);
1563
1564 assert_eq!(file.config.mt_pressure_axis_info.maximum, 255);
1565 assert_eq!(file.config.mt_touch_major_axis_info.maximum, 100);
1566 assert_eq!(file.config.mt_touch_minor_axis_info.maximum, 100);
1567
1568 assert!(file.config.supported_position_attributes.get(uapi::ABS_MT_SLOT));
1569 assert!(file.config.supported_position_attributes.get(uapi::ABS_MT_TRACKING_ID));
1570 assert!(file.config.supported_position_attributes.get(uapi::ABS_MT_POSITION_X));
1571 assert!(file.config.supported_position_attributes.get(uapi::ABS_MT_POSITION_Y));
1572 assert!(file.config.supported_position_attributes.get(uapi::ABS_MT_PRESSURE));
1573 assert!(file.config.supported_position_attributes.get(uapi::ABS_MT_TOUCH_MAJOR));
1574 assert!(file.config.supported_position_attributes.get(uapi::ABS_MT_TOUCH_MINOR));
1575 assert!(!file.config.supported_position_attributes.get(uapi::ABS_X));
1576 assert!(!file.config.supported_position_attributes.get(uapi::ABS_Y));
1577
1578 assert!(file.config.supported_keys.get(uapi::BTN_TOUCH));
1579 assert!(file.config.supported_keys.get(uapi::KEY_SLEEP));
1580 assert!(!file.config.supported_keys.get(uapi::KEY_UP));
1581 }
1582
1583 #[::fuchsia::test]
1584 fn test_input_file_from_touch_descriptor_no_buttons() {
1585 let inspector = fuchsia_inspect::Inspector::default();
1586 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1587 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
1588 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
1589 contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
1590 position_x: Some(fidl_fuchsia_input_report::Axis {
1591 range: fidl_fuchsia_input_report::Range { min: 0, max: 1080 },
1592 unit: fidl_fuchsia_input_report::Unit {
1593 type_: fidl_fuchsia_input_report::UnitType::None,
1594 exponent: 0,
1595 },
1596 }),
1597 position_y: Some(fidl_fuchsia_input_report::Axis {
1598 range: fidl_fuchsia_input_report::Range { min: 0, max: 2400 },
1599 unit: fidl_fuchsia_input_report::Unit {
1600 type_: fidl_fuchsia_input_report::UnitType::None,
1601 exponent: 0,
1602 },
1603 }),
1604 ..Default::default()
1605 }]),
1606 buttons: None,
1607 ..Default::default()
1608 }),
1609 ..Default::default()
1610 }),
1611 ..Default::default()
1612 };
1613
1614 let info = InputDeviceInfo::new(
1615 uapi::input_id { bustype: 3, vendor: 0x18d1, product: 0x5678, version: 1 },
1616 "test_touch_no_buttons".to_string(),
1617 );
1618 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1619
1620 assert!(file.config.supported_keys.get(uapi::BTN_TOUCH));
1621 assert!(!file.config.supported_keys.get(uapi::KEY_SLEEP));
1622 assert!(!file.config.supported_keys.get(uapi::KEY_UP));
1623 }
1624
1625 #[::fuchsia::test]
1626 fn test_input_file_from_touchpad_descriptor() {
1627 let inspector = fuchsia_inspect::Inspector::default();
1628 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1629 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
1630 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
1631 contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
1632 position_x: Some(fidl_fuchsia_input_report::Axis {
1633 range: fidl_fuchsia_input_report::Range { min: 0, max: 1920 },
1634 unit: fidl_fuchsia_input_report::Unit {
1635 type_: fidl_fuchsia_input_report::UnitType::Meters,
1636 exponent: -3,
1637 },
1638 }),
1639 position_y: Some(fidl_fuchsia_input_report::Axis {
1640 range: fidl_fuchsia_input_report::Range { min: 0, max: 1080 },
1641 unit: fidl_fuchsia_input_report::Unit {
1642 type_: fidl_fuchsia_input_report::UnitType::Meters,
1643 exponent: -3,
1644 },
1645 }),
1646 ..Default::default()
1647 }]),
1648 max_contacts: Some(2),
1649 touch_type: Some(fidl_fuchsia_input_report::TouchType::Touchpad),
1650 ..Default::default()
1651 }),
1652 ..Default::default()
1653 }),
1654 ..Default::default()
1655 };
1656
1657 let info = InputDeviceInfo::new(
1658 uapi::input_id { bustype: 3, vendor: 0x18d1, product: 0x1234, version: 1 },
1659 "test_touchpad".to_string(),
1660 );
1661 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1662
1663 assert!(file.config.supported_event_types.get(uapi::EV_ABS));
1664 assert!(file.config.properties.get(uapi::INPUT_PROP_POINTER));
1665 assert!(!file.config.properties.get(uapi::INPUT_PROP_DIRECT));
1666 assert!(file.config.supported_keys.get(uapi::BTN_TOOL_FINGER));
1667 assert_eq!(file.config.mt_slot_axis_info.maximum, 1);
1668 }
1669
1670 #[::fuchsia::test]
1671 fn test_input_file_from_keyboard_descriptor() {
1672 let inspector = fuchsia_inspect::Inspector::default();
1673 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1674 keyboard: Some(fidl_fuchsia_input_report::KeyboardDescriptor {
1675 input: Some(fidl_fuchsia_input_report::KeyboardInputDescriptor {
1676 keys3: Some(vec![
1677 fidl_fuchsia_input::Key::A,
1678 fidl_fuchsia_input::Key::B,
1679 fidl_fuchsia_input::Key::Enter,
1680 ]),
1681 ..Default::default()
1682 }),
1683 output: Some(fidl_fuchsia_input_report::KeyboardOutputDescriptor {
1684 leds: Some(vec![fidl_fuchsia_input_report::LedType::CapsLock]),
1685 ..Default::default()
1686 }),
1687 ..Default::default()
1688 }),
1689 ..Default::default()
1690 };
1691
1692 let info = InputDeviceInfo::new(
1693 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1694 "test_keyboard".to_string(),
1695 );
1696 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1697
1698 assert!(file.config.supported_event_types.get(uapi::EV_KEY));
1699 assert!(file.config.supported_event_types.get(uapi::EV_LED));
1700 assert!(!file.config.properties.get(uapi::INPUT_PROP_DIRECT));
1701
1702 assert!(file.config.supported_keys.get(uapi::KEY_A));
1703 assert!(file.config.supported_keys.get(uapi::KEY_B));
1704 assert!(file.config.supported_keys.get(uapi::KEY_ENTER));
1705 assert!(file.config.supported_leds.get(uapi::LED_CAPSL));
1706 }
1707
1708 #[::fuchsia::test]
1709 fn test_input_file_from_keyboard_descriptor_fallback_keys() {
1710 let inspector = fuchsia_inspect::Inspector::default();
1711 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1712 keyboard: Some(fidl_fuchsia_input_report::KeyboardDescriptor {
1713 input: Some(fidl_fuchsia_input_report::KeyboardInputDescriptor {
1714 keys3: None,
1715 ..Default::default()
1716 }),
1717 ..Default::default()
1718 }),
1719 ..Default::default()
1720 };
1721
1722 let info = InputDeviceInfo::new(
1723 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1724 "test_keyboard".to_string(),
1725 );
1726 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1727
1728 assert!(file.config.supported_event_types.get(uapi::EV_KEY));
1729 assert!(!file.config.properties.get(uapi::INPUT_PROP_DIRECT));
1730
1731 assert!(file.config.supported_keys.get(uapi::KEY_A));
1733 assert!(file.config.supported_keys.get(uapi::KEY_Z));
1734 assert!(file.config.supported_keys.get(uapi::KEY_ENTER));
1735 assert!(file.config.supported_keys.get(uapi::KEY_POWER));
1736 assert!(file.config.supported_keys.get(uapi::KEY_VOLUMEUP));
1737 assert!(file.config.supported_keys.get(uapi::KEY_VOLUMEDOWN));
1738 }
1739
1740 #[::fuchsia::test]
1741 fn test_input_file_from_keyboard_descriptor_unknown_led_does_not_set_ev_led() {
1742 let inspector = fuchsia_inspect::Inspector::default();
1743 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1744 keyboard: Some(fidl_fuchsia_input_report::KeyboardDescriptor {
1745 input: Some(fidl_fuchsia_input_report::KeyboardInputDescriptor {
1746 keys3: Some(vec![fidl_fuchsia_input::Key::A]),
1747 ..Default::default()
1748 }),
1749 output: Some(fidl_fuchsia_input_report::KeyboardOutputDescriptor {
1750 leds: Some(vec![
1751 fidl_fuchsia_input_report::LedType::from_primitive_allow_unknown(999),
1752 ]),
1753 ..Default::default()
1754 }),
1755 ..Default::default()
1756 }),
1757 ..Default::default()
1758 };
1759
1760 let info = InputDeviceInfo::new(
1761 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1762 "test_keyboard_unknown_led".to_string(),
1763 );
1764 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1765
1766 assert!(file.config.supported_event_types.get(uapi::EV_KEY));
1767 assert!(!file.config.supported_event_types.get(uapi::EV_LED));
1768 assert_eq!(file.config.supported_leds, BitSet::new());
1769 }
1770
1771 #[::fuchsia::test]
1772 fn test_input_file_from_mouse_descriptor() {
1773 let inspector = fuchsia_inspect::Inspector::default();
1774 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1775 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor {
1776 input: Some(fidl_fuchsia_input_report::MouseInputDescriptor {
1777 movement_x: Some(fidl_fuchsia_input_report::Axis {
1778 range: fidl_fuchsia_input_report::Range { min: -127, max: 127 },
1779 unit: fidl_fuchsia_input_report::Unit {
1780 type_: fidl_fuchsia_input_report::UnitType::None,
1781 exponent: 0,
1782 },
1783 }),
1784 movement_y: Some(fidl_fuchsia_input_report::Axis {
1785 range: fidl_fuchsia_input_report::Range { min: -127, max: 127 },
1786 unit: fidl_fuchsia_input_report::Unit {
1787 type_: fidl_fuchsia_input_report::UnitType::None,
1788 exponent: 0,
1789 },
1790 }),
1791 scroll_v: Some(fidl_fuchsia_input_report::Axis {
1792 range: fidl_fuchsia_input_report::Range { min: -1, max: 1 },
1793 unit: fidl_fuchsia_input_report::Unit {
1794 type_: fidl_fuchsia_input_report::UnitType::None,
1795 exponent: 0,
1796 },
1797 }),
1798 ..Default::default()
1799 }),
1800 ..Default::default()
1801 }),
1802 ..Default::default()
1803 };
1804
1805 let info = InputDeviceInfo::new(
1806 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1807 "test_mouse".to_string(),
1808 );
1809 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1810
1811 assert!(file.config.supported_event_types.get(uapi::EV_KEY));
1812 assert!(file.config.supported_event_types.get(uapi::EV_REL));
1813 assert!(file.config.properties.get(uapi::INPUT_PROP_POINTER));
1814
1815 assert!(file.config.supported_keys.get(uapi::BTN_LEFT));
1816 assert!(file.config.supported_keys.get(uapi::BTN_RIGHT));
1817 assert!(file.config.supported_motion_attributes.get(uapi::REL_X));
1818 assert!(file.config.supported_motion_attributes.get(uapi::REL_Y));
1819 assert!(file.config.supported_motion_attributes.get(uapi::REL_WHEEL));
1820 }
1821
1822 #[::fuchsia::test]
1823 fn test_input_file_from_mouse_descriptor_scroll_only() {
1824 let inspector = fuchsia_inspect::Inspector::default();
1825 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1826 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor {
1827 input: Some(fidl_fuchsia_input_report::MouseInputDescriptor {
1828 scroll_v: Some(fidl_fuchsia_input_report::Axis {
1829 range: fidl_fuchsia_input_report::Range { min: -1, max: 1 },
1830 unit: fidl_fuchsia_input_report::Unit {
1831 type_: fidl_fuchsia_input_report::UnitType::None,
1832 exponent: 0,
1833 },
1834 }),
1835 ..Default::default()
1836 }),
1837 ..Default::default()
1838 }),
1839 ..Default::default()
1840 };
1841
1842 let info = InputDeviceInfo::new(
1843 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1844 "test_mouse_scroll".to_string(),
1845 );
1846 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1847
1848 assert!(file.config.supported_event_types.get(uapi::EV_REL));
1849 assert!(file.config.supported_motion_attributes.get(uapi::REL_X));
1850 assert!(file.config.supported_motion_attributes.get(uapi::REL_Y));
1851 assert!(file.config.supported_motion_attributes.get(uapi::REL_WHEEL));
1852 }
1853
1854 #[::fuchsia::test]
1855 fn test_input_file_from_mouse_descriptor_with_buttons() {
1856 let inspector = fuchsia_inspect::Inspector::default();
1857 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1858 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor {
1859 input: Some(fidl_fuchsia_input_report::MouseInputDescriptor {
1860 buttons: Some(vec![1, 2]),
1861 movement_x: Some(fidl_fuchsia_input_report::Axis {
1862 range: fidl_fuchsia_input_report::Range { min: -10, max: 10 },
1863 unit: fidl_fuchsia_input_report::Unit {
1864 type_: fidl_fuchsia_input_report::UnitType::None,
1865 exponent: 0,
1866 },
1867 }),
1868 ..Default::default()
1869 }),
1870 ..Default::default()
1871 }),
1872 ..Default::default()
1873 };
1874
1875 let info = InputDeviceInfo::new(
1876 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1877 "test_mouse_buttons".to_string(),
1878 );
1879 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1880
1881 assert!(file.config.supported_keys.get(uapi::BTN_LEFT));
1882 assert!(file.config.supported_keys.get(uapi::BTN_RIGHT));
1883 assert!(!file.config.supported_keys.get(uapi::BTN_MIDDLE));
1884 }
1885
1886 #[::fuchsia::test]
1887 fn test_input_file_from_mouse_descriptor_fallback_rel() {
1888 let inspector = fuchsia_inspect::Inspector::default();
1889 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1890 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor {
1891 input: Some(fidl_fuchsia_input_report::MouseInputDescriptor {
1892 movement_x: None,
1893 movement_y: None,
1894 scroll_v: None,
1895 scroll_h: None,
1896 ..Default::default()
1897 }),
1898 ..Default::default()
1899 }),
1900 ..Default::default()
1901 };
1902
1903 let info = InputDeviceInfo::new(
1904 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1905 "test_mouse".to_string(),
1906 );
1907 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1908
1909 assert!(file.config.supported_event_types.get(uapi::EV_REL));
1911 assert!(file.config.supported_motion_attributes.get(uapi::REL_X));
1912 assert!(file.config.supported_motion_attributes.get(uapi::REL_Y));
1913 assert!(file.config.supported_motion_attributes.get(uapi::REL_WHEEL));
1914 assert!(file.config.supported_motion_attributes.get(uapi::REL_HWHEEL));
1915 }
1916
1917 #[::fuchsia::test]
1918 fn test_input_file_from_mouse_descriptor_absolute_coordinates() {
1919 let inspector = fuchsia_inspect::Inspector::default();
1920 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1921 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor {
1922 input: Some(fidl_fuchsia_input_report::MouseInputDescriptor {
1923 position_x: Some(fidl_fuchsia_input_report::Axis {
1924 range: fidl_fuchsia_input_report::Range { min: 0, max: 1920 },
1925 unit: fidl_fuchsia_input_report::Unit {
1926 type_: fidl_fuchsia_input_report::UnitType::None,
1927 exponent: 0,
1928 },
1929 }),
1930 position_y: Some(fidl_fuchsia_input_report::Axis {
1931 range: fidl_fuchsia_input_report::Range { min: 0, max: 1080 },
1932 unit: fidl_fuchsia_input_report::Unit {
1933 type_: fidl_fuchsia_input_report::UnitType::None,
1934 exponent: 0,
1935 },
1936 }),
1937 ..Default::default()
1938 }),
1939 ..Default::default()
1940 }),
1941 ..Default::default()
1942 };
1943
1944 let info = InputDeviceInfo::new(
1945 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1946 "test_abs_mouse".to_string(),
1947 );
1948 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
1949
1950 assert!(!file.config.supported_event_types.get(uapi::EV_ABS));
1953 assert!(file.config.supported_event_types.get(uapi::EV_REL));
1954 assert!(!file.config.supported_position_attributes.get(uapi::ABS_X));
1955 assert!(!file.config.supported_position_attributes.get(uapi::ABS_Y));
1956 assert!(!file.config.supported_keys.get(uapi::BTN_TOUCH));
1957 assert!(file.config.supported_motion_attributes.get(uapi::REL_X));
1958 assert!(file.config.supported_motion_attributes.get(uapi::REL_Y));
1959 }
1960
1961 #[::fuchsia::test]
1962 fn test_input_file_from_composite_touch_and_mouse() {
1963 let inspector = fuchsia_inspect::Inspector::default();
1964 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
1965 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
1966 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
1967 contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
1968 position_x: Some(fidl_fuchsia_input_report::Axis {
1969 range: fidl_fuchsia_input_report::Range { min: 0, max: 1080 },
1970 unit: fidl_fuchsia_input_report::Unit {
1971 type_: fidl_fuchsia_input_report::UnitType::None,
1972 exponent: 0,
1973 },
1974 }),
1975 position_y: Some(fidl_fuchsia_input_report::Axis {
1976 range: fidl_fuchsia_input_report::Range { min: 0, max: 2400 },
1977 unit: fidl_fuchsia_input_report::Unit {
1978 type_: fidl_fuchsia_input_report::UnitType::None,
1979 exponent: 0,
1980 },
1981 }),
1982 ..Default::default()
1983 }]),
1984 ..Default::default()
1985 }),
1986 ..Default::default()
1987 }),
1988 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor {
1989 input: Some(fidl_fuchsia_input_report::MouseInputDescriptor {
1990 position_x: Some(fidl_fuchsia_input_report::Axis {
1991 range: fidl_fuchsia_input_report::Range { min: 0, max: 500 },
1992 unit: fidl_fuchsia_input_report::Unit {
1993 type_: fidl_fuchsia_input_report::UnitType::None,
1994 exponent: 0,
1995 },
1996 }),
1997 movement_x: Some(fidl_fuchsia_input_report::Axis {
1998 range: fidl_fuchsia_input_report::Range { min: -10, max: 10 },
1999 unit: fidl_fuchsia_input_report::Unit {
2000 type_: fidl_fuchsia_input_report::UnitType::None,
2001 exponent: 0,
2002 },
2003 }),
2004 ..Default::default()
2005 }),
2006 ..Default::default()
2007 }),
2008 ..Default::default()
2009 };
2010
2011 let info = InputDeviceInfo::new(
2012 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
2013 "composite".to_string(),
2014 );
2015 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
2016
2017 assert!(file.config.properties.get(uapi::INPUT_PROP_DIRECT));
2019 assert!(!file.config.properties.get(uapi::INPUT_PROP_POINTER));
2020
2021 assert_eq!(file.config.x_axis_info.maximum, 1080);
2023 assert!(file.config.supported_event_types.get(uapi::EV_REL));
2024 assert!(file.config.supported_event_types.get(uapi::EV_ABS));
2025 }
2026
2027 #[::fuchsia::test]
2028 fn test_input_file_from_composite_touch_keyboard_descriptor() {
2029 let inspector = fuchsia_inspect::Inspector::default();
2030 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
2031 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
2032 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
2033 touch_type: Some(fidl_fuchsia_input_report::TouchType::Touchpad),
2034 ..Default::default()
2035 }),
2036 ..Default::default()
2037 }),
2038 keyboard: Some(fidl_fuchsia_input_report::KeyboardDescriptor {
2039 input: Some(fidl_fuchsia_input_report::KeyboardInputDescriptor {
2040 keys3: None,
2041 ..Default::default()
2042 }),
2043 ..Default::default()
2044 }),
2045 ..Default::default()
2046 };
2047
2048 let info = InputDeviceInfo::new(
2049 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
2050 "composite_touch_keyboard".to_string(),
2051 );
2052 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
2053
2054 assert!(file.config.supported_keys.get(uapi::BTN_TOUCH));
2056 assert!(file.config.supported_keys.get(uapi::BTN_TOOL_FINGER));
2057
2058 assert!(file.config.supported_keys.get(uapi::KEY_A));
2060 assert!(file.config.supported_keys.get(uapi::KEY_Z));
2061 assert!(file.config.supported_keys.get(uapi::KEY_ENTER));
2062 }
2063
2064 #[::fuchsia::test]
2065 fn test_input_file_from_consumer_control_descriptor() {
2066 let inspector = fuchsia_inspect::Inspector::default();
2067 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
2068 consumer_control: Some(fidl_fuchsia_input_report::ConsumerControlDescriptor {
2069 input: Some(fidl_fuchsia_input_report::ConsumerControlInputDescriptor {
2070 buttons: Some(vec![
2071 fidl_fuchsia_input_report::ConsumerControlButton::VolumeUp,
2072 fidl_fuchsia_input_report::ConsumerControlButton::VolumeDown,
2073 fidl_fuchsia_input_report::ConsumerControlButton::Power,
2074 fidl_fuchsia_input_report::ConsumerControlButton::Pause,
2075 fidl_fuchsia_input_report::ConsumerControlButton::MicMute,
2076 ]),
2077 ..Default::default()
2078 }),
2079 ..Default::default()
2080 }),
2081 ..Default::default()
2082 };
2083
2084 let info = InputDeviceInfo::new(
2085 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
2086 "test_buttons".to_string(),
2087 );
2088 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
2089
2090 assert!(file.config.supported_event_types.get(uapi::EV_KEY));
2091 assert!(file.config.supported_keys.get(uapi::KEY_VOLUMEUP));
2092 assert!(file.config.supported_keys.get(uapi::KEY_VOLUMEDOWN));
2093 assert!(file.config.supported_keys.get(uapi::KEY_POWER));
2094 assert!(file.config.supported_keys.get(uapi::KEY_PAUSE));
2095 assert!(file.config.supported_keys.get(uapi::KEY_MICMUTE));
2096 }
2097
2098 #[::fuchsia::test]
2099 fn test_input_file_from_consumer_control_descriptor_fallback() {
2100 let inspector = fuchsia_inspect::Inspector::default();
2101 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
2102 consumer_control: Some(fidl_fuchsia_input_report::ConsumerControlDescriptor {
2103 input: Some(fidl_fuchsia_input_report::ConsumerControlInputDescriptor {
2104 buttons: None,
2105 ..Default::default()
2106 }),
2107 ..Default::default()
2108 }),
2109 ..Default::default()
2110 };
2111
2112 let info = InputDeviceInfo::new(
2113 uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
2114 "test_buttons_fallback".to_string(),
2115 );
2116 let file = InputFile::from_device_descriptor(info, &descriptor, inspector.root());
2117
2118 assert!(file.config.supported_event_types.get(uapi::EV_KEY));
2119 assert!(file.config.supported_keys.get(uapi::KEY_POWER));
2120 assert!(file.config.supported_keys.get(uapi::KEY_VOLUMEUP));
2121 assert!(file.config.supported_keys.get(uapi::KEY_VOLUMEDOWN));
2122 assert!(!file.config.supported_keys.get(uapi::KEY_PAUSE));
2123 }
2124}