1047 lines · 45.0 KB · Python
RawDownload
#!/bin/env python3# SPDX-License-Identifier: GPL-2.0# -*- coding: utf-8 -*-## Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com># Copyright (c) 2017 Red Hat, Inc.# from . import baseimport hidtools.hidfrom hidtools.util import BusTypeimport libevdevimport loggingimport pytest logger = logging.getLogger("hidtools.test.mouse") # workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6try:    libevdev.EV_REL.REL_WHEEL_HI_RESexcept AttributeError:    libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B    libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C  class InvalidHIDCommunication(Exception):    pass  class MouseData(object):    pass  class BaseMouse(base.UHIDTestDevice):    def __init__(self, rdesc, name=None, input_info=None):        assert rdesc is not None        super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)        self.left = False        self.right = False        self.middle = False     def create_report(self, x, y, buttons=None, wheels=None, reportID=None):        """        Return an input report for this device.         :param x: relative x        :param y: relative y        :param buttons: a (l, r, m) tuple of bools for the button states,            where ``None`` is "leave unchanged"        :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for            the two wheels        :param reportID: the numeric report ID for this report, if needed        """        if buttons is not None:            left, right, middle = buttons            if left is not None:                self.left = left            if right is not None:                self.right = right            if middle is not None:                self.middle = middle        left = self.left        right = self.right        middle = self.middle        # Note: the BaseMouse doesn't actually have a wheel but the        # create_report magic only fills in those fields exist, so let's        # make this generic here.        wheel, acpan = 0, 0        if wheels is not None:            if isinstance(wheels, tuple):                wheel = wheels[0]                acpan = wheels[1]            else:                wheel = wheels         reportID = reportID or self.default_reportID         mouse = MouseData()        mouse.b1 = int(left)        mouse.b2 = int(right)        mouse.b3 = int(middle)        mouse.x = x        mouse.y = y        mouse.wheel = wheel        mouse.acpan = acpan        return super().create_report(mouse, reportID=reportID)     def event(self, x, y, buttons=None, wheels=None):        """        Send an input event on the default report ID.         :param x: relative x        :param y: relative y        :param buttons: a (l, r, m) tuple of bools for the button states,            where ``None`` is "leave unchanged"        :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for            the two wheels        """        r = self.create_report(x, y, buttons, wheels)        self.call_input_event(r)        return [r]  class ButtonMouse(BaseMouse):    # fmt: off    report_descriptor = [        0x05, 0x01,  # .Usage Page (Generic Desktop)        0        0x09, 0x02,  # .Usage (Mouse)                       2        0xa1, 0x01,  # .Collection (Application)            4        0x09, 0x02,  # ..Usage (Mouse)                      6        0xa1, 0x02,  # ..Collection (Logical)               8        0x09, 0x01,  # ...Usage (Pointer)                   10        0xa1, 0x00,  # ...Collection (Physical)             12        0x05, 0x09,  # ....Usage Page (Button)              14        0x19, 0x01,  # ....Usage Minimum (1)                16        0x29, 0x03,  # ....Usage Maximum (3)                18        0x15, 0x00,  # ....Logical Minimum (0)              20        0x25, 0x01,  # ....Logical Maximum (1)              22        0x75, 0x01,  # ....Report Size (1)                  24        0x95, 0x03,  # ....Report Count (3)                 26        0x81, 0x02,  # ....Input (Data,Var,Abs)             28        0x75, 0x05,  # ....Report Size (5)                  30        0x95, 0x01,  # ....Report Count (1)                 32        0x81, 0x03,  # ....Input (Cnst,Var,Abs)             34        0x05, 0x01,  # ....Usage Page (Generic Desktop)     36        0x09, 0x30,  # ....Usage (X)                        38        0x09, 0x31,  # ....Usage (Y)                        40        0x15, 0x81,  # ....Logical Minimum (-127)           42        0x25, 0x7f,  # ....Logical Maximum (127)            44        0x75, 0x08,  # ....Report Size (8)                  46        0x95, 0x02,  # ....Report Count (2)                 48        0x81, 0x06,  # ....Input (Data,Var,Rel)             50        0xc0,        # ...End Collection                    52        0xc0,        # ..End Collection                     53        0xc0,        # .End Collection                      54    ]    # fmt: on     def __init__(self, rdesc=report_descriptor, name=None, input_info=None):        super().__init__(rdesc, name, input_info)     def fake_report(self, x, y, buttons):        if buttons is not None:            left, right, middle = buttons            if left is None:                left = self.left            if right is None:                right = self.right            if middle is None:                middle = self.middle        else:            left = self.left            right = self.right            middle = self.middle         button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)        x = max(-127, min(127, x))        y = max(-127, min(127, y))        x = hidtools.util.to_twos_comp(x, 8)        y = hidtools.util.to_twos_comp(y, 8)        return [button_mask, x, y]  class WheelMouse(ButtonMouse):    # fmt: off    report_descriptor = [        0x05, 0x01,  # Usage Page (Generic Desktop)        0        0x09, 0x02,  # Usage (Mouse)                       2        0xa1, 0x01,  # Collection (Application)            4        0x05, 0x09,  # .Usage Page (Button)                6        0x19, 0x01,  # .Usage Minimum (1)                  8        0x29, 0x03,  # .Usage Maximum (3)                  10        0x15, 0x00,  # .Logical Minimum (0)                12        0x25, 0x01,  # .Logical Maximum (1)                14        0x95, 0x03,  # .Report Count (3)                   16        0x75, 0x01,  # .Report Size (1)                    18        0x81, 0x02,  # .Input (Data,Var,Abs)               20        0x95, 0x01,  # .Report Count (1)                   22        0x75, 0x05,  # .Report Size (5)                    24        0x81, 0x03,  # .Input (Cnst,Var,Abs)               26        0x05, 0x01,  # .Usage Page (Generic Desktop)       28        0x09, 0x01,  # .Usage (Pointer)                    30        0xa1, 0x00,  # .Collection (Physical)              32        0x09, 0x30,  # ..Usage (X)                         34        0x09, 0x31,  # ..Usage (Y)                         36        0x15, 0x81,  # ..Logical Minimum (-127)            38        0x25, 0x7f,  # ..Logical Maximum (127)             40        0x75, 0x08,  # ..Report Size (8)                   42        0x95, 0x02,  # ..Report Count (2)                  44        0x81, 0x06,  # ..Input (Data,Var,Rel)              46        0xc0,        # .End Collection                     48        0x09, 0x38,  # .Usage (Wheel)                      49        0x15, 0x81,  # .Logical Minimum (-127)             51        0x25, 0x7f,  # .Logical Maximum (127)              53        0x75, 0x08,  # .Report Size (8)                    55        0x95, 0x01,  # .Report Count (1)                   57        0x81, 0x06,  # .Input (Data,Var,Rel)               59        0xc0,        # End Collection                      61    ]    # fmt: on     def __init__(self, rdesc=report_descriptor, name=None, input_info=None):        super().__init__(rdesc, name, input_info)        self.wheel_multiplier = 1  class TwoWheelMouse(WheelMouse):    # fmt: off    report_descriptor = [        0x05, 0x01,        # Usage Page (Generic Desktop)        0        0x09, 0x02,        # Usage (Mouse)                       2        0xa1, 0x01,        # Collection (Application)            4        0x09, 0x01,        # .Usage (Pointer)                    6        0xa1, 0x00,        # .Collection (Physical)              8        0x05, 0x09,        # ..Usage Page (Button)               10        0x19, 0x01,        # ..Usage Minimum (1)                 12        0x29, 0x10,        # ..Usage Maximum (16)                14        0x15, 0x00,        # ..Logical Minimum (0)               16        0x25, 0x01,        # ..Logical Maximum (1)               18        0x95, 0x10,        # ..Report Count (16)                 20        0x75, 0x01,        # ..Report Size (1)                   22        0x81, 0x02,        # ..Input (Data,Var,Abs)              24        0x05, 0x01,        # ..Usage Page (Generic Desktop)      26        0x16, 0x01, 0x80,  # ..Logical Minimum (-32767)          28        0x26, 0xff, 0x7f,  # ..Logical Maximum (32767)           31        0x75, 0x10,        # ..Report Size (16)                  34        0x95, 0x02,        # ..Report Count (2)                  36        0x09, 0x30,        # ..Usage (X)                         38        0x09, 0x31,        # ..Usage (Y)                         40        0x81, 0x06,        # ..Input (Data,Var,Rel)              42        0x15, 0x81,        # ..Logical Minimum (-127)            44        0x25, 0x7f,        # ..Logical Maximum (127)             46        0x75, 0x08,        # ..Report Size (8)                   48        0x95, 0x01,        # ..Report Count (1)                  50        0x09, 0x38,        # ..Usage (Wheel)                     52        0x81, 0x06,        # ..Input (Data,Var,Rel)              54        0x05, 0x0c,        # ..Usage Page (Consumer Devices)     56        0x0a, 0x38, 0x02,  # ..Usage (AC Pan)                    58        0x95, 0x01,        # ..Report Count (1)                  61        0x81, 0x06,        # ..Input (Data,Var,Rel)              63        0xc0,              # .End Collection                     65        0xc0,              # End Collection                      66    ]    # fmt: on     def __init__(self, rdesc=report_descriptor, name=None, input_info=None):        super().__init__(rdesc, name, input_info)        self.hwheel_multiplier = 1  class MIDongleMIWirelessMouse(TwoWheelMouse):    # fmt: off    report_descriptor = [        0x05, 0x01,         # Usage Page (Generic Desktop)        0x09, 0x02,         # Usage (Mouse)        0xa1, 0x01,         # Collection (Application)        0x85, 0x01,         # .Report ID (1)        0x09, 0x01,         # .Usage (Pointer)        0xa1, 0x00,         # .Collection (Physical)        0x95, 0x05,         # ..Report Count (5)        0x75, 0x01,         # ..Report Size (1)        0x05, 0x09,         # ..Usage Page (Button)        0x19, 0x01,         # ..Usage Minimum (1)        0x29, 0x05,         # ..Usage Maximum (5)        0x15, 0x00,         # ..Logical Minimum (0)        0x25, 0x01,         # ..Logical Maximum (1)        0x81, 0x02,         # ..Input (Data,Var,Abs)        0x95, 0x01,         # ..Report Count (1)        0x75, 0x03,         # ..Report Size (3)        0x81, 0x01,         # ..Input (Cnst,Arr,Abs)        0x75, 0x08,         # ..Report Size (8)        0x95, 0x01,         # ..Report Count (1)        0x05, 0x01,         # ..Usage Page (Generic Desktop)        0x09, 0x38,         # ..Usage (Wheel)        0x15, 0x81,         # ..Logical Minimum (-127)        0x25, 0x7f,         # ..Logical Maximum (127)        0x81, 0x06,         # ..Input (Data,Var,Rel)        0x05, 0x0c,         # ..Usage Page (Consumer Devices)        0x0a, 0x38, 0x02,   # ..Usage (AC Pan)        0x95, 0x01,         # ..Report Count (1)        0x81, 0x06,         # ..Input (Data,Var,Rel)        0xc0,               # .End Collection        0x85, 0x02,         # .Report ID (2)        0x09, 0x01,         # .Usage (Consumer Control)        0xa1, 0x00,         # .Collection (Physical)        0x75, 0x0c,         # ..Report Size (12)        0x95, 0x02,         # ..Report Count (2)        0x05, 0x01,         # ..Usage Page (Generic Desktop)        0x09, 0x30,         # ..Usage (X)        0x09, 0x31,         # ..Usage (Y)        0x16, 0x01, 0xf8,   # ..Logical Minimum (-2047)        0x26, 0xff, 0x07,   # ..Logical Maximum (2047)        0x81, 0x06,         # ..Input (Data,Var,Rel)        0xc0,               # .End Collection        0xc0,               # End Collection        0x05, 0x0c,         # Usage Page (Consumer Devices)        0x09, 0x01,         # Usage (Consumer Control)        0xa1, 0x01,         # Collection (Application)        0x85, 0x03,         # .Report ID (3)        0x15, 0x00,         # .Logical Minimum (0)        0x25, 0x01,         # .Logical Maximum (1)        0x75, 0x01,         # .Report Size (1)        0x95, 0x01,         # .Report Count (1)        0x09, 0xcd,         # .Usage (Play/Pause)        0x81, 0x06,         # .Input (Data,Var,Rel)        0x0a, 0x83, 0x01,   # .Usage (AL Consumer Control Config)        0x81, 0x06,         # .Input (Data,Var,Rel)        0x09, 0xb5,         # .Usage (Scan Next Track)        0x81, 0x06,         # .Input (Data,Var,Rel)        0x09, 0xb6,         # .Usage (Scan Previous Track)        0x81, 0x06,         # .Input (Data,Var,Rel)        0x09, 0xea,         # .Usage (Volume Down)        0x81, 0x06,         # .Input (Data,Var,Rel)        0x09, 0xe9,         # .Usage (Volume Up)        0x81, 0x06,         # .Input (Data,Var,Rel)        0x0a, 0x25, 0x02,   # .Usage (AC Forward)        0x81, 0x06,         # .Input (Data,Var,Rel)        0x0a, 0x24, 0x02,   # .Usage (AC Back)        0x81, 0x06,         # .Input (Data,Var,Rel)        0xc0,               # End Collection    ]    # fmt: on    device_input_info = (BusType.USB, 0x2717, 0x003B)    device_name = "uhid test MI Dongle MI Wireless Mouse"     def __init__(        self, rdesc=report_descriptor, name=device_name, input_info=device_input_info    ):        super().__init__(rdesc, name, input_info)     def event(self, x, y, buttons=None, wheels=None):        # this mouse spreads the relative pointer and the mouse buttons        # onto 2 distinct reports        rs = []        r = self.create_report(x, y, buttons, wheels, reportID=1)        self.call_input_event(r)        rs.append(r)        r = self.create_report(x, y, buttons, reportID=2)        self.call_input_event(r)        rs.append(r)        return rs  class ResolutionMultiplierMouse(TwoWheelMouse):    # fmt: off    report_descriptor = [        0x05, 0x01,        # Usage Page (Generic Desktop)        83        0x09, 0x02,        # Usage (Mouse)                       85        0xa1, 0x01,        # Collection (Application)            87        0x05, 0x01,        # .Usage Page (Generic Desktop)       89        0x09, 0x02,        # .Usage (Mouse)                      91        0xa1, 0x02,        # .Collection (Logical)               93        0x85, 0x11,        # ..Report ID (17)                    95        0x09, 0x01,        # ..Usage (Pointer)                   97        0xa1, 0x00,        # ..Collection (Physical)             99        0x05, 0x09,        # ...Usage Page (Button)              101        0x19, 0x01,        # ...Usage Minimum (1)                103        0x29, 0x03,        # ...Usage Maximum (3)                105        0x95, 0x03,        # ...Report Count (3)                 107        0x75, 0x01,        # ...Report Size (1)                  109        0x25, 0x01,        # ...Logical Maximum (1)              111        0x81, 0x02,        # ...Input (Data,Var,Abs)             113        0x95, 0x01,        # ...Report Count (1)                 115        0x81, 0x01,        # ...Input (Cnst,Arr,Abs)             117        0x09, 0x05,        # ...Usage (Vendor Usage 0x05)        119        0x81, 0x02,        # ...Input (Data,Var,Abs)             121        0x95, 0x03,        # ...Report Count (3)                 123        0x81, 0x01,        # ...Input (Cnst,Arr,Abs)             125        0x05, 0x01,        # ...Usage Page (Generic Desktop)     127        0x09, 0x30,        # ...Usage (X)                        129        0x09, 0x31,        # ...Usage (Y)                        131        0x95, 0x02,        # ...Report Count (2)                 133        0x75, 0x08,        # ...Report Size (8)                  135        0x15, 0x81,        # ...Logical Minimum (-127)           137        0x25, 0x7f,        # ...Logical Maximum (127)            139        0x81, 0x06,        # ...Input (Data,Var,Rel)             141        0xa1, 0x02,        # ...Collection (Logical)             143        0x85, 0x12,        # ....Report ID (18)                  145        0x09, 0x48,        # ....Usage (Resolution Multiplier)   147        0x95, 0x01,        # ....Report Count (1)                149        0x75, 0x02,        # ....Report Size (2)                 151        0x15, 0x00,        # ....Logical Minimum (0)             153        0x25, 0x01,        # ....Logical Maximum (1)             155        0x35, 0x01,        # ....Physical Minimum (1)            157        0x45, 0x04,        # ....Physical Maximum (4)            159        0xb1, 0x02,        # ....Feature (Data,Var,Abs)          161        0x35, 0x00,        # ....Physical Minimum (0)            163        0x45, 0x00,        # ....Physical Maximum (0)            165        0x75, 0x06,        # ....Report Size (6)                 167        0xb1, 0x01,        # ....Feature (Cnst,Arr,Abs)          169        0x85, 0x11,        # ....Report ID (17)                  171        0x09, 0x38,        # ....Usage (Wheel)                   173        0x15, 0x81,        # ....Logical Minimum (-127)          175        0x25, 0x7f,        # ....Logical Maximum (127)           177        0x75, 0x08,        # ....Report Size (8)                 179        0x81, 0x06,        # ....Input (Data,Var,Rel)            181        0xc0,              # ...End Collection                   183        0x05, 0x0c,        # ...Usage Page (Consumer Devices)    184        0x75, 0x08,        # ...Report Size (8)                  186        0x0a, 0x38, 0x02,  # ...Usage (AC Pan)                   188        0x81, 0x06,        # ...Input (Data,Var,Rel)             191        0xc0,              # ..End Collection                    193        0xc0,              # .End Collection                     194        0xc0,              # End Collection                      195    ]    # fmt: on     def __init__(self, rdesc=report_descriptor, name=None, input_info=None):        super().__init__(rdesc, name, input_info)        self.default_reportID = 0x11         # Feature Report 12, multiplier Feature value must be set to 0b01,        # i.e. 1. We should extract that from the descriptor instead        # of hardcoding it here, but meanwhile this will do.        self.set_feature_report = [0x12, 0x1]     def set_report(self, req, rnum, rtype, data):        if rtype != self.UHID_FEATURE_REPORT:            raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")        if rnum != 0x12:            raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")         if data != self.set_feature_report:            raise InvalidHIDCommunication(                f"Unexpected data: {data}, expected {self.set_feature_report}"            )         self.wheel_multiplier = 4         return 0  class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):    def set_report(self, req, rnum, rtype, data):        super().set_report(req, rnum, rtype, data)         self.wheel_multiplier = 1        self.hwheel_multiplier = 1        return 32  # EPIPE  class BadReportDescriptorMouse(BaseMouse):    """    This "device" was one autogenerated by syzbot. There are a lot of issues in    it, and the most problematic is that it declares features that have no    size.     This leads to report->size being set to 0 and can mess up with usbhid    internals.  Fortunately, uhid merely passes the incoming buffer, without    touching it so a buffer of size 0 will be translated to [] without    triggering a kernel oops.     Because the report descriptor is wrong, no input are created, and we need    to tweak a little bit the parameters to make it look correct.    """     # fmt: off    report_descriptor = [        0x96, 0x01, 0x00,              # Report Count (1)                    0        0x06, 0x01, 0x00,              # Usage Page (Generic Desktop)        3        # 0x03, 0x00, 0x00, 0x00, 0x00,  # Ignored by the kernel somehow        0x2a, 0x90, 0xa0,              # Usage Maximum (41104)               6        0x27, 0x00, 0x00, 0x00, 0x00,  # Logical Maximum (0)                 9        0xb3, 0x81, 0x3e, 0x25, 0x03,  # Feature (Cnst,Arr,Abs,Vol)          14        0x1b, 0xdd, 0xe8, 0x40, 0x50,  # Usage Minimum (1346431197)          19        0x3b, 0x5d, 0x8c, 0x3d, 0xda,  # Designator Index                    24    ]    # fmt: on     def __init__(        self, rdesc=report_descriptor, name=None, input_info=(3, 0x045E, 0x07DA)    ):        super().__init__(rdesc, name, input_info)        self.high_resolution_report_called = False     def get_evdev(self, application=None):        assert self._input_nodes is None        return (            "Ok"  # should be a list or None, but both would fail, so abusing the system        )     def next_sync_events(self, application=None):        # there are no evdev nodes, so no events        return []     def is_ready(self):        # we wait for the SET_REPORT command to come        return self.high_resolution_report_called     def set_report(self, req, rnum, rtype, data):        if rtype != self.UHID_FEATURE_REPORT:            raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")        if rnum != 0x0:            raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")         if len(data) != 1:            raise InvalidHIDCommunication(f"Unexpected data: {data}, expected '[0]'")         self.high_resolution_report_called = True 

Lines 1–500 of 1047.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t