Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3# -*- coding: utf-8 -*-
4#
5# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
6# Copyright (c) 2017 Red Hat, Inc.
7#
8
9from . import base
10import hidtools.hid
11from hidtools.util import BusType
12import libevdev
13import logging
14import pytest
15
16logger = logging.getLogger("hidtools.test.mouse")
17
18# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6
19try:
20 libevdev.EV_REL.REL_WHEEL_HI_RES
21except AttributeError:
22 libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B
23 libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C
24
25
26class InvalidHIDCommunication(Exception):
27 pass
28
29
30class MouseData(object):
31 pass
32
33
34class BaseMouse(base.UHIDTestDevice):
35 def __init__(self, rdesc, name=None, input_info=None):
36 assert rdesc is not None
37 super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)
38 self.left = False
39 self.right = False
40 self.middle = False
41
42 def create_report(self, x, y, buttons=None, wheels=None, reportID=None):
43 """
44 Return an input report for this device.
45
46 :param x: relative x
47 :param y: relative y
48 :param buttons: a (l, r, m) tuple of bools for the button states,
49 where ``None`` is "leave unchanged"
50 :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
51 the two wheels
52 :param reportID: the numeric report ID for this report, if needed
53 """
54 if buttons is not None:
55 left, right, middle = buttons
56 if left is not None:
57 self.left = left
58 if right is not None:
59 self.right = right
60 if middle is not None:
61 self.middle = middle
62 left = self.left
63 right = self.right
64 middle = self.middle
65 # Note: the BaseMouse doesn't actually have a wheel but the
66 # create_report magic only fills in those fields exist, so let's
67 # make this generic here.
68 wheel, acpan = 0, 0
69 if wheels is not None:
70 if isinstance(wheels, tuple):
71 wheel = wheels[0]
72 acpan = wheels[1]
73 else:
74 wheel = wheels
75
76 reportID = reportID or self.default_reportID
77
78 mouse = MouseData()
79 mouse.b1 = int(left)
80 mouse.b2 = int(right)
81 mouse.b3 = int(middle)
82 mouse.x = x
83 mouse.y = y
84 mouse.wheel = wheel
85 mouse.acpan = acpan
86 return super().create_report(mouse, reportID=reportID)
87
88 def event(self, x, y, buttons=None, wheels=None):
89 """
90 Send an input event on the default report ID.
91
92 :param x: relative x
93 :param y: relative y
94 :param buttons: a (l, r, m) tuple of bools for the button states,
95 where ``None`` is "leave unchanged"
96 :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
97 the two wheels
98 """
99 r = self.create_report(x, y, buttons, wheels)
100 self.call_input_event(r)
101 return [r]
102
103
104class ButtonMouse(BaseMouse):
105 # fmt: off
106 report_descriptor = [
107 0x05, 0x01, # .Usage Page (Generic Desktop) 0
108 0x09, 0x02, # .Usage (Mouse) 2
109 0xa1, 0x01, # .Collection (Application) 4
110 0x09, 0x02, # ..Usage (Mouse) 6
111 0xa1, 0x02, # ..Collection (Logical) 8
112 0x09, 0x01, # ...Usage (Pointer) 10
113 0xa1, 0x00, # ...Collection (Physical) 12
114 0x05, 0x09, # ....Usage Page (Button) 14
115 0x19, 0x01, # ....Usage Minimum (1) 16
116 0x29, 0x03, # ....Usage Maximum (3) 18
117 0x15, 0x00, # ....Logical Minimum (0) 20
118 0x25, 0x01, # ....Logical Maximum (1) 22
119 0x75, 0x01, # ....Report Size (1) 24
120 0x95, 0x03, # ....Report Count (3) 26
121 0x81, 0x02, # ....Input (Data,Var,Abs) 28
122 0x75, 0x05, # ....Report Size (5) 30
123 0x95, 0x01, # ....Report Count (1) 32
124 0x81, 0x03, # ....Input (Cnst,Var,Abs) 34
125 0x05, 0x01, # ....Usage Page (Generic Desktop) 36
126 0x09, 0x30, # ....Usage (X) 38
127 0x09, 0x31, # ....Usage (Y) 40
128 0x15, 0x81, # ....Logical Minimum (-127) 42
129 0x25, 0x7f, # ....Logical Maximum (127) 44
130 0x75, 0x08, # ....Report Size (8) 46
131 0x95, 0x02, # ....Report Count (2) 48
132 0x81, 0x06, # ....Input (Data,Var,Rel) 50
133 0xc0, # ...End Collection 52
134 0xc0, # ..End Collection 53
135 0xc0, # .End Collection 54
136 ]
137 # fmt: on
138
139 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
140 super().__init__(rdesc, name, input_info)
141
142 def fake_report(self, x, y, buttons):
143 if buttons is not None:
144 left, right, middle = buttons
145 if left is None:
146 left = self.left
147 if right is None:
148 right = self.right
149 if middle is None:
150 middle = self.middle
151 else:
152 left = self.left
153 right = self.right
154 middle = self.middle
155
156 button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)
157 x = max(-127, min(127, x))
158 y = max(-127, min(127, y))
159 x = hidtools.util.to_twos_comp(x, 8)
160 y = hidtools.util.to_twos_comp(y, 8)
161 return [button_mask, x, y]
162
163
164class WheelMouse(ButtonMouse):
165 # fmt: off
166 report_descriptor = [
167 0x05, 0x01, # Usage Page (Generic Desktop) 0
168 0x09, 0x02, # Usage (Mouse) 2
169 0xa1, 0x01, # Collection (Application) 4
170 0x05, 0x09, # .Usage Page (Button) 6
171 0x19, 0x01, # .Usage Minimum (1) 8
172 0x29, 0x03, # .Usage Maximum (3) 10
173 0x15, 0x00, # .Logical Minimum (0) 12
174 0x25, 0x01, # .Logical Maximum (1) 14
175 0x95, 0x03, # .Report Count (3) 16
176 0x75, 0x01, # .Report Size (1) 18
177 0x81, 0x02, # .Input (Data,Var,Abs) 20
178 0x95, 0x01, # .Report Count (1) 22
179 0x75, 0x05, # .Report Size (5) 24
180 0x81, 0x03, # .Input (Cnst,Var,Abs) 26
181 0x05, 0x01, # .Usage Page (Generic Desktop) 28
182 0x09, 0x01, # .Usage (Pointer) 30
183 0xa1, 0x00, # .Collection (Physical) 32
184 0x09, 0x30, # ..Usage (X) 34
185 0x09, 0x31, # ..Usage (Y) 36
186 0x15, 0x81, # ..Logical Minimum (-127) 38
187 0x25, 0x7f, # ..Logical Maximum (127) 40
188 0x75, 0x08, # ..Report Size (8) 42
189 0x95, 0x02, # ..Report Count (2) 44
190 0x81, 0x06, # ..Input (Data,Var,Rel) 46
191 0xc0, # .End Collection 48
192 0x09, 0x38, # .Usage (Wheel) 49
193 0x15, 0x81, # .Logical Minimum (-127) 51
194 0x25, 0x7f, # .Logical Maximum (127) 53
195 0x75, 0x08, # .Report Size (8) 55
196 0x95, 0x01, # .Report Count (1) 57
197 0x81, 0x06, # .Input (Data,Var,Rel) 59
198 0xc0, # End Collection 61
199 ]
200 # fmt: on
201
202 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
203 super().__init__(rdesc, name, input_info)
204 self.wheel_multiplier = 1
205
206
207class TwoWheelMouse(WheelMouse):
208 # fmt: off
209 report_descriptor = [
210 0x05, 0x01, # Usage Page (Generic Desktop) 0
211 0x09, 0x02, # Usage (Mouse) 2
212 0xa1, 0x01, # Collection (Application) 4
213 0x09, 0x01, # .Usage (Pointer) 6
214 0xa1, 0x00, # .Collection (Physical) 8
215 0x05, 0x09, # ..Usage Page (Button) 10
216 0x19, 0x01, # ..Usage Minimum (1) 12
217 0x29, 0x10, # ..Usage Maximum (16) 14
218 0x15, 0x00, # ..Logical Minimum (0) 16
219 0x25, 0x01, # ..Logical Maximum (1) 18
220 0x95, 0x10, # ..Report Count (16) 20
221 0x75, 0x01, # ..Report Size (1) 22
222 0x81, 0x02, # ..Input (Data,Var,Abs) 24
223 0x05, 0x01, # ..Usage Page (Generic Desktop) 26
224 0x16, 0x01, 0x80, # ..Logical Minimum (-32767) 28
225 0x26, 0xff, 0x7f, # ..Logical Maximum (32767) 31
226 0x75, 0x10, # ..Report Size (16) 34
227 0x95, 0x02, # ..Report Count (2) 36
228 0x09, 0x30, # ..Usage (X) 38
229 0x09, 0x31, # ..Usage (Y) 40
230 0x81, 0x06, # ..Input (Data,Var,Rel) 42
231 0x15, 0x81, # ..Logical Minimum (-127) 44
232 0x25, 0x7f, # ..Logical Maximum (127) 46
233 0x75, 0x08, # ..Report Size (8) 48
234 0x95, 0x01, # ..Report Count (1) 50
235 0x09, 0x38, # ..Usage (Wheel) 52
236 0x81, 0x06, # ..Input (Data,Var,Rel) 54
237 0x05, 0x0c, # ..Usage Page (Consumer Devices) 56
238 0x0a, 0x38, 0x02, # ..Usage (AC Pan) 58
239 0x95, 0x01, # ..Report Count (1) 61
240 0x81, 0x06, # ..Input (Data,Var,Rel) 63
241 0xc0, # .End Collection 65
242 0xc0, # End Collection 66
243 ]
244 # fmt: on
245
246 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
247 super().__init__(rdesc, name, input_info)
248 self.hwheel_multiplier = 1
249
250
251class MIDongleMIWirelessMouse(TwoWheelMouse):
252 # fmt: off
253 report_descriptor = [
254 0x05, 0x01, # Usage Page (Generic Desktop)
255 0x09, 0x02, # Usage (Mouse)
256 0xa1, 0x01, # Collection (Application)
257 0x85, 0x01, # .Report ID (1)
258 0x09, 0x01, # .Usage (Pointer)
259 0xa1, 0x00, # .Collection (Physical)
260 0x95, 0x05, # ..Report Count (5)
261 0x75, 0x01, # ..Report Size (1)
262 0x05, 0x09, # ..Usage Page (Button)
263 0x19, 0x01, # ..Usage Minimum (1)
264 0x29, 0x05, # ..Usage Maximum (5)
265 0x15, 0x00, # ..Logical Minimum (0)
266 0x25, 0x01, # ..Logical Maximum (1)
267 0x81, 0x02, # ..Input (Data,Var,Abs)
268 0x95, 0x01, # ..Report Count (1)
269 0x75, 0x03, # ..Report Size (3)
270 0x81, 0x01, # ..Input (Cnst,Arr,Abs)
271 0x75, 0x08, # ..Report Size (8)
272 0x95, 0x01, # ..Report Count (1)
273 0x05, 0x01, # ..Usage Page (Generic Desktop)
274 0x09, 0x38, # ..Usage (Wheel)
275 0x15, 0x81, # ..Logical Minimum (-127)
276 0x25, 0x7f, # ..Logical Maximum (127)
277 0x81, 0x06, # ..Input (Data,Var,Rel)
278 0x05, 0x0c, # ..Usage Page (Consumer Devices)
279 0x0a, 0x38, 0x02, # ..Usage (AC Pan)
280 0x95, 0x01, # ..Report Count (1)
281 0x81, 0x06, # ..Input (Data,Var,Rel)
282 0xc0, # .End Collection
283 0x85, 0x02, # .Report ID (2)
284 0x09, 0x01, # .Usage (Consumer Control)
285 0xa1, 0x00, # .Collection (Physical)
286 0x75, 0x0c, # ..Report Size (12)
287 0x95, 0x02, # ..Report Count (2)
288 0x05, 0x01, # ..Usage Page (Generic Desktop)
289 0x09, 0x30, # ..Usage (X)
290 0x09, 0x31, # ..Usage (Y)
291 0x16, 0x01, 0xf8, # ..Logical Minimum (-2047)
292 0x26, 0xff, 0x07, # ..Logical Maximum (2047)
293 0x81, 0x06, # ..Input (Data,Var,Rel)
294 0xc0, # .End Collection
295 0xc0, # End Collection
296 0x05, 0x0c, # Usage Page (Consumer Devices)
297 0x09, 0x01, # Usage (Consumer Control)
298 0xa1, 0x01, # Collection (Application)
299 0x85, 0x03, # .Report ID (3)
300 0x15, 0x00, # .Logical Minimum (0)
301 0x25, 0x01, # .Logical Maximum (1)
302 0x75, 0x01, # .Report Size (1)
303 0x95, 0x01, # .Report Count (1)
304 0x09, 0xcd, # .Usage (Play/Pause)
305 0x81, 0x06, # .Input (Data,Var,Rel)
306 0x0a, 0x83, 0x01, # .Usage (AL Consumer Control Config)
307 0x81, 0x06, # .Input (Data,Var,Rel)
308 0x09, 0xb5, # .Usage (Scan Next Track)
309 0x81, 0x06, # .Input (Data,Var,Rel)
310 0x09, 0xb6, # .Usage (Scan Previous Track)
311 0x81, 0x06, # .Input (Data,Var,Rel)
312 0x09, 0xea, # .Usage (Volume Down)
313 0x81, 0x06, # .Input (Data,Var,Rel)
314 0x09, 0xe9, # .Usage (Volume Up)
315 0x81, 0x06, # .Input (Data,Var,Rel)
316 0x0a, 0x25, 0x02, # .Usage (AC Forward)
317 0x81, 0x06, # .Input (Data,Var,Rel)
318 0x0a, 0x24, 0x02, # .Usage (AC Back)
319 0x81, 0x06, # .Input (Data,Var,Rel)
320 0xc0, # End Collection
321 ]
322 # fmt: on
323 device_input_info = (BusType.USB, 0x2717, 0x003B)
324 device_name = "uhid test MI Dongle MI Wireless Mouse"
325
326 def __init__(
327 self, rdesc=report_descriptor, name=device_name, input_info=device_input_info
328 ):
329 super().__init__(rdesc, name, input_info)
330
331 def event(self, x, y, buttons=None, wheels=None):
332 # this mouse spreads the relative pointer and the mouse buttons
333 # onto 2 distinct reports
334 rs = []
335 r = self.create_report(x, y, buttons, wheels, reportID=1)
336 self.call_input_event(r)
337 rs.append(r)
338 r = self.create_report(x, y, buttons, reportID=2)
339 self.call_input_event(r)
340 rs.append(r)
341 return rs
342
343
344class ResolutionMultiplierMouse(TwoWheelMouse):
345 # fmt: off
346 report_descriptor = [
347 0x05, 0x01, # Usage Page (Generic Desktop) 83
348 0x09, 0x02, # Usage (Mouse) 85
349 0xa1, 0x01, # Collection (Application) 87
350 0x05, 0x01, # .Usage Page (Generic Desktop) 89
351 0x09, 0x02, # .Usage (Mouse) 91
352 0xa1, 0x02, # .Collection (Logical) 93
353 0x85, 0x11, # ..Report ID (17) 95
354 0x09, 0x01, # ..Usage (Pointer) 97
355 0xa1, 0x00, # ..Collection (Physical) 99
356 0x05, 0x09, # ...Usage Page (Button) 101
357 0x19, 0x01, # ...Usage Minimum (1) 103
358 0x29, 0x03, # ...Usage Maximum (3) 105
359 0x95, 0x03, # ...Report Count (3) 107
360 0x75, 0x01, # ...Report Size (1) 109
361 0x25, 0x01, # ...Logical Maximum (1) 111
362 0x81, 0x02, # ...Input (Data,Var,Abs) 113
363 0x95, 0x01, # ...Report Count (1) 115
364 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 117
365 0x09, 0x05, # ...Usage (Vendor Usage 0x05) 119
366 0x81, 0x02, # ...Input (Data,Var,Abs) 121
367 0x95, 0x03, # ...Report Count (3) 123
368 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 125
369 0x05, 0x01, # ...Usage Page (Generic Desktop) 127
370 0x09, 0x30, # ...Usage (X) 129
371 0x09, 0x31, # ...Usage (Y) 131
372 0x95, 0x02, # ...Report Count (2) 133
373 0x75, 0x08, # ...Report Size (8) 135
374 0x15, 0x81, # ...Logical Minimum (-127) 137
375 0x25, 0x7f, # ...Logical Maximum (127) 139
376 0x81, 0x06, # ...Input (Data,Var,Rel) 141
377 0xa1, 0x02, # ...Collection (Logical) 143
378 0x85, 0x12, # ....Report ID (18) 145
379 0x09, 0x48, # ....Usage (Resolution Multiplier) 147
380 0x95, 0x01, # ....Report Count (1) 149
381 0x75, 0x02, # ....Report Size (2) 151
382 0x15, 0x00, # ....Logical Minimum (0) 153
383 0x25, 0x01, # ....Logical Maximum (1) 155
384 0x35, 0x01, # ....Physical Minimum (1) 157
385 0x45, 0x04, # ....Physical Maximum (4) 159
386 0xb1, 0x02, # ....Feature (Data,Var,Abs) 161
387 0x35, 0x00, # ....Physical Minimum (0) 163
388 0x45, 0x00, # ....Physical Maximum (0) 165
389 0x75, 0x06, # ....Report Size (6) 167
390 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 169
391 0x85, 0x11, # ....Report ID (17) 171
392 0x09, 0x38, # ....Usage (Wheel) 173
393 0x15, 0x81, # ....Logical Minimum (-127) 175
394 0x25, 0x7f, # ....Logical Maximum (127) 177
395 0x75, 0x08, # ....Report Size (8) 179
396 0x81, 0x06, # ....Input (Data,Var,Rel) 181
397 0xc0, # ...End Collection 183
398 0x05, 0x0c, # ...Usage Page (Consumer Devices) 184
399 0x75, 0x08, # ...Report Size (8) 186
400 0x0a, 0x38, 0x02, # ...Usage (AC Pan) 188
401 0x81, 0x06, # ...Input (Data,Var,Rel) 191
402 0xc0, # ..End Collection 193
403 0xc0, # .End Collection 194
404 0xc0, # End Collection 195
405 ]
406 # fmt: on
407
408 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
409 super().__init__(rdesc, name, input_info)
410 self.default_reportID = 0x11
411
412 # Feature Report 12, multiplier Feature value must be set to 0b01,
413 # i.e. 1. We should extract that from the descriptor instead
414 # of hardcoding it here, but meanwhile this will do.
415 self.set_feature_report = [0x12, 0x1]
416
417 def set_report(self, req, rnum, rtype, data):
418 if rtype != self.UHID_FEATURE_REPORT:
419 raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
420 if rnum != 0x12:
421 raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
422
423 if data != self.set_feature_report:
424 raise InvalidHIDCommunication(
425 f"Unexpected data: {data}, expected {self.set_feature_report}"
426 )
427
428 self.wheel_multiplier = 4
429
430 return 0
431
432
433class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):
434 def set_report(self, req, rnum, rtype, data):
435 super().set_report(req, rnum, rtype, data)
436
437 self.wheel_multiplier = 1
438 self.hwheel_multiplier = 1
439 return 32 # EPIPE
440
441
442class BadReportDescriptorMouse(BaseMouse):
443 """
444 This "device" was one autogenerated by syzbot. There are a lot of issues in
445 it, and the most problematic is that it declares features that have no
446 size.
447
448 This leads to report->size being set to 0 and can mess up with usbhid
449 internals. Fortunately, uhid merely passes the incoming buffer, without
450 touching it so a buffer of size 0 will be translated to [] without
451 triggering a kernel oops.
452
453 Because the report descriptor is wrong, no input are created, and we need
454 to tweak a little bit the parameters to make it look correct.
455 """
456
457 # fmt: off
458 report_descriptor = [
459 0x96, 0x01, 0x00, # Report Count (1) 0
460 0x06, 0x01, 0x00, # Usage Page (Generic Desktop) 3
461 # 0x03, 0x00, 0x00, 0x00, 0x00, # Ignored by the kernel somehow
462 0x2a, 0x90, 0xa0, # Usage Maximum (41104) 6
463 0x27, 0x00, 0x00, 0x00, 0x00, # Logical Maximum (0) 9
464 0xb3, 0x81, 0x3e, 0x25, 0x03, # Feature (Cnst,Arr,Abs,Vol) 14
465 0x1b, 0xdd, 0xe8, 0x40, 0x50, # Usage Minimum (1346431197) 19
466 0x3b, 0x5d, 0x8c, 0x3d, 0xda, # Designator Index 24
467 ]
468 # fmt: on
469
470 def __init__(
471 self, rdesc=report_descriptor, name=None, input_info=(3, 0x045E, 0x07DA)
472 ):
473 super().__init__(rdesc, name, input_info)
474 self.high_resolution_report_called = False
475
476 def get_evdev(self, application=None):
477 assert self._input_nodes is None
478 return (
479 "Ok" # should be a list or None, but both would fail, so abusing the system
480 )
481
482 def next_sync_events(self, application=None):
483 # there are no evdev nodes, so no events
484 return []
485
486 def is_ready(self):
487 # we wait for the SET_REPORT command to come
488 return self.high_resolution_report_called
489
490 def set_report(self, req, rnum, rtype, data):
491 if rtype != self.UHID_FEATURE_REPORT:
492 raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
493 if rnum != 0x0:
494 raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
495
496 if len(data) != 1:
497 raise InvalidHIDCommunication(f"Unexpected data: {data}, expected '[0]'")
498
499 self.high_resolution_report_called = True
500
501 return 0
502
503
504class ResolutionMultiplierHWheelMouse(TwoWheelMouse):
505 # fmt: off
506 report_descriptor = [
507 0x05, 0x01, # Usage Page (Generic Desktop) 0
508 0x09, 0x02, # Usage (Mouse) 2
509 0xa1, 0x01, # Collection (Application) 4
510 0x05, 0x01, # .Usage Page (Generic Desktop) 6
511 0x09, 0x02, # .Usage (Mouse) 8
512 0xa1, 0x02, # .Collection (Logical) 10
513 0x85, 0x1a, # ..Report ID (26) 12
514 0x09, 0x01, # ..Usage (Pointer) 14
515 0xa1, 0x00, # ..Collection (Physical) 16
516 0x05, 0x09, # ...Usage Page (Button) 18
517 0x19, 0x01, # ...Usage Minimum (1) 20
518 0x29, 0x05, # ...Usage Maximum (5) 22
519 0x95, 0x05, # ...Report Count (5) 24
520 0x75, 0x01, # ...Report Size (1) 26
521 0x15, 0x00, # ...Logical Minimum (0) 28
522 0x25, 0x01, # ...Logical Maximum (1) 30
523 0x81, 0x02, # ...Input (Data,Var,Abs) 32
524 0x75, 0x03, # ...Report Size (3) 34
525 0x95, 0x01, # ...Report Count (1) 36
526 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38
527 0x05, 0x01, # ...Usage Page (Generic Desktop) 40
528 0x09, 0x30, # ...Usage (X) 42
529 0x09, 0x31, # ...Usage (Y) 44
530 0x95, 0x02, # ...Report Count (2) 46
531 0x75, 0x10, # ...Report Size (16) 48
532 0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50
533 0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53
534 0x81, 0x06, # ...Input (Data,Var,Rel) 56
535 0xa1, 0x02, # ...Collection (Logical) 58
536 0x85, 0x12, # ....Report ID (18) 60
537 0x09, 0x48, # ....Usage (Resolution Multiplier) 62
538 0x95, 0x01, # ....Report Count (1) 64
539 0x75, 0x02, # ....Report Size (2) 66
540 0x15, 0x00, # ....Logical Minimum (0) 68
541 0x25, 0x01, # ....Logical Maximum (1) 70
542 0x35, 0x01, # ....Physical Minimum (1) 72
543 0x45, 0x0c, # ....Physical Maximum (12) 74
544 0xb1, 0x02, # ....Feature (Data,Var,Abs) 76
545 0x85, 0x1a, # ....Report ID (26) 78
546 0x09, 0x38, # ....Usage (Wheel) 80
547 0x35, 0x00, # ....Physical Minimum (0) 82
548 0x45, 0x00, # ....Physical Maximum (0) 84
549 0x95, 0x01, # ....Report Count (1) 86
550 0x75, 0x10, # ....Report Size (16) 88
551 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90
552 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93
553 0x81, 0x06, # ....Input (Data,Var,Rel) 96
554 0xc0, # ...End Collection 98
555 0xa1, 0x02, # ...Collection (Logical) 99
556 0x85, 0x12, # ....Report ID (18) 101
557 0x09, 0x48, # ....Usage (Resolution Multiplier) 103
558 0x75, 0x02, # ....Report Size (2) 105
559 0x15, 0x00, # ....Logical Minimum (0) 107
560 0x25, 0x01, # ....Logical Maximum (1) 109
561 0x35, 0x01, # ....Physical Minimum (1) 111
562 0x45, 0x0c, # ....Physical Maximum (12) 113
563 0xb1, 0x02, # ....Feature (Data,Var,Abs) 115
564 0x35, 0x00, # ....Physical Minimum (0) 117
565 0x45, 0x00, # ....Physical Maximum (0) 119
566 0x75, 0x04, # ....Report Size (4) 121
567 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123
568 0x85, 0x1a, # ....Report ID (26) 125
569 0x05, 0x0c, # ....Usage Page (Consumer Devices) 127
570 0x95, 0x01, # ....Report Count (1) 129
571 0x75, 0x10, # ....Report Size (16) 131
572 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133
573 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136
574 0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139
575 0x81, 0x06, # ....Input (Data,Var,Rel) 142
576 0xc0, # ...End Collection 144
577 0xc0, # ..End Collection 145
578 0xc0, # .End Collection 146
579 0xc0, # End Collection 147
580 ]
581 # fmt: on
582
583 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
584 super().__init__(rdesc, name, input_info)
585 self.default_reportID = 0x1A
586
587 # Feature Report 12, multiplier Feature value must be set to 0b0101,
588 # i.e. 5. We should extract that from the descriptor instead
589 # of hardcoding it here, but meanwhile this will do.
590 self.set_feature_report = [0x12, 0x5]
591
592 def set_report(self, req, rnum, rtype, data):
593 super().set_report(req, rnum, rtype, data)
594
595 self.wheel_multiplier = 12
596 self.hwheel_multiplier = 12
597
598 return 0
599
600
601class BaseTest:
602 class TestMouse(base.BaseTestCase.TestUhid):
603 def test_buttons(self):
604 """check for button reliability."""
605 uhdev = self.uhdev
606 evdev = uhdev.get_evdev()
607 syn_event = self.syn_event
608
609 r = uhdev.event(0, 0, (None, True, None))
610 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
611 events = uhdev.next_sync_events()
612 self.debug_reports(r, uhdev, events)
613 self.assertInputEventsIn((syn_event, expected_event), events)
614 assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
615
616 r = uhdev.event(0, 0, (None, False, None))
617 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
618 events = uhdev.next_sync_events()
619 self.debug_reports(r, uhdev, events)
620 self.assertInputEventsIn((syn_event, expected_event), events)
621 assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
622
623 r = uhdev.event(0, 0, (None, None, True))
624 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1)
625 events = uhdev.next_sync_events()
626 self.debug_reports(r, uhdev, events)
627 self.assertInputEventsIn((syn_event, expected_event), events)
628 assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1
629
630 r = uhdev.event(0, 0, (None, None, False))
631 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0)
632 events = uhdev.next_sync_events()
633 self.debug_reports(r, uhdev, events)
634 self.assertInputEventsIn((syn_event, expected_event), events)
635 assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0
636
637 r = uhdev.event(0, 0, (True, None, None))
638 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
639 events = uhdev.next_sync_events()
640 self.debug_reports(r, uhdev, events)
641 self.assertInputEventsIn((syn_event, expected_event), events)
642 assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
643
644 r = uhdev.event(0, 0, (False, None, None))
645 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
646 events = uhdev.next_sync_events()
647 self.debug_reports(r, uhdev, events)
648 self.assertInputEventsIn((syn_event, expected_event), events)
649 assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
650
651 r = uhdev.event(0, 0, (True, True, None))
652 expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
653 expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
654 events = uhdev.next_sync_events()
655 self.debug_reports(r, uhdev, events)
656 self.assertInputEventsIn(
657 (syn_event, expected_event0, expected_event1), events
658 )
659 assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
660 assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
661
662 r = uhdev.event(0, 0, (False, None, None))
663 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
664 events = uhdev.next_sync_events()
665 self.debug_reports(r, uhdev, events)
666 self.assertInputEventsIn((syn_event, expected_event), events)
667 assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
668 assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
669
670 r = uhdev.event(0, 0, (None, False, None))
671 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
672 events = uhdev.next_sync_events()
673 self.debug_reports(r, uhdev, events)
674 self.assertInputEventsIn((syn_event, expected_event), events)
675 assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
676 assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
677
678 def test_relative(self):
679 """Check for relative events."""
680 uhdev = self.uhdev
681
682 syn_event = self.syn_event
683
684 r = uhdev.event(0, -1)
685 expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1)
686 events = uhdev.next_sync_events()
687 self.debug_reports(r, uhdev, events)
688 self.assertInputEvents((syn_event, expected_event), events)
689
690 r = uhdev.event(1, 0)
691 expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)
692 events = uhdev.next_sync_events()
693 self.debug_reports(r, uhdev, events)
694 self.assertInputEvents((syn_event, expected_event), events)
695
696 r = uhdev.event(-1, 2)
697 expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)
698 expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)
699 events = uhdev.next_sync_events()
700 self.debug_reports(r, uhdev, events)
701 self.assertInputEvents(
702 (syn_event, expected_event0, expected_event1), events
703 )
704
705
706class TestSimpleMouse(BaseTest.TestMouse):
707 def create_device(self):
708 return ButtonMouse()
709
710 def test_rdesc(self):
711 """Check that the testsuite actually manages to format the
712 reports according to the report descriptors.
713 No kernel device is used here"""
714 uhdev = self.uhdev
715
716 event = (0, 0, (None, None, None))
717 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
718
719 event = (0, 0, (None, True, None))
720 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
721
722 event = (0, 0, (True, True, None))
723 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
724
725 event = (0, 0, (False, False, False))
726 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
727
728 event = (1, 0, (True, False, True))
729 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
730
731 event = (-1, 0, (True, False, True))
732 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
733
734 event = (-5, 5, (True, False, True))
735 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
736
737 event = (-127, 127, (True, False, True))
738 assert uhdev.fake_report(*event) == uhdev.create_report(*event)
739
740 event = (0, -128, (True, False, True))
741 with pytest.raises(hidtools.hid.RangeError):
742 uhdev.create_report(*event)
743
744
745class TestWheelMouse(BaseTest.TestMouse):
746 def create_device(self):
747 return WheelMouse()
748
749 def is_wheel_highres(self, uhdev):
750 evdev = uhdev.get_evdev()
751 assert evdev.has(libevdev.EV_REL.REL_WHEEL)
752 return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES)
753
754 def test_wheel(self):
755 uhdev = self.uhdev
756
757 # check if the kernel is high res wheel compatible
758 high_res_wheel = self.is_wheel_highres(uhdev)
759
760 syn_event = self.syn_event
761 # The Resolution Multiplier is applied to the HID reports, so we
762 # need to pre-multiply too.
763 mult = uhdev.wheel_multiplier
764
765 r = uhdev.event(0, 0, wheels=1 * mult)
766 expected = [syn_event]
767 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
768 if high_res_wheel:
769 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120))
770 events = uhdev.next_sync_events()
771 self.debug_reports(r, uhdev, events)
772 self.assertInputEvents(expected, events)
773
774 r = uhdev.event(0, 0, wheels=-1 * mult)
775 expected = [syn_event]
776 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1))
777 if high_res_wheel:
778 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120))
779 events = uhdev.next_sync_events()
780 self.debug_reports(r, uhdev, events)
781 self.assertInputEvents(expected, events)
782
783 r = uhdev.event(-1, 2, wheels=3 * mult)
784 expected = [syn_event]
785 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
786 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
787 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3))
788 if high_res_wheel:
789 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360))
790 events = uhdev.next_sync_events()
791 self.debug_reports(r, uhdev, events)
792 self.assertInputEvents(expected, events)
793
794
795class TestTwoWheelMouse(TestWheelMouse):
796 def create_device(self):
797 return TwoWheelMouse()
798
799 def is_hwheel_highres(self, uhdev):
800 evdev = uhdev.get_evdev()
801 assert evdev.has(libevdev.EV_REL.REL_HWHEEL)
802 return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES)
803
804 def test_ac_pan(self):
805 uhdev = self.uhdev
806
807 # check if the kernel is high res wheel compatible
808 high_res_wheel = self.is_wheel_highres(uhdev)
809 high_res_hwheel = self.is_hwheel_highres(uhdev)
810 assert high_res_wheel == high_res_hwheel
811
812 syn_event = self.syn_event
813 # The Resolution Multiplier is applied to the HID reports, so we
814 # need to pre-multiply too.
815 hmult = uhdev.hwheel_multiplier
816 vmult = uhdev.wheel_multiplier
817
818 r = uhdev.event(0, 0, wheels=(0, 1 * hmult))
819 expected = [syn_event]
820 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
821 if high_res_hwheel:
822 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120))
823 events = uhdev.next_sync_events()
824 self.debug_reports(r, uhdev, events)
825 self.assertInputEvents(expected, events)
826
827 r = uhdev.event(0, 0, wheels=(0, -1 * hmult))
828 expected = [syn_event]
829 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1))
830 if high_res_hwheel:
831 expected.append(
832 libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120)
833 )
834 events = uhdev.next_sync_events()
835 self.debug_reports(r, uhdev, events)
836 self.assertInputEvents(expected, events)
837
838 r = uhdev.event(-1, 2, wheels=(0, 3 * hmult))
839 expected = [syn_event]
840 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
841 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
842 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3))
843 if high_res_hwheel:
844 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360))
845 events = uhdev.next_sync_events()
846 self.debug_reports(r, uhdev, events)
847 self.assertInputEvents(expected, events)
848
849 r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult))
850 expected = [syn_event]
851 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
852 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
853 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3))
854 if high_res_wheel:
855 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360))
856 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4))
857 if high_res_wheel:
858 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480))
859 events = uhdev.next_sync_events()
860 self.debug_reports(r, uhdev, events)
861 self.assertInputEvents(expected, events)
862
863
864class TestResolutionMultiplierMouse(TestTwoWheelMouse):
865 def create_device(self):
866 return ResolutionMultiplierMouse()
867
868 def is_wheel_highres(self, uhdev):
869 high_res = super().is_wheel_highres(uhdev)
870
871 if not high_res:
872 # the kernel doesn't seem to support the high res wheel mice,
873 # make sure we haven't triggered the feature
874 assert uhdev.wheel_multiplier == 1
875
876 return high_res
877
878 def test_resolution_multiplier_wheel(self):
879 uhdev = self.uhdev
880
881 if not self.is_wheel_highres(uhdev):
882 pytest.skip("Kernel not compatible, we can not trigger the conditions")
883
884 assert uhdev.wheel_multiplier > 1
885 assert 120 % uhdev.wheel_multiplier == 0
886
887 def test_wheel_with_multiplier(self):
888 uhdev = self.uhdev
889
890 if not self.is_wheel_highres(uhdev):
891 pytest.skip("Kernel not compatible, we can not trigger the conditions")
892
893 assert uhdev.wheel_multiplier > 1
894
895 syn_event = self.syn_event
896 mult = uhdev.wheel_multiplier
897
898 r = uhdev.event(0, 0, wheels=1)
899 expected = [syn_event]
900 expected.append(
901 libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
902 )
903 events = uhdev.next_sync_events()
904 self.debug_reports(r, uhdev, events)
905 self.assertInputEvents(expected, events)
906
907 r = uhdev.event(0, 0, wheels=-1)
908 expected = [syn_event]
909 expected.append(
910 libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult)
911 )
912 events = uhdev.next_sync_events()
913 self.debug_reports(r, uhdev, events)
914 self.assertInputEvents(expected, events)
915
916 expected = [syn_event]
917 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
918 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
919 expected.append(
920 libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
921 )
922
923 for _ in range(mult - 1):
924 r = uhdev.event(1, -2, wheels=1)
925 events = uhdev.next_sync_events()
926 self.debug_reports(r, uhdev, events)
927 self.assertInputEvents(expected, events)
928
929 r = uhdev.event(1, -2, wheels=1)
930 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
931 events = uhdev.next_sync_events()
932 self.debug_reports(r, uhdev, events)
933 self.assertInputEvents(expected, events)
934
935
936class TestBadResolutionMultiplierMouse(TestTwoWheelMouse):
937 def create_device(self):
938 return BadResolutionMultiplierMouse()
939
940 def is_wheel_highres(self, uhdev):
941 high_res = super().is_wheel_highres(uhdev)
942
943 assert uhdev.wheel_multiplier == 1
944
945 return high_res
946
947 def test_resolution_multiplier_wheel(self):
948 uhdev = self.uhdev
949
950 assert uhdev.wheel_multiplier == 1
951
952
953class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse):
954 def create_device(self):
955 return ResolutionMultiplierHWheelMouse()
956
957 def is_hwheel_highres(self, uhdev):
958 high_res = super().is_hwheel_highres(uhdev)
959
960 if not high_res:
961 # the kernel doesn't seem to support the high res wheel mice,
962 # make sure we haven't triggered the feature
963 assert uhdev.hwheel_multiplier == 1
964
965 return high_res
966
967 def test_resolution_multiplier_ac_pan(self):
968 uhdev = self.uhdev
969
970 if not self.is_hwheel_highres(uhdev):
971 pytest.skip("Kernel not compatible, we can not trigger the conditions")
972
973 assert uhdev.hwheel_multiplier > 1
974 assert 120 % uhdev.hwheel_multiplier == 0
975
976 def test_ac_pan_with_multiplier(self):
977 uhdev = self.uhdev
978
979 if not self.is_hwheel_highres(uhdev):
980 pytest.skip("Kernel not compatible, we can not trigger the conditions")
981
982 assert uhdev.hwheel_multiplier > 1
983
984 syn_event = self.syn_event
985 hmult = uhdev.hwheel_multiplier
986
987 r = uhdev.event(0, 0, wheels=(0, 1))
988 expected = [syn_event]
989 expected.append(
990 libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
991 )
992 events = uhdev.next_sync_events()
993 self.debug_reports(r, uhdev, events)
994 self.assertInputEvents(expected, events)
995
996 r = uhdev.event(0, 0, wheels=(0, -1))
997 expected = [syn_event]
998 expected.append(
999 libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult)
1000 )
1001 events = uhdev.next_sync_events()
1002 self.debug_reports(r, uhdev, events)
1003 self.assertInputEvents(expected, events)
1004
1005 expected = [syn_event]
1006 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
1007 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
1008 expected.append(
1009 libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
1010 )
1011
1012 for _ in range(hmult - 1):
1013 r = uhdev.event(1, -2, wheels=(0, 1))
1014 events = uhdev.next_sync_events()
1015 self.debug_reports(r, uhdev, events)
1016 self.assertInputEvents(expected, events)
1017
1018 r = uhdev.event(1, -2, wheels=(0, 1))
1019 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
1020 events = uhdev.next_sync_events()
1021 self.debug_reports(r, uhdev, events)
1022 self.assertInputEvents(expected, events)
1023
1024
1025class TestMiMouse(TestWheelMouse):
1026 def create_device(self):
1027 return MIDongleMIWirelessMouse()
1028
1029 def assertInputEvents(self, expected_events, effective_events):
1030 # Buttons and x/y are spread over two HID reports, so we can get two
1031 # event frames for this device.
1032 remaining = self.assertInputEventsIn(expected_events, effective_events)
1033 try:
1034 remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0))
1035 except ValueError:
1036 # If there's no SYN_REPORT in the list, continue and let the
1037 # assert below print out the real error
1038 pass
1039 assert remaining == []
1040
1041
1042class TestBadReportDescriptorMouse(base.BaseTestCase.TestUhid):
1043 def create_device(self):
1044 return BadReportDescriptorMouse()
1045
1046 def assertName(self, uhdev):
1047 pass