From 7d0b3f100b144d6910d75158c586b047f7e634a2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 23 Jan 2023 10:54:09 +0100 Subject: selftests: hid: make vmtest rely on make Having a default binary is simple enough, but this also means that we need to keep the targets in sync as we are adding them in the Makefile. So instead of doing that manual work, make vmtest.sh generic enough to actually be capable of running 'make -C tools/testing/selftests/hid'. The new image we use has make installed, which the base fedora image doesn't. Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/vmtest.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh index 90f34150f257..6346b0620dba 100755 --- a/tools/testing/selftests/hid/vmtest.sh +++ b/tools/testing/selftests/hid/vmtest.sh @@ -16,7 +16,6 @@ x86_64) exit 1 ;; esac -DEFAULT_COMMAND="./hid_bpf" SCRIPT_DIR="$(dirname $(realpath $0))" OUTPUT_DIR="$SCRIPT_DIR/results" KCONFIG_REL_PATHS=("${SCRIPT_DIR}/config" "${SCRIPT_DIR}/config.common" "${SCRIPT_DIR}/config.${ARCH}") @@ -25,7 +24,10 @@ NUM_COMPILE_JOBS="$(nproc)" LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")" LOG_FILE="${LOG_FILE_BASE}.log" EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status" -CONTAINER_IMAGE="registry.fedoraproject.org/fedora:36" +CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1" + +TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}" +DEFAULT_COMMAND="make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" usage() { @@ -33,9 +35,9 @@ usage() Usage: $0 [-i] [-s] [-d ] -- [] is the command you would normally run when you are in -tools/testing/selftests/bpf. e.g: +the source kernel direcory. e.g: - $0 -- ./hid_bpf + $0 -- ./tools/testing/selftests/hid/hid_bpf If no command is specified and a debug shell (-s) is not requested, "${DEFAULT_COMMAND}" will be run by default. @@ -43,11 +45,11 @@ If no command is specified and a debug shell (-s) is not requested, If you build your kernel using KBUILD_OUTPUT= or O= options, these can be passed as environment variables to the script: - O= $0 -- ./hid_bpf + O= $0 -- ./tools/testing/selftests/hid/hid_bpf or - KBUILD_OUTPUT= $0 -- ./hid_bpf + KBUILD_OUTPUT= $0 -- ./tools/testing/selftests/hid/hid_bpf Options: @@ -91,11 +93,14 @@ update_selftests() run_vm() { - local b2c="$1" - local kernel_bzimage="$2" - local command="$3" + local run_dir="$1" + local b2c="$2" + local kernel_bzimage="$3" + local command="$4" local post_command="" + cd "${run_dir}" + if ! which "${QEMU_BINARY}" &> /dev/null; then cat < Date: Fri, 17 Feb 2023 12:25:14 +0100 Subject: selftests: hid: import hid-tools hid-core tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. I haven't imported all of hid-tools, the python module, but only the tests related to the kernel. We can rely on pip to fetch the latest hid-tools release, and then run the tests directly from the tree. This should now be easier to request tests when something is not behaving properly in the HID subsystem. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Signed-off-by: Peter Hutterer Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 2 + tools/testing/selftests/hid/hid-core.sh | 7 + tools/testing/selftests/hid/run-hid-tools-tests.sh | 28 ++ tools/testing/selftests/hid/tests/__init__.py | 2 + tools/testing/selftests/hid/tests/base.py | 345 +++++++++++++++++++++ tools/testing/selftests/hid/tests/conftest.py | 81 +++++ tools/testing/selftests/hid/tests/test_hid_core.py | 154 +++++++++ tools/testing/selftests/hid/vmtest.sh | 2 +- 8 files changed, 620 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/hid/hid-core.sh create mode 100755 tools/testing/selftests/hid/run-hid-tools-tests.sh create mode 100644 tools/testing/selftests/hid/tests/__init__.py create mode 100644 tools/testing/selftests/hid/tests/base.py create mode 100644 tools/testing/selftests/hid/tests/conftest.py create mode 100644 tools/testing/selftests/hid/tests/test_hid_core.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 83e8f87d643a..bdcb36d80c8c 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -5,6 +5,8 @@ include ../../../build/Build.include include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include +TEST_PROGS := hid-core.sh + CXX ?= $(CROSS_COMPILE)g++ HOSTPKG_CONFIG := pkg-config diff --git a/tools/testing/selftests/hid/hid-core.sh b/tools/testing/selftests/hid/hid-core.sh new file mode 100755 index 000000000000..5bbabc12c34f --- /dev/null +++ b/tools/testing/selftests/hid/hid-core.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_hid_core.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/run-hid-tools-tests.sh b/tools/testing/selftests/hid/run-hid-tools-tests.sh new file mode 100755 index 000000000000..bdae8464da86 --- /dev/null +++ b/tools/testing/selftests/hid/run-hid-tools-tests.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +if ! command -v python3 > /dev/null 2>&1; then + echo "hid-tools: [SKIP] python3 not installed" + exit 77 +fi + +if ! python3 -c "import pytest" > /dev/null 2>&1; then + echo "hid: [SKIP/ pytest module not installed" + exit 77 +fi + +if ! python3 -c "import pytest_tap" > /dev/null 2>&1; then + echo "hid: [SKIP/ pytest_tap module not installed" + exit 77 +fi + +if ! python3 -c "import hidtools" > /dev/null 2>&1; then + echo "hid: [SKIP/ hid-tools module not installed" + exit 77 +fi + +TARGET=${TARGET:=.} + +echo TAP version 13 +python3 -u -m pytest $PYTEST_XDIST ./tests/$TARGET --tap-stream --udevd diff --git a/tools/testing/selftests/hid/tests/__init__.py b/tools/testing/selftests/hid/tests/__init__.py new file mode 100644 index 000000000000..c940e9275252 --- /dev/null +++ b/tools/testing/selftests/hid/tests/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +# Just to make sphinx-apidoc document this directory diff --git a/tools/testing/selftests/hid/tests/base.py b/tools/testing/selftests/hid/tests/base.py new file mode 100644 index 000000000000..1305cfc9646e --- /dev/null +++ b/tools/testing/selftests/hid/tests/base.py @@ -0,0 +1,345 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. + +import libevdev +import os +import pytest +import time + +import logging + +from hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile +from pathlib import Path +from typing import Final + +logger = logging.getLogger("hidtools.test.base") + +# application to matches +application_matches: Final = { + # pyright: ignore + "Accelerometer": EvdevMatch( + req_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ] + ), + "Game Pad": EvdevMatch( # in systemd, this is a lot more complex, but that will do + requires=[ + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + libevdev.EV_ABS.ABS_RX, + libevdev.EV_ABS.ABS_RY, + libevdev.EV_KEY.BTN_START, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Joystick": EvdevMatch( # in systemd, this is a lot more complex, but that will do + requires=[ + libevdev.EV_ABS.ABS_RX, + libevdev.EV_ABS.ABS_RY, + libevdev.EV_KEY.BTN_START, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Key": EvdevMatch( + requires=[ + libevdev.EV_KEY.KEY_A, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + libevdev.INPUT_PROP_DIRECT, + libevdev.INPUT_PROP_POINTER, + ], + ), + "Mouse": EvdevMatch( + requires=[ + libevdev.EV_REL.REL_X, + libevdev.EV_REL.REL_Y, + libevdev.EV_KEY.BTN_LEFT, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Pad": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_0, + ], + excludes=[ + libevdev.EV_KEY.BTN_TOOL_PEN, + libevdev.EV_KEY.BTN_TOUCH, + libevdev.EV_ABS.ABS_DISTANCE, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Pen": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_STYLUS, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Stylus": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_STYLUS, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Touch Pad": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_LEFT, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS], + req_properties=[ + libevdev.INPUT_PROP_POINTER, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Touch Screen": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_TOUCH, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS], + req_properties=[ + libevdev.INPUT_PROP_DIRECT, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), +} + + +class UHIDTestDevice(BaseDevice): + def __init__(self, name, application, rdesc_str=None, rdesc=None, input_info=None): + super().__init__(name, application, rdesc_str, rdesc, input_info) + self.application_matches = application_matches + if name is None: + name = f"uhid test {self.__class__.__name__}" + if not name.startswith("uhid test "): + name = "uhid test " + self.name + self.name = name + + +class BaseTestCase: + class TestUhid(object): + syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) # type: ignore + key_event = libevdev.InputEvent(libevdev.EV_KEY) # type: ignore + abs_event = libevdev.InputEvent(libevdev.EV_ABS) # type: ignore + rel_event = libevdev.InputEvent(libevdev.EV_REL) # type: ignore + msc_event = libevdev.InputEvent(libevdev.EV_MSC.MSC_SCAN) # type: ignore + + # List of kernel modules to load before starting the test + # if any module is not available (not compiled), the test will skip. + # Each element is a tuple '(kernel driver name, kernel module)', + # for example ("playstation", "hid-playstation") + kernel_modules = [] + + def assertInputEventsIn(self, expected_events, effective_events): + effective_events = effective_events.copy() + for ev in expected_events: + assert ev in effective_events + effective_events.remove(ev) + return effective_events + + def assertInputEvents(self, expected_events, effective_events): + remaining = self.assertInputEventsIn(expected_events, effective_events) + assert remaining == [] + + @classmethod + def debug_reports(cls, reports, uhdev=None, events=None): + data = [" ".join([f"{v:02x}" for v in r]) for r in reports] + + if uhdev is not None: + human_data = [ + uhdev.parsed_rdesc.format_report(r, split_lines=True) + for r in reports + ] + try: + human_data = [ + f'\n\t {" " * h.index("/")}'.join(h.split("\n")) + for h in human_data + ] + except ValueError: + # '/' not found: not a numbered report + human_data = ["\n\t ".join(h.split("\n")) for h in human_data] + data = [f"{d}\n\t ====> {h}" for d, h in zip(data, human_data)] + + reports = data + + if len(reports) == 1: + print("sending 1 report:") + else: + print(f"sending {len(reports)} reports:") + for report in reports: + print("\t", report) + + if events is not None: + print("events received:", events) + + def create_device(self): + raise Exception("please reimplement me in subclasses") + + def _load_kernel_module(self, kernel_driver, kernel_module): + sysfs_path = Path("/sys/bus/hid/drivers") + if kernel_driver is not None: + sysfs_path /= kernel_driver + else: + # special case for when testing all available modules: + # we don't know beforehand the name of the module from modinfo + sysfs_path = Path("/sys/module") / kernel_module.replace("-", "_") + if not sysfs_path.exists(): + import subprocess + + ret = subprocess.run(["/usr/sbin/modprobe", kernel_module]) + if ret.returncode != 0: + pytest.skip( + f"module {kernel_module} could not be loaded, skipping the test" + ) + + @pytest.fixture() + def load_kernel_module(self): + for kernel_driver, kernel_module in self.kernel_modules: + self._load_kernel_module(kernel_driver, kernel_module) + yield + + @pytest.fixture() + def new_uhdev(self, load_kernel_module): + return self.create_device() + + def assertName(self, uhdev): + evdev = uhdev.get_evdev() + assert uhdev.name in evdev.name + + @pytest.fixture(autouse=True) + def context(self, new_uhdev, request): + try: + with HIDTestUdevRule.instance(): + with new_uhdev as self.uhdev: + skip_cond = request.node.get_closest_marker("skip_if_uhdev") + if skip_cond: + test, message, *rest = skip_cond.args + + if test(self.uhdev): + pytest.skip(message) + + self.uhdev.create_kernel_device() + now = time.time() + while not self.uhdev.is_ready() and time.time() - now < 5: + self.uhdev.dispatch(1) + if self.uhdev.get_evdev() is None: + logger.warning( + f"available list of input nodes: (default application is '{self.uhdev.application}')" + ) + logger.warning(self.uhdev.input_nodes) + yield + self.uhdev = None + except PermissionError: + pytest.skip("Insufficient permissions, run me as root") + + @pytest.fixture(autouse=True) + def check_taint(self): + # we are abusing SysfsFile here, it's in /proc, but meh + taint_file = SysfsFile("/proc/sys/kernel/tainted") + taint = taint_file.int_value + + yield + + assert taint_file.int_value == taint + + def test_creation(self): + """Make sure the device gets processed by the kernel and creates + the expected application input node. + + If this fail, there is something wrong in the device report + descriptors.""" + uhdev = self.uhdev + assert uhdev is not None + assert uhdev.get_evdev() is not None + self.assertName(uhdev) + assert len(uhdev.next_sync_events()) == 0 + assert uhdev.get_evdev() is not None + + +class HIDTestUdevRule(object): + _instance = None + """ + A context-manager compatible class that sets up our udev rules file and + deletes it on context exit. + + This class is tailored to our test setup: it only sets up the udev rule + on the **second** context and it cleans it up again on the last context + removed. This matches the expected pytest setup: we enter a context for + the session once, then once for each test (the first of which will + trigger the udev rule) and once the last test exited and the session + exited, we clean up after ourselves. + """ + + def __init__(self): + self.refs = 0 + self.rulesfile = None + + def __enter__(self): + self.refs += 1 + if self.refs == 2 and self.rulesfile is None: + self.create_udev_rule() + self.reload_udev_rules() + + def __exit__(self, exc_type, exc_value, traceback): + self.refs -= 1 + if self.refs == 0 and self.rulesfile: + os.remove(self.rulesfile.name) + self.reload_udev_rules() + + def reload_udev_rules(self): + import subprocess + + subprocess.run("udevadm control --reload-rules".split()) + subprocess.run("systemd-hwdb update".split()) + + def create_udev_rule(self): + import tempfile + + os.makedirs("/run/udev/rules.d", exist_ok=True) + with tempfile.NamedTemporaryFile( + prefix="91-uhid-test-device-REMOVEME-", + suffix=".rules", + mode="w+", + dir="/run/udev/rules.d", + delete=False, + ) as f: + f.write( + 'KERNELS=="*input*", ATTRS{name}=="*uhid test *", ENV{LIBINPUT_IGNORE_DEVICE}="1"\n' + ) + f.write( + 'KERNELS=="*input*", ATTRS{name}=="*uhid test * System Multi Axis", ENV{ID_INPUT_TOUCHSCREEN}="", ENV{ID_INPUT_SYSTEM_MULTIAXIS}="1"\n' + ) + self.rulesfile = f + + @classmethod + def instance(cls): + if not cls._instance: + cls._instance = HIDTestUdevRule() + return cls._instance diff --git a/tools/testing/selftests/hid/tests/conftest.py b/tools/testing/selftests/hid/tests/conftest.py new file mode 100644 index 000000000000..1361ec981db6 --- /dev/null +++ b/tools/testing/selftests/hid/tests/conftest.py @@ -0,0 +1,81 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. + +import platform +import pytest +import re +import resource +import subprocess +from .base import HIDTestUdevRule +from pathlib import Path + + +# See the comment in HIDTestUdevRule, this doesn't set up but it will clean +# up once the last test exited. +@pytest.fixture(autouse=True, scope="session") +def udev_rules_session_setup(): + with HIDTestUdevRule.instance(): + yield + + +@pytest.fixture(autouse=True, scope="session") +def setup_rlimit(): + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +@pytest.fixture(autouse=True, scope="session") +def start_udevd(pytestconfig): + if pytestconfig.getoption("udevd"): + import subprocess + + with subprocess.Popen("/usr/lib/systemd/systemd-udevd") as proc: + yield + proc.kill() + else: + yield + + +def pytest_configure(config): + config.addinivalue_line( + "markers", + "skip_if_uhdev(condition, message): mark test to skip if the condition on the uhdev device is met", + ) + + +# Generate the list of modules and modaliases +# for the tests that need to be parametrized with those +def pytest_generate_tests(metafunc): + if "usbVidPid" in metafunc.fixturenames: + modules = ( + Path("/lib/modules/") + / platform.uname().release + / "kernel" + / "drivers" + / "hid" + ) + + modalias_re = re.compile(r"alias:\s+hid:b0003g.*v([0-9a-fA-F]+)p([0-9a-fA-F]+)") + + params = [] + ids = [] + for module in modules.glob("*.ko"): + p = subprocess.run( + ["modinfo", module], capture_output=True, check=True, encoding="utf-8" + ) + for line in p.stdout.split("\n"): + m = modalias_re.match(line) + if m is not None: + vid, pid = m.groups() + vid = int(vid, 16) + pid = int(pid, 16) + params.append([module.name.replace(".ko", ""), vid, pid]) + ids.append(f"{module.name} {vid:04x}:{pid:04x}") + metafunc.parametrize("usbVidPid", params, ids=ids) + + +def pytest_addoption(parser): + parser.addoption("--udevd", action="store_true", default=False) diff --git a/tools/testing/selftests/hid/tests/test_hid_core.py b/tools/testing/selftests/hid/tests/test_hid_core.py new file mode 100644 index 000000000000..9a7fe40020d2 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_hid_core.py @@ -0,0 +1,154 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is for generic devices + +from . import base +import logging + +logger = logging.getLogger("hidtools.test.hid") + + +class TestCollectionOverflow(base.BaseTestCase.TestUhid): + """ + Test class to test re-allocation of the HID collection stack in + hid-core.c. + """ + + def create_device(self): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # .Usage Page (Generic Desktop) + 0x09, 0x02, # .Usage (Mouse) + 0xa1, 0x01, # .Collection (Application) + 0x09, 0x02, # ..Usage (Mouse) + 0xa1, 0x02, # ..Collection (Logical) + 0x09, 0x01, # ...Usage (Pointer) + 0xa1, 0x00, # ...Collection (Physical) + 0x05, 0x09, # ....Usage Page (Button) + 0x19, 0x01, # ....Usage Minimum (1) + 0x29, 0x03, # ....Usage Maximum (3) + 0x15, 0x00, # ....Logical Minimum (0) + 0x25, 0x01, # ....Logical Maximum (1) + 0x75, 0x01, # ....Report Size (1) + 0x95, 0x03, # ....Report Count (3) + 0x81, 0x02, # ....Input (Data,Var,Abs) + 0x75, 0x05, # ....Report Size (5) + 0x95, 0x01, # ....Report Count (1) + 0x81, 0x03, # ....Input (Cnst,Var,Abs) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0x05, 0x01, # .....Usage Page (Generic Desktop) + 0x09, 0x30, # .....Usage (X) + 0x09, 0x31, # .....Usage (Y) + 0x15, 0x81, # .....Logical Minimum (-127) + 0x25, 0x7f, # .....Logical Maximum (127) + 0x75, 0x08, # .....Report Size (8) + 0x95, 0x02, # .....Report Count (2) + 0x81, 0x06, # .....Input (Data,Var,Rel) + 0xa1, 0x02, # ...Collection (Logical) + 0x85, 0x12, # ....Report ID (18) + 0x09, 0x48, # ....Usage (Resolution Multiplier) + 0x95, 0x01, # ....Report Count (1) + 0x75, 0x02, # ....Report Size (2) + 0x15, 0x00, # ....Logical Minimum (0) + 0x25, 0x01, # ....Logical Maximum (1) + 0x35, 0x01, # ....Physical Minimum (1) + 0x45, 0x0c, # ....Physical Maximum (12) + 0xb1, 0x02, # ....Feature (Data,Var,Abs) + 0x85, 0x1a, # ....Report ID (26) + 0x09, 0x38, # ....Usage (Wheel) + 0x35, 0x00, # ....Physical Minimum (0) + 0x45, 0x00, # ....Physical Maximum (0) + 0x95, 0x01, # ....Report Count (1) + 0x75, 0x10, # ....Report Size (16) + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) + 0x81, 0x06, # ....Input (Data,Var,Rel) + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ..End Collection + 0xc0, # .End Collection + ] + # fmt: on + return base.UHIDTestDevice( + name=None, rdesc=report_descriptor, application="Mouse" + ) + + def test_rdesc(self): + """ + This test can only check for negatives. If the kernel crashes, you + know why. If this test passes, either the bug isn't present or just + didn't get triggered. No way to know. + + For an explanation, see kernel patch + HID: core: replace the collection tree pointers with indices + """ + pass diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh index 6346b0620dba..681b906b4853 100755 --- a/tools/testing/selftests/hid/vmtest.sh +++ b/tools/testing/selftests/hid/vmtest.sh @@ -27,7 +27,7 @@ EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status" CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1" TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}" -DEFAULT_COMMAND="make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" +DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" usage() { -- cgit v1.2.3-58-ga151 From 8837469ac4de8e72ed972d7123d40218a7922f80 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 12:27:39 +0100 Subject: selftests: hid: import hid-tools hid-gamepad tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Candle Sun Cc: Jose Torreguitar Cc: Peter Hutterer Cc: Roderick Colenbrander Cc: Silvan Jegen Signed-off-by: Silvan Jegen Signed-off-by: Peter Hutterer Signed-off-by: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-gamepad.sh | 7 + tools/testing/selftests/hid/tests/test_gamepad.py | 209 ++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-gamepad.sh create mode 100644 tools/testing/selftests/hid/tests/test_gamepad.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index bdcb36d80c8c..d16a22477140 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -6,6 +6,7 @@ include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh +TEST_PROGS += hid-gamepad.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-gamepad.sh b/tools/testing/selftests/hid/hid-gamepad.sh new file mode 100755 index 000000000000..1ba00c0ca95f --- /dev/null +++ b/tools/testing/selftests/hid/hid-gamepad.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_gamepad.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_gamepad.py b/tools/testing/selftests/hid/tests/test_gamepad.py new file mode 100644 index 000000000000..26c74040b796 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_gamepad.py @@ -0,0 +1,209 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019 Benjamin Tissoires +# Copyright (c) 2019 Red Hat, Inc. +# + +from . import base +import libevdev +import pytest + +from hidtools.device.base_gamepad import AsusGamepad, SaitekGamepad + +import logging + +logger = logging.getLogger("hidtools.test.gamepad") + + +class BaseTest: + class TestGamepad(base.BaseTestCase.TestUhid): + @pytest.fixture(autouse=True) + def send_initial_state(self): + """send an empty report to initialize the axes""" + uhdev = self.uhdev + + r = uhdev.event() + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + def assert_button(self, button): + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + buttons = {} + key = libevdev.evbit(uhdev.buttons_map[button]) + + buttons[button] = True + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key] == 1 + + buttons[button] = False + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key] == 0 + + def test_buttons(self): + """check for button reliability.""" + uhdev = self.uhdev + + for b in uhdev.buttons: + self.assert_button(b) + + def test_dual_buttons(self): + """check for button reliability when pressing 2 buttons""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + # can change intended b1 b2 values + b1 = uhdev.buttons[0] + key1 = libevdev.evbit(uhdev.buttons_map[b1]) + b2 = uhdev.buttons[1] + key2 = libevdev.evbit(uhdev.buttons_map[b2]) + + buttons = {b1: True, b2: True} + r = uhdev.event(buttons=buttons) + expected_event0 = libevdev.InputEvent(key1, 1) + expected_event1 = libevdev.InputEvent(key2, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn( + (syn_event, expected_event0, expected_event1), events + ) + assert evdev.value[key1] == 1 + assert evdev.value[key2] == 1 + + buttons = {b1: False, b2: None} + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key1, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key1] == 0 + assert evdev.value[key2] == 1 + + buttons = {b1: None, b2: False} + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key2, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key1] == 0 + assert evdev.value[key2] == 0 + + def _get_libevdev_abs_events(self, which): + """Returns which ABS_* evdev axes are expected for the given stick""" + abs_map = self.uhdev.axes_map[which] + + x = abs_map["x"].evdev + y = abs_map["y"].evdev + + assert x + assert y + + return x, y + + def _test_joystick_press(self, which, data): + uhdev = self.uhdev + + libevdev_axes = self._get_libevdev_abs_events(which) + + r = None + if which == "left_stick": + r = uhdev.event(left=data) + else: + r = uhdev.event(right=data) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + for i, d in enumerate(data): + if d is not None and d != 127: + assert libevdev.InputEvent(libevdev_axes[i], d) in events + else: + assert libevdev.InputEvent(libevdev_axes[i]) not in events + + def test_left_joystick_press_left(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (63, None)) + self._test_joystick_press("left_stick", (0, 127)) + + def test_left_joystick_press_right(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (191, 127)) + self._test_joystick_press("left_stick", (255, None)) + + def test_left_joystick_press_up(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (None, 63)) + self._test_joystick_press("left_stick", (127, 0)) + + def test_left_joystick_press_down(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (127, 191)) + self._test_joystick_press("left_stick", (None, 255)) + + def test_right_joystick_press_left(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (63, None)) + self._test_joystick_press("right_stick", (0, 127)) + + def test_right_joystick_press_right(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (191, 127)) + self._test_joystick_press("right_stick", (255, None)) + + def test_right_joystick_press_up(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (None, 63)) + self._test_joystick_press("right_stick", (127, 0)) + + def test_right_joystick_press_down(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (127, 191)) + self._test_joystick_press("right_stick", (None, 255)) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Hat switch" not in uhdev.fields, + "Device not compatible, missing Hat switch usage", + ) + @pytest.mark.parametrize( + "hat_value,expected_evdev,evdev_value", + [ + (0, "ABS_HAT0Y", -1), + (2, "ABS_HAT0X", 1), + (4, "ABS_HAT0Y", 1), + (6, "ABS_HAT0X", -1), + ], + ) + def test_hat_switch(self, hat_value, expected_evdev, evdev_value): + uhdev = self.uhdev + + r = uhdev.event(hat_switch=hat_value) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert ( + libevdev.InputEvent( + libevdev.evbit("EV_ABS", expected_evdev), evdev_value + ) + in events + ) + + +class TestSaitekGamepad(BaseTest.TestGamepad): + def create_device(self): + return SaitekGamepad() + + +class TestAsusGamepad(BaseTest.TestGamepad): + def create_device(self): + return AsusGamepad() -- cgit v1.2.3-58-ga151 From b2c4944e16744aee54e312b3be9c044130297f50 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 13:23:23 +0100 Subject: selftests: hid: import hid-tools hid-keyboards tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Nicolas Saenz Julienne Cc: Peter Hutterer Cc: Roderick Colenbrander Signed-off-by: Peter Hutterer Signed-off-by: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-keyboard.sh | 7 + tools/testing/selftests/hid/tests/test_keyboard.py | 485 +++++++++++++++++++++ 3 files changed, 493 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-keyboard.sh create mode 100644 tools/testing/selftests/hid/tests/test_keyboard.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index d16a22477140..181a594ffe92 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -7,6 +7,7 @@ include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh TEST_PROGS += hid-gamepad.sh +TEST_PROGS += hid-keyboard.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-keyboard.sh b/tools/testing/selftests/hid/hid-keyboard.sh new file mode 100755 index 000000000000..55368f17d1d5 --- /dev/null +++ b/tools/testing/selftests/hid/hid-keyboard.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_keyboard.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_keyboard.py b/tools/testing/selftests/hid/tests/test_keyboard.py new file mode 100644 index 000000000000..b3b2bdbf63b7 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_keyboard.py @@ -0,0 +1,485 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018 Benjamin Tissoires +# Copyright (c) 2018 Red Hat, Inc. +# + +from . import base +import hidtools.hid +import libevdev +import logging + +logger = logging.getLogger("hidtools.test.keyboard") + + +class InvalidHIDCommunication(Exception): + pass + + +class KeyboardData(object): + pass + + +class BaseKeyboard(base.UHIDTestDevice): + def __init__(self, rdesc, name=None, input_info=None): + assert rdesc is not None + super().__init__(name, "Key", input_info=input_info, rdesc=rdesc) + self.keystates = {} + + def _update_key_state(self, keys): + """ + Update the internal state of keys with the new state given. + + :param key: a tuple of chars for the currently pressed keys. + """ + # First remove the already released keys + unused_keys = [k for k, v in self.keystates.items() if not v] + for key in unused_keys: + del self.keystates[key] + + # self.keystates contains now the list of currently pressed keys, + # release them... + for key in self.keystates.keys(): + self.keystates[key] = False + + # ...and press those that are in parameter + for key in keys: + self.keystates[key] = True + + def _create_report_data(self): + keyboard = KeyboardData() + for key, value in self.keystates.items(): + key = key.replace(" ", "").lower() + setattr(keyboard, key, value) + return keyboard + + def create_array_report(self, keys, reportID=None, application=None): + """ + Return an input report for this device. + + :param keys: a tuple of chars for the pressed keys. The class maintains + the list of currently pressed keys, so to release a key, the caller + needs to call again this function without the key in this tuple. + :param reportID: the numeric report ID for this report, if needed + """ + self._update_key_state(keys) + reportID = reportID or self.default_reportID + + keyboard = self._create_report_data() + return self.create_report(keyboard, reportID=reportID, application=application) + + def event(self, keys, reportID=None, application=None): + """ + Send an input event on the default report ID. + + :param keys: a tuple of chars for the pressed keys. The class maintains + the list of currently pressed keys, so to release a key, the caller + needs to call again this function without the key in this tuple. + """ + r = self.create_array_report(keys, reportID, application) + self.call_input_event(r) + return [r] + + +class PlainKeyboard(BaseKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x01, # .Report ID (1) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x19, 0x00, # .Usage Minimum (0) + 0x29, 0x97, # .Usage Maximum (151) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x98, # .Report Count (152) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + self.default_reportID = 1 + + +class ArrayKeyboard(BaseKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x95, 0x06, # .Report Count (6) + 0x75, 0x08, # .Report Size (8) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xa4, 0x00, # .Logical Maximum (164) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0x00, # .Usage Minimum (0) + 0x29, 0xa4, # .Usage Maximum (164) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + + def _create_report_data(self): + data = KeyboardData() + array = [] + + hut = hidtools.hut.HUT + + # strip modifiers from the array + for k, v in self.keystates.items(): + # we ignore depressed keys + if not v: + continue + + usage = hut[0x07].from_name[k].usage + if usage >= 224 and usage <= 231: + # modifier + setattr(data, k.lower(), 1) + else: + array.append(k) + + # if array length is bigger than 6, report ErrorRollOver + if len(array) > 6: + array = ["ErrorRollOver"] * 6 + + data.keyboard = array + return data + + +class LEDKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x95, 0x01, # .Report Count (1) + 0x75, 0x08, # .Report Size (8) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x95, 0x05, # .Report Count (5) + 0x75, 0x01, # .Report Size (1) + 0x05, 0x08, # .Usage Page (LEDs) + 0x19, 0x01, # .Usage Minimum (1) + 0x29, 0x05, # .Usage Maximum (5) + 0x91, 0x02, # .Output (Data,Var,Abs) + 0x95, 0x01, # .Report Count (1) + 0x75, 0x03, # .Report Size (3) + 0x91, 0x01, # .Output (Cnst,Arr,Abs) + 0x95, 0x06, # .Report Count (6) + 0x75, 0x08, # .Report Size (8) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xa4, 0x00, # .Logical Maximum (164) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0x00, # .Usage Minimum (0) + 0x29, 0xa4, # .Usage Maximum (164) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + + +# Some Primax manufactured keyboards set the Usage Page after having defined +# some local Usages. It relies on the fact that the specification states that +# Usages are to be concatenated with Usage Pages upon finding a Main item (see +# 6.2.2.8). This test covers this case. +class PrimaxKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xA1, 0x01, # Collection (Application) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xE0, # .Usage Minimum (224) + 0x29, 0xE7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x01, # .Report Count (1) + 0x81, 0x01, # .Input (Data,Var,Abs) + 0x05, 0x08, # .Usage Page (LEDs) + 0x19, 0x01, # .Usage Minimum (1) + 0x29, 0x03, # .Usage Maximum (3) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x03, # .Report Count (3) + 0x91, 0x02, # .Output (Data,Var,Abs) + 0x95, 0x01, # .Report Count (1) + 0x75, 0x05, # .Report Size (5) + 0x91, 0x01, # .Output (Constant) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xFF, 0x00, # .Logical Maximum (255) + 0x19, 0x00, # .Usage Minimum (0) + 0x2A, 0xFF, 0x00, # .Usage Maximum (255) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x06, # .Report Count (6) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xC0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + + +class BaseTest: + class TestKeyboard(base.BaseTestCase.TestUhid): + def test_single_key(self): + """check for key reliability.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["a and A"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 0 + + def test_two_keys(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["a and A", "q and Q"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_Q] == 0 + + r = uhdev.event(["c and C"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_C] == 1 + + r = uhdev.event(["c and C", "Spacebar"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.KEY_C) not in events + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_C] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1 + + r = uhdev.event(["Spacebar"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE) not in events + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_C] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 0 + + def test_modifiers(self): + # ctrl-alt-del would be very nice :) + uhdev = self.uhdev + syn_event = self.syn_event + + r = uhdev.event(["LeftControl", "LeftShift", "= and +"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTCTRL, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTSHIFT, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_EQUAL, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + +class TestPlainKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return PlainKeyboard() + + def test_10_keys(self): + uhdev = self.uhdev + syn_event = self.syn_event + + r = uhdev.event( + [ + "1 and !", + "2 and @", + "3 and #", + "4 and $", + "5 and %", + "6 and ^", + "7 and &", + "8 and *", + "9 and (", + "0 and )", + ] + ) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + +class TestArrayKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return ArrayKeyboard() + + def test_10_keys(self): + uhdev = self.uhdev + syn_event = self.syn_event + + r = uhdev.event( + [ + "1 and !", + "2 and @", + "3 and #", + "4 and $", + "5 and %", + "6 and ^", + ] + ) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1)) + events = uhdev.next_sync_events() + + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + # ErrRollOver + r = uhdev.event( + [ + "1 and !", + "2 and @", + "3 and #", + "4 and $", + "5 and %", + "6 and ^", + "7 and &", + "8 and *", + "9 and (", + "0 and )", + ] + ) + events = uhdev.next_sync_events() + + self.debug_reports(r, uhdev, events) + + assert len(events) == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + +class TestLEDKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return LEDKeyboard() + + +class TestPrimaxKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return PrimaxKeyboard() -- cgit v1.2.3-58-ga151 From 356888cb08890a61f4d4565c5950c070feafada1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 13:26:06 +0100 Subject: selftests: hid: import hid-tools hid-mouse tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Cc: Roderick Colenbrander Signed-off-by: Peter Hutterer Signed-off-by: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-mouse.sh | 7 + tools/testing/selftests/hid/tests/test_mouse.py | 977 ++++++++++++++++++++++++ 3 files changed, 985 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-mouse.sh create mode 100644 tools/testing/selftests/hid/tests/test_mouse.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 181a594ffe92..00b2cc25e24c 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -8,6 +8,7 @@ include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh TEST_PROGS += hid-gamepad.sh TEST_PROGS += hid-keyboard.sh +TEST_PROGS += hid-mouse.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-mouse.sh b/tools/testing/selftests/hid/hid-mouse.sh new file mode 100755 index 000000000000..7b4ad4f646f7 --- /dev/null +++ b/tools/testing/selftests/hid/hid-mouse.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_mouse.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_mouse.py b/tools/testing/selftests/hid/tests/test_mouse.py new file mode 100644 index 000000000000..fd2ba62e783a --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_mouse.py @@ -0,0 +1,977 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# + +from . import base +import hidtools.hid +from hidtools.util import BusType +import libevdev +import logging +import pytest + +logger = logging.getLogger("hidtools.test.mouse") + +# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6 +try: + libevdev.EV_REL.REL_WHEEL_HI_RES +except 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: + l, r, m = buttons + if l is not None: + self.left = l + if r is not None: + self.right = r + if m is not None: + self.middle = m + 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 ResolutionMultiplierHWheelMouse(TwoWheelMouse): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) 0 + 0x09, 0x02, # Usage (Mouse) 2 + 0xa1, 0x01, # Collection (Application) 4 + 0x05, 0x01, # .Usage Page (Generic Desktop) 6 + 0x09, 0x02, # .Usage (Mouse) 8 + 0xa1, 0x02, # .Collection (Logical) 10 + 0x85, 0x1a, # ..Report ID (26) 12 + 0x09, 0x01, # ..Usage (Pointer) 14 + 0xa1, 0x00, # ..Collection (Physical) 16 + 0x05, 0x09, # ...Usage Page (Button) 18 + 0x19, 0x01, # ...Usage Minimum (1) 20 + 0x29, 0x05, # ...Usage Maximum (5) 22 + 0x95, 0x05, # ...Report Count (5) 24 + 0x75, 0x01, # ...Report Size (1) 26 + 0x15, 0x00, # ...Logical Minimum (0) 28 + 0x25, 0x01, # ...Logical Maximum (1) 30 + 0x81, 0x02, # ...Input (Data,Var,Abs) 32 + 0x75, 0x03, # ...Report Size (3) 34 + 0x95, 0x01, # ...Report Count (1) 36 + 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38 + 0x05, 0x01, # ...Usage Page (Generic Desktop) 40 + 0x09, 0x30, # ...Usage (X) 42 + 0x09, 0x31, # ...Usage (Y) 44 + 0x95, 0x02, # ...Report Count (2) 46 + 0x75, 0x10, # ...Report Size (16) 48 + 0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50 + 0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53 + 0x81, 0x06, # ...Input (Data,Var,Rel) 56 + 0xa1, 0x02, # ...Collection (Logical) 58 + 0x85, 0x12, # ....Report ID (18) 60 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 62 + 0x95, 0x01, # ....Report Count (1) 64 + 0x75, 0x02, # ....Report Size (2) 66 + 0x15, 0x00, # ....Logical Minimum (0) 68 + 0x25, 0x01, # ....Logical Maximum (1) 70 + 0x35, 0x01, # ....Physical Minimum (1) 72 + 0x45, 0x0c, # ....Physical Maximum (12) 74 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 76 + 0x85, 0x1a, # ....Report ID (26) 78 + 0x09, 0x38, # ....Usage (Wheel) 80 + 0x35, 0x00, # ....Physical Minimum (0) 82 + 0x45, 0x00, # ....Physical Maximum (0) 84 + 0x95, 0x01, # ....Report Count (1) 86 + 0x75, 0x10, # ....Report Size (16) 88 + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90 + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93 + 0x81, 0x06, # ....Input (Data,Var,Rel) 96 + 0xc0, # ...End Collection 98 + 0xa1, 0x02, # ...Collection (Logical) 99 + 0x85, 0x12, # ....Report ID (18) 101 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 103 + 0x75, 0x02, # ....Report Size (2) 105 + 0x15, 0x00, # ....Logical Minimum (0) 107 + 0x25, 0x01, # ....Logical Maximum (1) 109 + 0x35, 0x01, # ....Physical Minimum (1) 111 + 0x45, 0x0c, # ....Physical Maximum (12) 113 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 115 + 0x35, 0x00, # ....Physical Minimum (0) 117 + 0x45, 0x00, # ....Physical Maximum (0) 119 + 0x75, 0x04, # ....Report Size (4) 121 + 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123 + 0x85, 0x1a, # ....Report ID (26) 125 + 0x05, 0x0c, # ....Usage Page (Consumer Devices) 127 + 0x95, 0x01, # ....Report Count (1) 129 + 0x75, 0x10, # ....Report Size (16) 131 + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133 + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136 + 0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139 + 0x81, 0x06, # ....Input (Data,Var,Rel) 142 + 0xc0, # ...End Collection 144 + 0xc0, # ..End Collection 145 + 0xc0, # .End Collection 146 + 0xc0, # End Collection 147 + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + self.default_reportID = 0x1A + + # Feature Report 12, multiplier Feature value must be set to 0b0101, + # i.e. 5. We should extract that from the descriptor instead + # of hardcoding it here, but meanwhile this will do. + self.set_feature_report = [0x12, 0x5] + + def set_report(self, req, rnum, rtype, data): + super().set_report(req, rnum, rtype, data) + + self.wheel_multiplier = 12 + self.hwheel_multiplier = 12 + + return 0 + + +class BaseTest: + class TestMouse(base.BaseTestCase.TestUhid): + def test_buttons(self): + """check for button reliability.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(0, 0, (None, True, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + + r = uhdev.event(0, 0, (None, False, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0 + + r = uhdev.event(0, 0, (None, None, True)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1 + + r = uhdev.event(0, 0, (None, None, False)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0 + + r = uhdev.event(0, 0, (True, None, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(0, 0, (False, None, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + r = uhdev.event(0, 0, (True, True, None)) + expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) + expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn( + (syn_event, expected_event0, expected_event1), events + ) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(0, 0, (False, None, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + r = uhdev.event(0, 0, (None, False, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + def test_relative(self): + """Check for relative events.""" + uhdev = self.uhdev + + syn_event = self.syn_event + + r = uhdev.event(0, -1) + expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents((syn_event, expected_event), events) + + r = uhdev.event(1, 0) + expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents((syn_event, expected_event), events) + + r = uhdev.event(-1, 2) + expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1) + expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents( + (syn_event, expected_event0, expected_event1), events + ) + + +class TestSimpleMouse(BaseTest.TestMouse): + def create_device(self): + return ButtonMouse() + + def test_rdesc(self): + """Check that the testsuite actually manages to format the + reports according to the report descriptors. + No kernel device is used here""" + uhdev = self.uhdev + + event = (0, 0, (None, None, None)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, 0, (None, True, None)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, 0, (True, True, None)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, 0, (False, False, False)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (1, 0, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (-1, 0, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (-5, 5, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (-127, 127, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, -128, (True, False, True)) + with pytest.raises(hidtools.hid.RangeError): + uhdev.create_report(*event) + + +class TestWheelMouse(BaseTest.TestMouse): + def create_device(self): + return WheelMouse() + + def is_wheel_highres(self, uhdev): + evdev = uhdev.get_evdev() + assert evdev.has(libevdev.EV_REL.REL_WHEEL) + return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES) + + def test_wheel(self): + uhdev = self.uhdev + + # check if the kernel is high res wheel compatible + high_res_wheel = self.is_wheel_highres(uhdev) + + syn_event = self.syn_event + # The Resolution Multiplier is applied to the HID reports, so we + # need to pre-multiply too. + mult = uhdev.wheel_multiplier + + r = uhdev.event(0, 0, wheels=1 * mult) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=-1 * mult) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(-1, 2, wheels=3 * mult) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestTwoWheelMouse(TestWheelMouse): + def create_device(self): + return TwoWheelMouse() + + def is_hwheel_highres(self, uhdev): + evdev = uhdev.get_evdev() + assert evdev.has(libevdev.EV_REL.REL_HWHEEL) + return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES) + + def test_ac_pan(self): + uhdev = self.uhdev + + # check if the kernel is high res wheel compatible + high_res_wheel = self.is_wheel_highres(uhdev) + high_res_hwheel = self.is_hwheel_highres(uhdev) + assert high_res_wheel == high_res_hwheel + + syn_event = self.syn_event + # The Resolution Multiplier is applied to the HID reports, so we + # need to pre-multiply too. + hmult = uhdev.hwheel_multiplier + vmult = uhdev.wheel_multiplier + + r = uhdev.event(0, 0, wheels=(0, 1 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1)) + if high_res_hwheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=(0, -1 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1)) + if high_res_hwheel: + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(-1, 2, wheels=(0, 3 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3)) + if high_res_hwheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestResolutionMultiplierMouse(TestTwoWheelMouse): + def create_device(self): + return ResolutionMultiplierMouse() + + def is_wheel_highres(self, uhdev): + high_res = super().is_wheel_highres(uhdev) + + if not high_res: + # the kernel doesn't seem to support the high res wheel mice, + # make sure we haven't triggered the feature + assert uhdev.wheel_multiplier == 1 + + return high_res + + def test_resolution_multiplier_wheel(self): + uhdev = self.uhdev + + if not self.is_wheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.wheel_multiplier > 1 + assert 120 % uhdev.wheel_multiplier == 0 + + def test_wheel_with_multiplier(self): + uhdev = self.uhdev + + if not self.is_wheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.wheel_multiplier > 1 + + syn_event = self.syn_event + mult = uhdev.wheel_multiplier + + r = uhdev.event(0, 0, wheels=1) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=-1) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2)) + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult) + ) + + for _ in range(mult - 1): + r = uhdev.event(1, -2, wheels=1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(1, -2, wheels=1) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestBadResolutionMultiplierMouse(TestTwoWheelMouse): + def create_device(self): + return BadResolutionMultiplierMouse() + + def is_wheel_highres(self, uhdev): + high_res = super().is_wheel_highres(uhdev) + + assert uhdev.wheel_multiplier == 1 + + return high_res + + def test_resolution_multiplier_wheel(self): + uhdev = self.uhdev + + assert uhdev.wheel_multiplier == 1 + + +class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse): + def create_device(self): + return ResolutionMultiplierHWheelMouse() + + def is_hwheel_highres(self, uhdev): + high_res = super().is_hwheel_highres(uhdev) + + if not high_res: + # the kernel doesn't seem to support the high res wheel mice, + # make sure we haven't triggered the feature + assert uhdev.hwheel_multiplier == 1 + + return high_res + + def test_resolution_multiplier_ac_pan(self): + uhdev = self.uhdev + + if not self.is_hwheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.hwheel_multiplier > 1 + assert 120 % uhdev.hwheel_multiplier == 0 + + def test_ac_pan_with_multiplier(self): + uhdev = self.uhdev + + if not self.is_hwheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.hwheel_multiplier > 1 + + syn_event = self.syn_event + hmult = uhdev.hwheel_multiplier + + r = uhdev.event(0, 0, wheels=(0, 1)) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=(0, -1)) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2)) + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult) + ) + + for _ in range(hmult - 1): + r = uhdev.event(1, -2, wheels=(0, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(1, -2, wheels=(0, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestMiMouse(TestWheelMouse): + def create_device(self): + return MIDongleMIWirelessMouse() + + def assertInputEvents(self, expected_events, effective_events): + # Buttons and x/y are spread over two HID reports, so we can get two + # event frames for this device. + remaining = self.assertInputEventsIn(expected_events, effective_events) + try: + remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0)) + except ValueError: + # If there's no SYN_REPORT in the list, continue and let the + # assert below print out the real error + pass + assert remaining == [] -- cgit v1.2.3-58-ga151 From 0bb3ed717d9f4f204643e3fd0791be2ddd8eaf4c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 13:28:45 +0100 Subject: selftests: hid: import hid-tools hid-multitouch and hid-tablets tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. There are a lot of multitouch tests, and the default timeout of 45 seconds is not big enough. Bump it to 200 seconds. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Cc: Kai-Heng Feng Cc: Roderick Colenbrander Cc: наб Cc: Blaž Hrastnik Signed-off-by: Ahelenia Ziemiańska Signed-off-by: Peter Hutterer Signed-off-by: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 2 + tools/testing/selftests/hid/config | 1 + tools/testing/selftests/hid/hid-multitouch.sh | 7 + tools/testing/selftests/hid/hid-tablet.sh | 7 + tools/testing/selftests/hid/settings | 3 + .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++ tools/testing/selftests/hid/tests/test_tablet.py | 872 ++++++++ 7 files changed, 2980 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-multitouch.sh create mode 100755 tools/testing/selftests/hid/hid-tablet.sh create mode 100644 tools/testing/selftests/hid/settings create mode 100644 tools/testing/selftests/hid/tests/test_multitouch.py create mode 100644 tools/testing/selftests/hid/tests/test_tablet.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 00b2cc25e24c..d8ad104c3863 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -9,6 +9,8 @@ TEST_PROGS := hid-core.sh TEST_PROGS += hid-gamepad.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh +TEST_PROGS += hid-multitouch.sh +TEST_PROGS += hid-tablet.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index 5b5cef445b54..d096d0bf74f3 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -20,3 +20,4 @@ CONFIG_HID=y CONFIG_HID_BPF=y CONFIG_INPUT_EVDEV=y CONFIG_UHID=y +CONFIG_HID_MULTITOUCH=y diff --git a/tools/testing/selftests/hid/hid-multitouch.sh b/tools/testing/selftests/hid/hid-multitouch.sh new file mode 100755 index 000000000000..d03a1ddbfb1f --- /dev/null +++ b/tools/testing/selftests/hid/hid-multitouch.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_multitouch.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/hid-tablet.sh b/tools/testing/selftests/hid/hid-tablet.sh new file mode 100755 index 000000000000..e86b3fedafd9 --- /dev/null +++ b/tools/testing/selftests/hid/hid-tablet.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_tablet.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/settings b/tools/testing/selftests/hid/settings new file mode 100644 index 000000000000..b3cbfc521b10 --- /dev/null +++ b/tools/testing/selftests/hid/settings @@ -0,0 +1,3 @@ +# HID tests can be long, so give a little bit more time +# to them +timeout=200 diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py new file mode 100644 index 000000000000..4265012231c6 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_multitouch.py @@ -0,0 +1,2088 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# + +from . import base +from hidtools.hut import HUT +from hidtools.util import BusType +import libevdev +import logging +import pytest +import sys +import time + +logger = logging.getLogger("hidtools.test.multitouch") + +KERNEL_MODULE = ("hid-multitouch", "hid_multitouch") + + +def BIT(x): + return 1 << x + + +mt_quirks = { + "NOT_SEEN_MEANS_UP": BIT(0), + "SLOT_IS_CONTACTID": BIT(1), + "CYPRESS": BIT(2), + "SLOT_IS_CONTACTNUMBER": BIT(3), + "ALWAYS_VALID": BIT(4), + "VALID_IS_INRANGE": BIT(5), + "VALID_IS_CONFIDENCE": BIT(6), + "CONFIDENCE": BIT(7), + "SLOT_IS_CONTACTID_MINUS_ONE": BIT(8), + "NO_AREA": BIT(9), + "IGNORE_DUPLICATES": BIT(10), + "HOVERING": BIT(11), + "CONTACT_CNT_ACCURATE": BIT(12), + "FORCE_GET_FEATURE": BIT(13), + "FIX_CONST_CONTACT_ID": BIT(14), + "TOUCH_SIZE_SCALING": BIT(15), + "STICKY_FINGERS": BIT(16), + "ASUS_CUSTOM_UP": BIT(17), + "WIN8_PTP_BUTTONS": BIT(18), + "SEPARATE_APP_REPORT": BIT(19), + "MT_QUIRK_FORCE_MULTI_INPUT": BIT(20), +} + + +class Data(object): + pass + + +class Touch(object): + def __init__(self, id, x, y): + self.contactid = id + self.x = x + self.y = y + self.cx = x + self.cy = y + self.tipswitch = True + self.confidence = True + self.tippressure = 15 + self.azimuth = 0 + self.inrange = True + self.width = 10 + self.height = 10 + + +class Pen(Touch): + def __init__(self, x, y): + super().__init__(0, x, y) + self.barrel = False + self.invert = False + self.eraser = False + self.x_tilt = False + self.y_tilt = False + self.twist = 0 + + +class Digitizer(base.UHIDTestDevice): + @classmethod + def msCertificationBlob(cls, reportID): + return f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID ({reportID}) + Usage Page (0xff00) + Usage (0xc5) + Logical Minimum (0) + Logical Maximum (255) + Report Size (8) + Report Count (256) + Feature (Data,Var,Abs) + End Collection + """ + + def __init__( + self, + name, + rdesc_str=None, + rdesc=None, + application="Touch Screen", + physical="Finger", + max_contacts=None, + input_info=(BusType.USB, 1, 2), + quirks=None, + ): + super().__init__(name, application, rdesc_str, rdesc, input_info) + self.scantime = 0 + self.quirks = quirks + if max_contacts is None: + self.max_contacts = sys.maxsize + for features in self.parsed_rdesc.feature_reports.values(): + for feature in features: + if feature.usage_name in ["Contact Max"]: + self.max_contacts = feature.logical_max + for inputs in self.parsed_rdesc.input_reports.values(): + for i in inputs: + if ( + i.usage_name in ["Contact Count"] + and i.logical_max > 0 + and self.max_contacts > i.logical_max + ): + self.max_contacts = i.logical_max + if self.max_contacts == sys.maxsize: + self.max_contacts = 1 + else: + self.max_contacts = max_contacts + self.physical = physical + self.cur_application = application + + for features in self.parsed_rdesc.feature_reports.values(): + for feature in features: + if feature.usage_name == "Inputmode": + self.cur_application = "Mouse" + + self.fields = [] + for r in self.parsed_rdesc.input_reports.values(): + if r.application_name == self.application: + physicals = [f.physical_name for f in r] + if self.physical not in physicals and None not in physicals: + continue + self.fields = [f.usage_name for f in r] + + @property + def touches_in_a_report(self): + return self.fields.count("Contact Id") + + def event(self, slots, global_data=None, contact_count=None, incr_scantime=True): + if incr_scantime: + self.scantime += 1 + rs = [] + # make sure we have only the required number of available slots + slots = slots[: self.max_contacts] + + if global_data is None: + global_data = Data() + if contact_count is None: + global_data.contactcount = len(slots) + else: + global_data.contactcount = contact_count + global_data.scantime = self.scantime + + while len(slots): + r = self.create_report( + application=self.cur_application, data=slots, global_data=global_data + ) + self.call_input_event(r) + rs.append(r) + global_data.contactcount = 0 + return rs + + def get_report(self, req, rnum, rtype): + if rtype != self.UHID_FEATURE_REPORT: + return (1, []) + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return (1, []) + + if "Contact Max" not in [f.usage_name for f in rdesc]: + return (1, []) + + self.contactmax = self.max_contacts + r = rdesc.create_report([self], None) + return (0, r) + + def set_report(self, req, rnum, rtype, data): + if rtype != self.UHID_FEATURE_REPORT: + return 1 + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return 1 + + if "Inputmode" not in [f.usage_name for f in rdesc]: + return 0 + + Inputmode_seen = False + for f in rdesc: + if "Inputmode" == f.usage_name: + values = f.get_values(data) + assert len(values) == 1 + value = values[0] + + if not Inputmode_seen: + Inputmode_seen = True + if value == 0: + self.cur_application = "Mouse" + elif value == 2: + self.cur_application = "Touch Screen" + elif value == 3: + self.cur_application = "Touch Pad" + else: + if value != 0: + # Elan bug where the device doesn't work properly + # if we set twice an Input Mode in the same Feature + self.cur_application = "Mouse" + + return 0 + + +class PTP(Digitizer): + def __init__( + self, + name, + type="Click Pad", + rdesc_str=None, + rdesc=None, + application="Touch Pad", + physical="Pointer", + max_contacts=None, + input_info=None, + ): + self.type = type.lower().replace(" ", "") + if self.type == "clickpad": + self.buttontype = 0 + else: # pressurepad + self.buttontype = 1 + self.clickpad_state = False + self.left_state = False + self.right_state = False + super().__init__( + name, rdesc_str, rdesc, application, physical, max_contacts, input_info + ) + + def event( + self, + slots=None, + click=None, + left=None, + right=None, + contact_count=None, + incr_scantime=True, + ): + # update our internal state + if click is not None: + self.clickpad_state = click + if left is not None: + self.left_state = left + if right is not None: + self.right_state = right + + # now create the global data + global_data = Data() + global_data.b1 = 1 if self.clickpad_state else 0 + global_data.b2 = 1 if self.left_state else 0 + global_data.b3 = 1 if self.right_state else 0 + + if slots is None: + slots = [Data()] + + return super().event(slots, global_data, contact_count, incr_scantime) + + +class MinWin8TSParallel(Digitizer): + def __init__(self, max_slots): + self.max_slots = max_slots + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Report Size (7) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + Usage Page (Digitizers) + Usage (Azimuth) + Logical Maximum (360) + Unit (SILinear: deg) + Report Size (16) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * self.max_slots} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({self.max_slots}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__(f"uhid test parallel {self.max_slots}", rdesc_str) + + +class MinWin8TSHybrid(Digitizer): + def __init__(self): + self.max_slots = 10 + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Report Size (7) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * 2} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({self.max_slots}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__("uhid test hybrid", rdesc_str) + + +class Win8TSConfidence(Digitizer): + def __init__(self, max_slots): + self.max_slots = max_slots + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Usage (Confidence) + Input (Data,Var,Abs) + Report Size (6) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + Usage Page (Digitizers) + Usage (Azimuth) + Logical Maximum (360) + Unit (SILinear: deg) + Report Size (16) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * self.max_slots} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({self.max_slots}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__(f"uhid test confidence {self.max_slots}", rdesc_str) + + +class SmartTechDigitizer(Digitizer): + def __init__(self, name, input_info): + super().__init__( + name, + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 05 81 03 05 01 15 00 26 ff 0f 55 0e 65 11 75 10 95 01 35 00 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 06 15 00 26 ff 00 a1 01 85 02 75 08 95 3f 09 00 82 02 01 95 3f 09 00 92 02 01 c0 05 0d 09 04 a1 01 85 05 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 75 08 95 01 15 00 25 0a 09 54 81 02 09 55 b1 02 c0 05 0d 09 0e a1 01 85 04 09 23 a1 02 15 00 25 02 75 08 95 02 09 52 09 53 b1 02 c0 c0 05 0d 09 04 a1 01 85 03 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 75 08 95 01 15 00 25 0a 09 54 81 02 09 55 b1 02 c0 05 0d 09 04 a1 01 85 06 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 06 81 03 95 01 75 10 65 11 55 0e 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 07 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 08 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 09 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 0a 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0", + input_info=input_info, + ) + + def create_report(self, data, global_data=None, reportID=None, application=None): + # this device has *a lot* of different reports, and most of them + # have the Touch Screen application. But the first one is a stylus + # report (as stated in the physical type), so we simply override + # the report ID to use what the device sends + return super().create_report(data, global_data=global_data, reportID=3) + + def match_evdev_rule(self, application, evdev): + # we need to select the correct evdev node, as the device has multiple + # Touch Screen application collections + if application != "Touch Screen": + return True + absinfo = evdev.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_X] + return absinfo is not None and absinfo.resolution == 3 + + +class BaseTest: + class TestMultitouch(base.BaseTestCase.TestUhid): + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + raise Exception("please reimplement me in subclasses") + + def get_slot(self, uhdev, t, default): + if uhdev.quirks is None: + return default + + if "SLOT_IS_CONTACTID" in uhdev.quirks: + return t.contactid + + if "SLOT_IS_CONTACTID_MINUS_ONE" in uhdev.quirks: + return t.contactid - 1 + + return default + + def test_creation(self): + """Make sure the device gets processed by the kernel and creates + the expected application input node. + + If this fail, there is something wrong in the device report + descriptors.""" + super().test_creation() + + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + # some sanity checking for the quirks + if uhdev.quirks is not None: + for q in uhdev.quirks: + assert q in mt_quirks + + assert evdev.num_slots == uhdev.max_contacts + + if uhdev.max_contacts > 1: + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + if uhdev.max_contacts > 2: + assert evdev.slots[2][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_required_usages(self): + """Make sure the device exports the correct required features and + inputs.""" + uhdev = self.uhdev + rdesc = uhdev.parsed_rdesc + for feature in rdesc.feature_reports.values(): + for field in feature: + page_id = field.usage >> 16 + value = field.usage & 0xFF + try: + if HUT[page_id][value] == "Contact Max": + assert HUT[page_id][field.application] in [ + "Touch Screen", + "Touch Pad", + "System Multi-Axis Controller", + ] + except KeyError: + pass + + try: + if HUT[page_id][value] == "Inputmode": + assert HUT[page_id][field.application] in [ + "Touch Screen", + "Touch Pad", + "Device Configuration", + ] + except KeyError: + pass + + def test_mt_single_touch(self): + """send a single touch in the first slot of the device, + and release it.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + slot = self.get_slot(uhdev, t0, 0) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + + t0.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t0.inrange = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_mt_dual_touch(self): + """Send 2 touches in the first 2 slots. + Make sure the kernel sees this as a dual touch. + Release and check + + Note: PTP will send here BTN_DOUBLETAP emulation""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + t1 = Touch(2, 150, 200) + + if uhdev.quirks is not None and ( + "SLOT_IS_CONTACTID" in uhdev.quirks + or "SLOT_IS_CONTACTNUMBER" in uhdev.quirks + ): + t1.contactid = 0 + + slot0 = self.get_slot(uhdev, t0, 0) + slot1 = self.get_slot(uhdev, t1, 1) + + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + r = uhdev.event([t0, t1]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events + ) + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events + ) + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + + t0.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t0.inrange = False + r = uhdev.event([t0, t1]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events + + t1.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t1.inrange = False + + if uhdev.quirks is not None and "SLOT_IS_CONTACTNUMBER" in uhdev.quirks: + r = uhdev.event([t0, t1]) + else: + r = uhdev.event([t1]) + + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.max_contacts <= 2, "Device not compatible" + ) + def test_mt_triple_tap(self): + """Send 3 touches in the first 3 slots. + Make sure the kernel sees this as a triple touch. + Release and check + + Note: PTP will send here BTN_TRIPLETAP emulation""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + t1 = Touch(2, 150, 200) + t2 = Touch(3, 250, 300) + r = uhdev.event([t0, t1, t2]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + slot0 = self.get_slot(uhdev, t0, 0) + slot1 = self.get_slot(uhdev, t1, 1) + slot2 = self.get_slot(uhdev, t2, 2) + + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 2 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_POSITION_X] == 250 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 300 + + t0.tipswitch = False + t1.tipswitch = False + t2.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t0.inrange = False + t1.inrange = False + t2.inrange = False + r = uhdev.event([t0, t1, t2]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.max_contacts <= 2, "Device not compatible" + ) + def test_mt_max_contact(self): + """send the maximum number of contact as reported by the device. + Make sure all contacts are forwarded and that there is no miss. + Release and check.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + touches = [ + Touch(i, (i + 3) * 20, (i + 3) * 20 + 5) + for i in range(uhdev.max_contacts) + ] + if ( + uhdev.quirks is not None + and "SLOT_IS_CONTACTID_MINUS_ONE" in uhdev.quirks + ): + for t in touches: + t.contactid += 1 + r = uhdev.event(touches) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + for i, t in enumerate(touches): + slot = self.get_slot(uhdev, t, i) + + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == i + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_X] == t.x + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_Y] == t.y + + for t in touches: + t.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t.inrange = False + + r = uhdev.event(touches) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + for i, t in enumerate(touches): + slot = self.get_slot(uhdev, t, i) + + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: ( + uhdev.touches_in_a_report == 1 + or uhdev.quirks is not None + and "CONTACT_CNT_ACCURATE" not in uhdev.quirks + ), + "Device not compatible, we can not trigger the conditions", + ) + def test_mt_contact_count_accurate(self): + """Test the MT_QUIRK_CONTACT_CNT_ACCURATE from the kernel. + A report should forward an accurate contact count and the kernel + should ignore any data provided after we have reached this + contact count.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + t1 = Touch(2, 150, 200) + + slot0 = self.get_slot(uhdev, t0, 0) + slot1 = self.get_slot(uhdev, t1, 1) + + r = uhdev.event([t0, t1], contact_count=1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TRACKING_ID, 0) in events + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + class TestWin8Multitouch(TestMultitouch): + def test_required_usages8(self): + """Make sure the device exports the correct required features and + inputs.""" + uhdev = self.uhdev + rdesc = uhdev.parsed_rdesc + for feature in rdesc.feature_reports.values(): + for field in feature: + page_id = field.usage >> 16 + value = field.usage & 0xFF + try: + if HUT[page_id][value] == "Inputmode": + assert HUT[field.application] not in ["Touch Screen"] + except KeyError: + pass + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.fields.count("X") == uhdev.touches_in_a_report, + "Device not compatible, we can not trigger the conditions", + ) + def test_mt_tx_cx(self): + """send a single touch in the first slot of the device, with + different values of Tx and Cx. Make sure the kernel reports Tx.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 5, 10) + t0.cx = 50 + t0.cy = 100 + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 5 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TOOL_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 10 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TOOL_Y] == 100 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "In Range" not in uhdev.fields, + "Device not compatible, missing In Range usage", + ) + def test_mt_inrange(self): + """Send one contact that has the InRange bit set before/after + tipswitch. + Kernel is supposed to mark the contact with a distance > 0 + when inrange is set but not tipswitch. + + This tests the hovering capability of devices (MT_QUIRK_HOVERING). + + Make sure the contact is only released from the kernel POV + when the inrange bit is set to 0.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 150, 200) + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TRACKING_ID, 0) in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_DISTANCE) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_DISTANCE] > 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + t0.tipswitch = True + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_DISTANCE, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_DISTANCE] == 0 + + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_DISTANCE) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_DISTANCE] > 0 + + t0.inrange = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_mt_duplicates(self): + """Test the MT_QUIRK_IGNORE_DUPLICATES from the kernel. + If a touch is reported more than once with the same Contact ID, + we should only handle the first touch. + + Note: this is not in MS spec, but the current kernel behaves + like that""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 5, 10) + t1 = Touch(1, 15, 20) + t2 = Touch(2, 50, 100) + + r = uhdev.event([t0, t1, t2], contact_count=2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TRACKING_ID, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 5 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 10 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + + def test_mt_release_miss(self): + """send a single touch in the first slot of the device, and + forget to release it. The kernel is supposed to release by itself + the touch in 100ms. + Make sure that we are dealing with a new touch by resending the + same touch after the timeout expired, and check that the kernel + considers it as a separate touch (different tracking ID)""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 5, 10) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + + time.sleep(0.2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Azimuth" not in uhdev.fields, + "Device not compatible, missing Azimuth usage", + ) + def test_mt_azimuth(self): + """Check for the azimtuh information bit. + When azimuth is presented by the device, it should be exported + as ABS_MT_ORIENTATION and the exported value should report a quarter + of circle.""" + uhdev = self.uhdev + + t0 = Touch(1, 5, 10) + t0.azimuth = 270 + + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + # orientation is clockwise, while Azimuth is counter clockwise + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_ORIENTATION, 90) in events + + class TestPTP(TestWin8Multitouch): + def test_ptp_buttons(self): + """check for button reliability. + There are 2 types of touchpads: the click pads and the pressure pads. + Each should reliably report the BTN_LEFT events. + """ + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + if uhdev.type == "clickpad": + r = uhdev.event(click=True) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(click=False) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + else: + r = uhdev.event(left=True) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(left=False) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + r = uhdev.event(right=True) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + + r = uhdev.event(right=False) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) in events + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Confidence" not in uhdev.fields, + "Device not compatible, missing Confidence usage", + ) + def test_ptp_confidence(self): + """Check for the validity of the confidence bit. + When a contact is marked as not confident, it should be detected + as a palm from the kernel POV and released. + + Note: if the kernel exports ABS_MT_TOOL_TYPE, it shouldn't release + the touch but instead convert it to ABS_MT_TOOL_PALM.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 150, 200) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + t0.confidence = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + if evdev.absinfo[libevdev.EV_ABS.ABS_MT_TOOL_TYPE] is not None: + # the kernel exports MT_TOOL_PALM + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TOOL_TYPE, 2) in events + ) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] != -1 + + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.touches_in_a_report >= uhdev.max_contacts, + "Device not compatible, we can not trigger the conditions", + ) + def test_ptp_non_touch_data(self): + """Some single finger hybrid touchpads might not provide the + button information in subsequent reports (only in the first report). + + Emulate this and make sure we do not release the buttons in the + middle of the event.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + touches = [Touch(i, i * 10, i * 10 + 5) for i in range(uhdev.max_contacts)] + contact_count = uhdev.max_contacts + incr_scantime = True + btn_state = True + events = None + while touches: + t = touches[: uhdev.touches_in_a_report] + touches = touches[uhdev.touches_in_a_report :] + r = uhdev.event( + t, + click=btn_state, + left=btn_state, + contact_count=contact_count, + incr_scantime=incr_scantime, + ) + contact_count = 0 + incr_scantime = False + btn_state = False + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + if touches: + assert len(events) == 0 + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) in events + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) not in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + +################################################################################ +# +# Windows 7 compatible devices +# +################################################################################ +class Test3m_0596_0500(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test 3m_0596_0500", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 00 00 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 01 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 10 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 85 12 09 55 95 01 75 08 15 00 25 0a b1 02 06 00 ff 15 00 26 ff 00 85 03 09 01 75 08 95 07 b1 02 85 04 09 01 75 08 95 17 b1 02 85 05 09 01 75 08 95 47 b1 02 85 06 09 01 75 08 95 07 b1 02 85 07 09 01 75 08 95 07 b1 02 85 08 09 01 75 08 95 07 b1 02 85 09 09 01 75 08 95 3f b1 02 c0", + input_info=(BusType.USB, 0x0596, 0x0500), + max_contacts=60, + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID", "TOUCH_SIZE_SCALING"), + ) + + +class Test3m_0596_0506(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test 3m_0596_0506", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 00 00 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 03 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 13 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 3c 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 02 81 03 05 0d 85 12 09 55 95 01 75 08 15 00 25 3c b1 02 06 00 ff 15 00 26 ff 00 85 03 09 01 75 08 95 07 b1 02 85 04 09 01 75 08 95 17 b1 02 85 05 09 01 75 08 95 47 b1 02 85 06 09 01 75 08 95 07 b1 02 85 73 09 01 75 08 95 07 b1 02 85 08 09 01 75 08 95 07 b1 02 85 09 09 01 75 08 95 3f b1 02 85 0f 09 01 75 08 96 07 02 b1 02 c0", + input_info=(BusType.USB, 0x0596, 0x0506), + max_contacts=60, + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID", "TOUCH_SIZE_SCALING"), + ) + + +class TestActionStar_2101_1011(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ActionStar_2101_1011", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4d 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4d 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x2101, 0x1011), + ) + + def test_mt_actionstar_inrange(self): + """Special sequence that might not be handled properly""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + # fmt: off + sequence = [ + # t0 = Touch(1, 6999, 2441) | t1 = Touch(2, 15227, 2026) + '01 ff 01 57 1b 89 09 ff 02 7b 3b ea 07 02', + # t0.xy = (6996, 2450) | t1.y = 2028 + '01 ff 01 54 1b 92 09 ff 02 7b 3b ec 07 02', + # t1.xy = (15233, 2040) | t0.tipswitch = False + '01 ff 02 81 3b f8 07 fe 01 54 1b 92 09 02', + # t1 | t0.inrange = False + '01 ff 02 81 3b f8 07 fc 01 54 1b 92 09 02', + ] + # fmt: on + + for num, r_str in enumerate(sequence): + r = [int(i, 16) for i in r_str.split()] + uhdev.call_input_event(r) + events = uhdev.next_sync_events() + self.debug_reports([r], uhdev) + for e in events: + print(e) + if num == 2: + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + +class TestAsus_computers_0486_0185(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test asus-computers_0486_0185", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 95 01 75 01 81 02 09 32 81 02 09 47 81 02 75 05 81 03 09 30 26 ff 00 75 08 81 02 09 51 25 02 81 02 26 96 0d 05 01 75 10 55 0d 65 33 09 30 35 00 46 fd 1d 81 02 09 31 46 60 11 81 02 c0 09 22 a1 02 05 0d 35 00 45 00 55 00 65 00 09 42 25 01 75 01 81 02 09 32 81 02 09 47 81 02 75 05 81 03 09 30 26 ff 00 75 08 81 02 09 51 25 02 81 02 26 96 0d 05 01 75 10 55 0d 65 33 09 30 46 fd 1d 81 02 09 31 46 60 11 81 02 c0 35 00 45 00 55 00 65 00 05 0d 09 54 75 08 25 02 81 02 85 08 09 55 b1 02 c0 09 0e a1 01 85 07 09 22 a1 00 09 52 25 0a b1 02 c0 05 0c 09 01 a1 01 85 06 09 01 26 ff 00 95 08 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 02 25 01 75 01 95 02 81 02 95 06 81 03 26 96 0d 05 01 75 10 95 01 55 0d 65 33 09 30 46 fd 1d 81 02 09 31 46 60 11 81 02 c0 c0 06 ff 01 09 01 a1 01 26 ff 00 35 00 45 00 55 00 65 00 85 05 75 08 95 3f 09 00 81 02 c0", + input_info=(BusType.USB, 0x0486, 0x0185), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID_MINUS_ONE"), + ) + + +class TestAtmel_03eb_201c(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_201c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x03EB, 0x201C), + ) + + +class TestAtmel_03eb_211c(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_211c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 37 81 02 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 46 56 0a 26 ff 0f 09 30 81 02 46 b2 05 26 ff 0f 09 31 81 02 05 0d 75 08 85 02 09 55 25 10 b1 02 c0 c0", + input_info=(BusType.USB, 0x03EB, 0x211C), + ) + + +class TestCando_2087_0a02(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cando_2087_0a02", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 0e 65 33 09 30 35 00 46 6d 03 81 02 46 ec 01 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 0e 65 33 09 30 35 00 46 6d 03 81 02 46 ec 01 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 02 81 02 85 02 09 55 b1 02 c0 06 00 ff 09 01 a1 01 85 a6 95 22 75 08 26 ff 00 15 00 09 01 81 02 85 a5 95 06 75 08 26 ff 00 15 00 09 01 91 02 c0", + input_info=(BusType.USB, 0x2087, 0x0A02), + ) + + +class TestCando_2087_0b03(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cando_2087_0b03", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 49 46 f2 03 81 02 09 31 26 ff 29 46 39 02 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 49 46 f2 03 81 02 09 31 26 ff 29 46 39 02 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x2087, 0x0B03), + ) + + +class TestCVTouch_1ff7_0013(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cvtouch_1ff7_0013", + rdesc="06 00 ff 09 00 a1 01 85 fd 06 00 ff 09 01 09 02 09 03 09 04 09 05 09 06 15 00 26 ff 00 75 08 95 06 81 02 85 fe 06 00 ff 09 01 09 02 09 03 09 04 15 00 26 ff 00 75 08 95 04 b1 02 c0 05 01 09 02 a1 01 09 01 a1 00 85 01 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 ff 7f 75 10 95 02 81 02 05 0d 09 33 15 00 26 ff 00 35 00 46 ff 00 75 08 95 01 81 02 05 01 09 38 15 81 25 7f 35 81 45 7f 95 01 81 06 c0 c0 06 00 ff 09 00 a1 01 85 fc 15 00 26 ff 00 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 06 00 ff 09 00 a1 01 85 fb 15 00 26 ff 00 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 85 03 09 55 15 00 25 02 b1 02 c0 09 0e a1 01 85 04 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x1FF7, 0x0013), + quirks=("NOT_SEEN_MEANS_UP",), + ) + + +class TestCvtouch_1ff7_0017(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cvtouch_1ff7_0017", + rdesc="06 00 ff 09 00 a1 01 85 fd 06 00 ff 09 01 09 02 09 03 09 04 09 05 09 06 15 00 26 ff 00 75 08 95 06 81 02 85 fe 06 00 ff 09 01 09 02 09 03 09 04 15 00 26 ff 00 75 08 95 04 b1 02 c0 05 01 09 02 a1 01 09 01 a1 00 85 01 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 03 05 01 09 30 09 31 15 00 26 ff 0f 35 00 46 ff 0f 75 10 95 02 81 02 09 00 15 00 25 ff 35 00 45 ff 75 08 95 01 81 02 09 38 15 81 25 7f 95 01 81 06 c0 c0 06 00 ff 09 00 a1 01 85 fc 15 00 25 ff 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 06 00 ff 09 00 a1 01 85 fb 15 00 25 ff 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 05 0d 09 54 95 01 75 08 81 02 85 03 09 55 25 02 b1 02 c0 09 0e a1 01 85 04 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x1FF7, 0x0017), + ) + + +class TestCypress_04b4_c001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cypress_04b4_c001", + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 04 a1 01 85 02 09 22 09 53 95 01 75 08 81 02 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 09 55 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x04B4, 0xC001), + ) + + +class TestData_modul_7374_1232(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test data-modul_7374_1232", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 37 81 02 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 46 d0 07 26 ff 0f 09 30 81 02 46 40 06 09 31 81 02 05 0d 75 08 85 02 09 55 25 10 b1 02 c0 c0", + input_info=(BusType.USB, 0x7374, 0x1232), + ) + + +class TestData_modul_7374_1252(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test data-modul_7374_1252", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 37 81 02 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 46 d0 07 26 ff 0f 09 30 81 02 46 40 06 09 31 81 02 05 0d 75 08 85 02 09 55 25 10 b1 02 c0 c0", + input_info=(BusType.USB, 0x7374, 0x1252), + ) + + +class TestE4_2219_044c(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test e4_2219_044c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 09 55 b1 02 c0 09 0e a1 01 85 02 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 05 01 09 38 15 81 25 7f 75 08 95 01 81 06 c0 c0", + input_info=(BusType.USB, 0x2219, 0x044C), + ) + + +class TestEgalax_capacitive_0eef_7224(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7224", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7224), + quirks=("SLOT_IS_CONTACTID", "ALWAYS_VALID"), + ) + + +class TestEgalax_capacitive_0eef_72fa(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_72fa", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 72 22 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 87 13 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 72 22 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 87 13 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x72FA), + quirks=("SLOT_IS_CONTACTID", "VALID_IS_INRANGE"), + ) + + +class TestEgalax_capacitive_0eef_7336(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7336", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 c1 20 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c2 18 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 c1 20 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c2 18 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7336), + ) + + +class TestEgalax_capacitive_0eef_7337(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7337", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 ae 17 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c3 0e 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 ae 17 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c3 0e 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7337), + ) + + +class TestEgalax_capacitive_0eef_7349(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7349", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7349), + quirks=("SLOT_IS_CONTACTID", "ALWAYS_VALID"), + ) + + +class TestEgalax_capacitive_0eef_73f4(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_73f4", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 96 4e 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 23 2c 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 96 4e 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 23 2c 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x73F4), + ) + + +class TestEgalax_capacitive_0eef_a001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_a001", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 23 28 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 11 19 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0xA001), + quirks=("SLOT_IS_CONTACTID", "VALID_IS_INRANGE"), + ) + + +class TestElo_touchsystems_04e7_0022(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test elo-touchsystems_04e7_0022", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 0e 65 33 09 30 35 00 46 ff 0f 81 02 46 ff 0f 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 46 ff 0f 09 31 81 02 c0 05 0d 09 54 25 10 95 01 75 08 81 02 85 08 09 55 25 02 b1 02 c0 09 0e a1 01 85 07 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 06 00 ff 09 55 85 80 15 00 26 ff 00 75 08 95 01 b1 82 c0 05 01 09 02 a1 01 85 54 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 15 00 26 ff 0f 75 10 95 01 81 02 09 31 75 10 95 01 81 02 09 3b 16 00 00 26 00 01 36 00 00 46 00 01 66 00 00 75 10 95 01 81 62 c0 c0", + input_info=(BusType.USB, 0x04E7, 0x0022), + ) + + +class TestElo_touchsystems_04e7_0080(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test elo-touchsystems_04e7_0080", + rdesc="05 0d 09 04 a1 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 05 0d 09 54 75 08 95 01 15 00 25 08 81 02 09 55 b1 02 c0", + input_info=(BusType.USB, 0x04E7, 0x0080), + ) + + +class TestFlatfrog_25b5_0002(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test flatfrog_25b5_0002", + rdesc="05 0d 09 04 a1 01 85 05 09 22 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 65 00 55 00 05 0d 55 0c 66 01 10 75 20 95 01 27 ff ff ff 7f 45 00 09 56 81 02 75 08 95 01 15 00 25 28 09 54 81 02 09 55 85 06 25 28 b1 02 c0 65 00 55 00 45 00 09 0e a1 01 85 03 09 23 a1 02 09 52 15 02 25 02 75 08 95 01 b1 02 09 53 15 00 25 0a 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x25B5, 0x0002), + quirks=("NOT_SEEN_MEANS_UP", "NO_AREA"), + max_contacts=40, + ) + + +class TestFocaltech_10c4_81b9(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test focaltech_10c4_81b9", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 85 02 09 55 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x10C4, 0x81B9), + quirks=("ALWAYS_VALID",), + max_contacts=5, + ) + + +class TestHanvon_20b3_0a18(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test hanvon_20b3_0a18", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x20B3, 0x0A18), + ) + + +class TestHuitoo_03f7_0003(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test huitoo_03f7_0003", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 09 55 b1 02 c0 09 0e a1 01 85 02 09 23 a1 02 09 52 09 53 15 00 25 10 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 15 00 26 ff 0f 35 00 46 ff 0f 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 3f 09 02 81 02 95 3f 09 02 91 02 c0", + input_info=(BusType.USB, 0x03F7, 0x0003), + ) + + +class TestIdeacom_1cb6_6650(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ideacom_1cb6_6650", + rdesc="05 0d 09 04 a1 01 85 0a 09 22 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 26 ff 1f 75 10 95 01 55 0d 65 33 09 31 35 00 46 61 13 81 02 09 30 46 73 22 81 02 05 0d 75 08 95 01 09 30 26 ff 00 81 02 09 51 81 02 85 0c 09 55 25 02 95 01 b1 02 c0 06 00 ff 85 02 09 01 75 08 95 07 b1 02 85 03 09 02 75 08 95 07 b1 02 85 04 09 03 75 08 95 07 b1 02 85 05 09 04 75 08 95 07 b1 02 85 06 09 05 75 08 96 27 00 b1 02 85 07 09 06 75 08 96 27 00 b1 02 85 08 09 07 75 08 95 07 b1 02 85 09 09 08 75 08 95 07 b1 02 85 0b 09 09 75 08 96 07 00 b1 02 85 0d 09 0a 75 08 96 27 00 b1 02 c0 09 0e a1 01 85 0e 09 52 09 53 95 07 b1 02 c0 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 75 06 95 01 81 01 05 01 09 31 09 30 15 00 27 ff 1f 00 00 75 10 95 02 81 02 c0 09 01 a1 02 15 00 26 ff 00 95 02 75 08 81 03 c0 c0", + input_info=(BusType.USB, 0x1CB6, 0x6650), + ) + + +class TestIdeacom_1cb6_6651(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ideacom_1cb6_6651", + rdesc="05 0d 09 04 a1 01 85 0a 09 22 a1 02 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 26 ff 1f 75 10 95 01 55 0d 65 33 09 31 35 00 46 39 13 81 02 09 30 46 24 22 81 02 05 0d 75 08 95 01 09 30 26 ff 00 81 02 09 51 81 02 85 0c 09 55 25 02 95 01 b1 02 c0 06 00 ff 85 02 09 01 75 08 95 07 b1 02 85 03 09 02 75 08 95 07 b1 02 85 04 09 03 75 08 95 07 b1 02 85 05 09 04 75 08 95 07 b1 02 85 06 09 05 75 08 95 1f b1 02 85 07 09 06 75 08 96 1f 00 b1 02 85 08 09 07 75 08 95 07 b1 02 85 09 09 08 75 08 95 07 b1 02 85 0b 09 09 75 08 95 07 b1 02 85 0d 09 0a 75 08 96 1f 00 b1 02 c0 09 0e a1 01 85 0e 09 52 09 53 95 07 b1 02 c0 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 75 06 95 01 81 01 05 01 09 31 09 30 15 00 27 ff 1f 00 00 75 10 95 02 81 02 c0 09 01 a1 02 15 00 26 ff 00 95 02 75 08 81 03 c0 c0", + input_info=(BusType.USB, 0x1CB6, 0x6651), + ) + + +class TestIkaist_2793_0001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ikaist_2793_0001", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 00 00 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 03 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 13 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 3c 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 02 81 03 05 0d 85 12 09 55 95 01 75 08 15 00 25 3c b1 02 06 00 ff 15 00 26 ff 00 85 1e 09 01 75 08 95 80 b1 02 85 1f 09 01 75 08 96 3f 01 b1 02 c0", + input_info=(BusType.USB, 0x2793, 0x0001), + ) + + +class TestIrmtouch_23c9_5666(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test irmtouch_23c9_5666", + rdesc="05 0d 09 04 a1 01 85 0a 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 0c 09 23 a1 02 09 52 15 00 25 06 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x23C9, 0x5666), + ) + + +class TestIrtouch_6615_0070(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test irtouch_6615_0070", + rdesc="05 01 09 02 a1 01 85 10 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 30 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 05 0d 09 54 15 00 26 02 00 75 08 95 01 81 02 85 03 09 55 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 05 0d 09 02 a1 01 85 20 09 20 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 85 01 06 00 ff 09 01 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x6615, 0x0070), + ) + + +class TestIrtouch_6615_0081(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test irtouch_6615_0081", + rdesc="05 0d 09 04 a1 01 85 30 09 22 09 00 15 00 26 ff 00 75 08 95 05 81 02 a1 00 05 0d 09 51 15 00 26 ff 00 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0e 65 13 35 00 46 b5 04 75 10 95 01 81 02 09 31 35 00 46 8a 03 81 02 09 32 35 00 46 8a 03 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 a1 00 05 0d 09 51 15 00 26 ff 00 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0e 65 13 35 00 46 b5 04 75 10 95 01 81 02 09 31 35 00 46 8a 03 81 02 09 32 35 00 46 8a 03 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 a1 00 05 0d 09 51 15 00 26 ff 00 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0e 65 13 35 00 46 b5 04 75 10 95 01 81 02 09 31 35 00 46 8a 03 81 02 09 32 35 00 46 8a 03 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 a1 00 05 0d 09 54 15 00 25 1f 75 05 95 01 81 02 09 00 15 00 25 07 75 03 95 01 81 02 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 09 55 85 03 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 06 00 ff 09 00 a1 01 09 02 a1 00 85 aa 09 06 15 00 26 ff 00 35 00 46 ff 00 75 08 95 3f b1 02 c0 c0 05 01 09 02 a1 01 85 10 09 01 a1 00 05 01 09 00 15 00 26 ff 00 75 08 95 05 81 02 09 30 09 31 09 32 15 00 26 ff 7f 75 10 95 03 81 02 05 09 19 01 29 08 15 00 25 01 95 08 75 01 81 02 09 00 15 00 26 ff 00 75 08 95 02 81 02 c0 c0 06 00 ff 09 00 a1 01 85 40 09 00 15 00 26 ff 00 75 08 95 2e 81 02 c0", + input_info=(BusType.USB, 0x6615, 0x0081), + ) + + +class TestLG_043e_9aa1(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lg_043e_9aa1", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 09 31 46 78 0a 26 38 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 25 0a 09 55 b1 02 c0 09 0e a1 01 85 03 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 04 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 75 10 95 01 15 00 26 7f 07 81 02 09 31 26 37 04 81 02 c0 c0 06 00 ff 09 01 a1 01 85 05 15 00 26 ff 00 75 08 95 19 09 01 b1 02 c0 05 14 09 2b a1 02 85 07 09 2b 15 00 25 0a 75 08 95 40 b1 02 09 4b 15 00 25 0a 75 08 95 02 91 02 c0 05 14 09 2c a1 02 85 08 09 2b 15 00 25 0a 75 08 95 05 81 02 09 4b 15 00 25 0a 75 08 95 47 91 02 c0", + input_info=(BusType.USB, 0x043E, 0x9AA1), + ) + + +class TestLG_043e_9aa3(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lg_043e_9aa3", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 09 31 46 78 0a 26 38 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 25 0a 09 55 b1 02 c0 09 0e a1 01 85 03 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 04 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 75 10 95 01 15 00 26 7f 07 81 02 09 31 26 37 04 81 02 c0 c0 06 00 ff 09 01 a1 01 85 05 15 00 26 ff 00 75 08 95 19 09 01 b1 02 c0 05 14 09 2b a1 02 85 07 09 2b 15 00 25 0a 75 08 95 40 b1 02 09 4b 15 00 25 0a 75 08 95 02 91 02 c0 05 14 09 2c a1 02 85 08 09 2b 15 00 25 0a 75 08 95 05 81 02 09 4b 15 00 25 0a 75 08 95 47 91 02 c0", + input_info=(BusType.USB, 0x043E, 0x9AA3), + ) + + +class TestLG_1fd2_0064(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lg_1fd2_0064", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 a1 00 05 01 26 80 07 75 10 55 0e 65 33 09 30 35 00 46 53 07 81 02 26 38 04 46 20 04 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 a1 00 05 01 26 80 07 75 10 55 0e 65 33 09 30 35 00 46 53 07 81 02 26 38 04 46 20 04 09 31 81 02 45 00 c0 c0 05 0d 09 54 95 01 75 08 81 02 85 08 09 55 95 01 25 02 b1 02 c0 09 0e a1 01 85 07 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 75 10 95 02 15 00 26 ff 7f 81 02 c0 c0", + input_info=(BusType.USB, 0x1FD2, 0x0064), + ) + + +class TestLumio_202e_0006(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lumio_202e_0006", + rdesc="05 0d 09 04 a1 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 05 0d 09 54 75 08 95 01 15 00 25 08 81 02 09 55 b1 02 c0", + input_info=(BusType.USB, 0x202E, 0x0006), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID_MINUS_ONE"), + ) + + +class TestLumio_202e_0007(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lumio_202e_0007", + rdesc="05 0d 09 04 a1 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 0a 81 03 05 01 26 ff 7f 65 11 55 0e 46 ba 0e 75 10 95 01 09 30 81 02 09 31 46 ea 0b 81 02 05 0d 09 51 75 10 95 01 81 02 09 55 15 00 25 08 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x202E, 0x0007), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID_MINUS_ONE"), + ) + + +class TestNexio_1870_0100(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test nexio_1870_0100", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 40 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x0100), + ) + + +class TestNexio_1870_010d(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test nexio_1870_010d", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x010D), + ) + + +class TestNexio_1870_0119(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test nexio_1870_0119", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x0119), + ) + + +class TestPenmount_14e1_3500(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test penmount_14e1_3500", + rdesc="05 0d 09 04 a1 01 09 22 a1 00 09 51 15 00 25 0f 75 04 95 01 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 81 01 05 01 75 10 95 01 09 30 26 ff 07 81 02 09 31 26 ff 07 81 02 05 0d 09 55 75 08 95 05 b1 02 c0 c0", + input_info=(BusType.USB, 0x14E1, 0x3500), + quirks=("VALID_IS_CONFIDENCE",), + max_contacts=10, + ) + + +class TestPixart_093a_8002(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test pixart_093a_8002", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 5a 14 26 ff 7f 09 30 81 22 46 72 0b 26 ff 7f 09 31 81 22 95 08 75 08 81 03 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 5a 14 26 ff 7f 81 02 09 31 46 72 0b 26 ff 7f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 5a 14 26 ff 7f 81 02 46 72 0b 26 ff 7f 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x093A, 0x8002), + quirks=("VALID_IS_INRANGE", "SLOT_IS_CONTACTNUMBER"), + ) + + +class TestPqlabs_1ef1_0001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test pqlabs_1ef1_0001", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 3f 81 02 c0 c0 05 8c 09 07 a1 01 85 11 09 02 15 00 26 ff 00 75 08 95 3f 81 02 85 10 09 10 91 02 c0", + input_info=(BusType.USB, 0x1EF1, 0x0001), + ) + + +class TestQuanta_0408_3000(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3000", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 e3 13 26 7f 07 81 02 09 31 46 2f 0b 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 e3 13 26 7f 07 81 02 46 2f 0b 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 06 00 ff 09 01 26 ff 00 75 08 95 2f 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 c0", + input_info=(BusType.USB, 0x0408, 0x3000), + ) + + +class TestQuanta_0408_3001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3001", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 09 31 46 78 0a 26 38 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 0f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 c0", + input_info=(BusType.USB, 0x0408, 0x3001), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID"), + ) + + +class TestQuanta_0408_3008_1(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3008_1", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 4c 11 26 7f 07 09 30 81 22 46 bb 09 26 37 04 09 31 81 22 95 08 75 08 81 03 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 4c 11 26 7f 07 81 02 09 31 46 bb 09 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 4c 11 26 7f 07 81 02 46 bb 09 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x0408, 0x3008), + ) + + +class TestQuanta_0408_3008(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3008", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 98 12 26 7f 07 09 30 81 22 46 78 0a 26 37 04 09 31 81 22 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 09 31 46 78 0a 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 46 78 0a 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x0408, 0x3008), + ) + + +class TestRafi_05bd_0107(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test rafi_05bd_0107", + rdesc="05 0d 09 04 a1 01 85 01 09 22 65 00 55 00 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 09 81 02 05 0d 85 02 95 01 75 08 09 55 25 0a b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 05 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 65 11 55 0f 09 30 26 ff 03 35 00 46 9c 01 75 10 95 01 81 02 09 31 26 ff 03 35 00 46 e7 00 81 02 c0 c0", + input_info=(BusType.USB, 0x05BD, 0x0107), + ) + + +class TestRndplus_2512_5003(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test rndplus_2512_5003", + rdesc="05 0d 09 04 a1 01 85 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 85 08 09 55 b1 02 c0 09 0e a1 01 85 07 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 16 00 00 26 ff 3f 36 00 00 46 ff 3f 66 00 00 75 10 95 02 81 62 c0 c0", + input_info=(BusType.USB, 0x2512, 0x5003), + ) + + +class TestRndplus_2512_5004(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test rndplus_2512_5004", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 85 05 09 55 b1 02 c0 09 0e a1 01 85 06 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 16 00 00 26 ff 3f 36 00 00 46 ff 3f 66 00 00 75 10 95 02 81 62 c0 c0 06 00 ff 09 01 a1 01 85 01 09 01 15 00 26 ff 00 75 08 95 3f 82 00 01 85 02 09 01 15 00 26 ff 00 75 08 95 3f 92 00 01 c0", + input_info=(BusType.USB, 0x2512, 0x5004), + ) + + +class TestSitronix_1403_5001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test sitronix_1403_5001", + rdesc="05 0d 09 04 a1 01 85 01 09 54 95 01 75 08 81 02 09 22 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 05 01 26 90 04 75 0c 95 01 55 0f 65 11 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 04 09 48 09 49 81 02 c0 85 02 09 55 26 ff 00 75 08 95 01 b1 02 09 04 15 00 25 ff 75 08 95 07 91 02 c0 09 0e a1 01 85 03 09 23 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x1403, 0x5001), + max_contacts=10, + ) + + +class TestSmart_0b8c_0092(BaseTest.TestMultitouch): + def create_device(self): + return SmartTechDigitizer( + "uhid test smart_0b8c_0092", input_info=(BusType.USB, 0x0B8C, 0x0092) + ) + + +class TestStantum_1f87_0002(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test stantum_1f87_0002", + rdesc="05 0d 09 04 a1 01 85 03 05 0d 09 54 95 01 75 08 81 02 06 00 ff 75 02 09 01 81 01 75 0e 09 02 81 02 05 0d 09 22 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 85 08 05 0d 09 55 95 01 75 08 25 0a b1 02 c0", + input_info=(BusType.USB, 0x1F87, 0x0002), + ) + + +class TestTopseed_1784_0016(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test topseed_1784_0016", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 04 75 10 55 00 65 00 09 30 35 00 46 ff 04 81 02 09 31 46 ff 04 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 09 55 b1 02 c0 05 0c 09 01 a1 01 85 03 a1 02 09 b5 15 00 25 01 75 01 95 01 81 02 09 b6 81 02 09 b7 81 02 09 cd 81 02 09 e2 81 02 09 e9 81 02 09 ea 81 02 05 01 09 82 81 02 c0 c0", + input_info=(BusType.USB, 0x1784, 0x0016), + max_contacts=2, + ) + + +class TestTpv_25aa_8883(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test tpv_25aa_8883", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 05 0d 09 32 95 01 75 01 81 02 95 01 75 05 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 98 12 26 7f 07 09 30 81 22 46 78 0a 26 37 04 09 31 81 22 35 00 45 00 15 81 25 7f 75 08 95 01 09 38 81 06 09 00 75 08 95 07 81 03 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 09 31 46 78 0a 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 46 78 0a 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x25AA, 0x8883), + ) + + +class TestTrs_star_238f_0001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test trs-star_238f_0001", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 95 01 81 03 09 37 95 01 81 03 95 01 81 03 15 00 25 0f 75 04 09 51 95 01 81 02 09 54 95 01 81 02 09 55 95 01 81 02 05 01 26 ff 03 15 00 75 10 65 00 09 30 95 01 81 02 09 31 81 02 c0 05 0d 09 0e 85 02 09 23 a1 02 15 00 25 0a 09 52 75 08 95 01 b1 02 09 53 95 01 b1 02 09 55 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x238F, 0x0001), + ) + + +class TestUnitec_227d_0103(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test unitec_227d_0103", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 03 09 55 25 05 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 53 15 00 25 05 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x227D, 0x0103), + ) + + +class TestZytronic_14c8_0005(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test zytronic_14c8_0005", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 95 01 81 02 95 06 81 01 05 01 26 00 10 75 10 95 01 65 00 09 30 81 02 09 31 46 00 10 81 02 05 0d 09 51 26 ff 00 75 08 95 01 81 02 c0 85 02 09 55 15 00 25 08 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 03 a1 02 09 23 09 52 09 53 15 00 25 08 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 15 00 26 00 10 35 00 46 00 10 65 00 75 10 95 02 81 62 c0 c0 06 00 ff 09 01 a1 01 85 05 09 00 15 00 26 ff 00 75 08 95 3f b1 02 c0 06 00 ff 09 01 a1 01 85 06 09 00 15 00 26 ff 00 75 08 95 3f 81 02 c0", + input_info=(BusType.USB, 0x14C8, 0x0005), + ) + + +class TestZytronic_14c8_0006(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test zytronic_14c8_0006", + rdesc="05 0d 09 04 a1 01 85 01 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 54 95 01 75 08 15 00 25 3c 81 02 05 0d 85 02 09 55 95 01 75 08 15 00 25 3c b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 15 00 26 00 10 35 00 46 00 10 65 00 75 10 95 02 81 62 c0 c0 06 00 ff 09 01 a1 01 85 05 09 00 15 00 26 ff 00 75 08 95 3f b1 02 c0 06 00 ff 09 01 a1 01 85 06 09 00 15 00 26 ff 00 75 08 95 3f 81 02 c0", + input_info=(BusType.USB, 0x14C8, 0x0006), + ) + + +################################################################################ +# +# Windows 8 compatible devices +# +################################################################################ + + +class TestMinWin8TSParallelTriple(BaseTest.TestWin8Multitouch): + def create_device(self): + return MinWin8TSParallel(3) + + +class TestMinWin8TSParallel(BaseTest.TestWin8Multitouch): + def create_device(self): + return MinWin8TSParallel(10) + + +class TestMinWin8TSHybrid(BaseTest.TestWin8Multitouch): + def create_device(self): + return MinWin8TSHybrid() + + +class TestWin8TSConfidence(BaseTest.TestWin8Multitouch): + def create_device(self): + return Win8TSConfidence(5) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Confidence" not in uhdev.fields, + "Device not compatible, missing Confidence usage", + ) + def test_mt_confidence_bad_release(self): + """Check for the validity of the confidence bit. + When a contact is marked as not confident, it should be detected + as a palm from the kernel POV and released. + + Note: if the kernel exports ABS_MT_TOOL_TYPE, it shouldn't release + the touch but instead convert it to ABS_MT_TOOL_PALM.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 150, 200) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + t0.confidence = False + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + if evdev.absinfo[libevdev.EV_ABS.ABS_MT_TOOL_TYPE] is not None: + # the kernel exports MT_TOOL_PALM + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TOOL_TYPE, 2) in events + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + +class TestElanXPS9360(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test ElanXPS9360", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class TestTouchpadXPS9360(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test TouchpadXPS9360", + max_contacts=5, + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 03 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 3d 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 3d 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 03 b1 02 85 0e 09 07 15 00 26 ff 00 75 08 95 01 b1 02 c0", + ) + + +class TestSurfaceBook2(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test SurfaceBook2", + max_contacts=5, + rdesc="05 01 09 06 A1 01 85 01 14 25 01 75 01 95 08 05 07 19 E0 29 E7 81 02 75 08 95 0A 18 29 91 26 FF 00 80 05 0C 0A C0 02 A1 02 1A C1 02 2A C6 02 95 06 B1 03 C0 05 08 19 01 29 03 75 01 95 03 25 01 91 02 95 05 91 01 C0 05 01 09 02 A1 01 85 02 05 09 19 01 29 05 81 02 95 01 75 03 81 03 15 81 25 7F 75 08 95 02 05 01 09 30 09 31 81 06 A1 02 09 48 14 25 01 35 01 45 10 75 02 95 01 A4 B1 02 09 38 15 81 25 7F 34 44 75 08 81 06 C0 A1 02 09 48 B4 B1 02 34 44 75 04 B1 03 05 0C 0A 38 02 15 81 25 7F 75 08 81 06 C0 C0 05 0C 09 01 A1 01 85 03 75 10 14 26 FF 03 18 2A FF 03 80 C0 06 05 FF 09 01 A1 01 85 0D 25 FF 95 02 75 08 09 20 81 02 09 22 91 02 15 81 25 7F 95 20 75 08 09 21 81 02 09 23 91 02 C0 09 02 A1 01 85 0C 14 25 FF 95 01 08 91 02 C0 05 0D 09 05 A1 01 85 04 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 44 54 64 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 44 54 64 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 44 54 64 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 C0 05 0D 55 0C 66 01 10 47 FF FF 00 00 27 FF FF 00 00 09 56 81 02 09 54 25 7F 75 08 81 02 05 09 09 01 25 01 75 01 81 02 95 07 81 03 05 0D 85 04 09 55 09 59 75 04 95 02 25 0F B1 02 06 00 FF 09 C6 85 05 14 25 08 75 08 95 01 B1 02 09 C7 26 FF 00 75 08 95 20 B1 02 C0 05 0D 09 0E A1 01 85 07 09 22 A1 02 09 52 14 25 0A 75 08 95 01 B1 02 C0 09 22 A0 85 08 09 57 09 58 75 01 95 02 25 01 B1 02 95 06 B1 03 C0 C0 06 07 FF 09 01 A1 01 85 0A 09 02 26 FF 00 75 08 95 14 91 02 85 09 09 03 91 02 85 0A 09 04 95 26 81 02 85 09 09 05 81 02 85 09 09 06 95 01 B1 02 85 0B 09 07 B1 02 C0 06 05 FF 09 04 A1 01 85 0E 09 31 91 02 09 31 81 03 09 30 91 02 09 30 81 02 95 39 09 32 92 02 01 09 32 82 02 01 C0 06 05 FF 09 50 A1 01 85 20 14 25 FF 75 08 95 3C 09 60 82 02 01 09 61 92 02 01 09 62 B2 02 01 85 21 09 63 82 02 01 09 64 92 02 01 09 65 B2 02 01 85 22 25 FF 75 20 95 04 19 66 29 69 81 02 19 6A 29 6D 91 02 19 6E 29 71 B1 02 85 23 19 72 29 75 81 02 19 76 29 79 91 02 19 7A 29 7D B1 02 85 24 19 7E 29 81 81 02 19 82 29 85 91 02 19 86 29 89 B1 02 85 25 19 8A 29 8D 81 02 19 8E 29 91 91 02 19 92 29 95 B1 02 85 26 19 96 29 99 81 02 19 9A 29 9D 91 02 19 9E 29 A1 B1 02 85 27 19 A2 29 A5 81 02 19 A6 29 A9 91 02 19 AA 29 AD B1 02 85 28 19 AE 29 B1 81 02 19 B2 29 B5 91 02 19 B6 29 B9 B1 02 85 29 19 BA 29 BD 81 02 19 BE 29 C1 91 02 19 C2 29 C5 B1 02 C0 06 00 FF 0A 00 F9 A1 01 85 32 75 10 95 02 14 27 FF FF 00 00 0A 01 F9 B1 02 75 20 95 01 25 FF 0A 02 F9 B1 02 75 08 95 08 26 FF 00 0A 03 F9 B2 02 01 95 10 0A 04 F9 B2 02 01 0A 05 F9 B2 02 01 95 01 75 10 27 FF FF 00 00 0A 06 F9 81 02 C0", + ) + + +class Test3m_0596_051c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test 3m_0596_051c", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 ff 7f 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 03 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 13 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 14 81 02 05 0d 55 0c 66 01 10 35 00 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 05 0d 09 55 85 12 15 00 25 14 75 08 95 01 b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 06 00 ff 15 00 26 ff 00 85 03 09 01 75 08 95 07 b1 02 85 04 09 01 75 08 95 17 b1 02 85 05 09 01 75 08 95 47 b1 02 85 06 09 01 75 08 95 07 b1 02 85 73 09 01 75 08 95 07 b1 02 85 08 09 01 75 08 95 07 b1 02 85 09 09 01 75 08 95 3f b1 02 85 0f 09 01 75 08 96 07 02 b1 02 c0", + ) + + +class Testadvanced_silicon_04e8_2084(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_04e8_2084", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_2306(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_2306", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_230a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_230a", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_231c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_231c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_2703(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_2703", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_270b(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_270b", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2575_0204(BaseTest.TestWin8Multitouch): + """found on the Dell Canvas 27""" + + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2575_0204", + rdesc="05 0d 09 04 a1 01 85 01 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 42 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 c0 05 01 09 0e a1 01 85 05 05 01 09 08 a1 00 09 30 55 0e 65 11 15 00 26 ff 7f 35 00 46 4f 17 75 10 95 01 81 42 09 31 46 1d 0d 81 42 06 00 ff 09 01 75 20 81 03 05 01 09 37 55 00 65 14 16 98 fe 26 68 01 36 98 fe 46 68 01 75 0f 81 06 05 09 09 01 65 00 15 00 25 01 35 00 45 00 75 01 81 02 05 0d 09 42 81 02 09 51 75 07 25 7f 81 02 05 0d 09 48 55 0e 65 11 15 00 26 ff 7f 35 00 46 ff 7f 75 10 81 02 09 49 81 02 09 3f 55 00 65 14 15 00 26 67 01 35 00 46 67 01 81 0a c0 65 00 35 00 45 00 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 05 75 08 09 54 81 02 85 47 09 55 25 05 b1 02 c0 06 00 ff 09 04 a1 01 85 f0 09 01 75 08 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 85 c0 09 01 95 03 b1 02 85 c2 09 01 95 0f b1 02 85 c4 09 01 95 3e b1 02 85 c5 09 01 95 7e b1 02 85 c6 09 01 95 fe b1 02 85 c8 09 01 96 fe 03 b1 02 85 0a 09 01 95 3f b1 02 c0", + ) + + +class Testadvanced_silicon_2619_5610(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2619_5610", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 c0", + ) + + +class Testatmel_03eb_8409(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_8409", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 00 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 46 18 06 26 77 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0", + ) + + +class Testatmel_03eb_840b(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_840b", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 00 0a 26 ff 0f 09 30 81 02 46 a0 05 26 ff 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0", + ) + + +class Testdell_044e_1220(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test dell_044e_1220", + type="pressurepad", + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 09 38 95 01 81 06 05 0c 0a 38 02 81 06 c0 c0 05 0d 09 05 a1 01 85 08 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 af 04 75 10 55 0e 65 11 09 30 35 00 46 e8 03 95 01 81 02 26 7b 02 46 12 02 09 31 81 02 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 05 0d 09 56 81 02 09 54 25 05 95 01 75 08 81 02 05 09 19 01 29 03 25 01 75 01 95 03 81 02 95 05 81 03 05 0d 85 09 09 55 75 08 95 01 25 05 b1 02 06 00 ff 85 0a 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 01 ff 09 01 a1 01 85 03 09 01 15 00 26 ff 00 95 1b 81 02 85 04 09 02 95 50 81 02 85 05 09 03 95 07 b1 02 85 06 09 04 81 02 c0 06 02 ff 09 01 a1 01 85 07 09 02 95 86 75 08 b1 02 c0 05 0d 09 0e a1 01 85 0b 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 0c 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0", + ) + + +class Testdell_06cb_75db(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test dell_06cb_75db", + max_contacts=3, + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 03 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c8 04 75 10 55 0e 65 11 09 30 35 00 46 fb 03 95 01 81 02 46 6c 02 26 e8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c8 04 75 10 55 0e 65 11 09 30 35 00 46 fb 03 95 01 81 02 46 6c 02 26 e8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c8 04 75 10 55 0e 65 11 09 30 35 00 46 fb 03 95 01 81 02 46 6c 02 26 e8 02 09 31 81 02 05 0d c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 1a 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 1a 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 01 b1 02 85 0e 09 07 15 00 26 ff 00 75 08 95 01 b1 02 c0", + ) + + +class Testegalax_capacitive_0eef_790a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test egalax_capacitive_0eef_790a", + max_contacts=10, + rdesc="05 0d 09 04 a1 01 85 06 05 0d 09 54 75 08 15 00 25 0c 95 01 81 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 17 00 00 00 00 27 ff ff ff 7f 75 20 95 01 55 00 65 00 09 56 81 02 09 55 09 53 75 08 95 02 26 ff 00 b1 02 06 00 ff 09 c5 85 07 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 01 a1 01 85 01 09 01 a1 02 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 0e a1 01 85 05 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + ) + + +class Testelan_04f3_000a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_000a", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_000c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_000c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_010c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_010c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_0125(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_0125", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_016f(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_016f", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_0732(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_0732", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff 00 00 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 25 ff 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0", + ) + + +class Testelan_04f3_200a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_200a", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff 00 00 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 0e 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0", + ) + + +class Testelan_04f3_300b(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test elan_04f3_300b", + max_contacts=3, + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06 05 0c 0a 38 02 95 01 81 06 75 08 95 03 81 03 c0 06 00 ff 85 0d 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0c 09 c6 96 76 02 75 08 b1 02 85 0b 09 c7 95 42 75 08 b1 02 09 01 85 5d 95 1f 75 08 81 06 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 02 25 02 09 51 81 02 75 01 95 04 81 03 05 01 15 00 26 a7 0c 75 10 55 0e 65 13 09 30 35 00 46 9d 01 95 01 81 02 46 25 01 26 2b 09 26 2b 09 09 31 81 02 05 0d 15 00 25 64 95 03 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 02 09 55 09 59 75 04 95 02 25 0f b1 02 85 07 09 60 75 01 95 01 15 00 25 01 b1 02 95 0f b1 03 06 00 ff 06 00 ff 85 06 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 03 09 22 a1 00 09 52 15 00 25 0a 75 08 95 02 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 15 00 75 01 95 02 25 03 b1 02 95 0e b1 03 c0 c0", + ) + + +class Testelan_04f3_3045(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test elan_04f3_3045", + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06 05 0c 0a 38 02 95 01 81 06 75 08 95 03 81 03 c0 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 75 01 95 02 81 03 95 01 75 04 25 0f 09 51 81 02 05 01 15 00 26 80 0c 75 10 55 0e 65 13 09 30 35 00 46 90 01 95 01 81 02 46 13 01 26 96 08 26 96 08 09 31 81 02 05 0d 15 00 25 64 95 03 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 09 c5 75 08 95 04 81 03 05 0d 85 02 09 55 09 59 75 04 95 02 25 0f b1 02 85 07 09 60 75 01 95 01 15 00 25 01 b1 02 95 0f b1 03 06 00 ff 06 00 ff 85 06 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 85 0d 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0c 09 c6 96 8a 02 75 08 b1 02 85 0b 09 c7 95 80 75 08 b1 02 c0 05 0d 09 0e a1 01 85 03 09 22 a1 00 09 52 15 00 25 0a 75 08 95 02 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 15 00 75 01 95 02 25 03 b1 02 95 0e b1 03 c0 c0", + ) + + +class Testelan_04f3_313a(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test elan_04f3_313a", + type="touchpad", + input_info=(BusType.I2C, 0x04F3, 0x313A), + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 75 08 95 05 81 03 c0 06 00 ff 09 01 85 0e 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0a 09 c6 15 00 26 ff 00 75 08 95 04 b1 02 c0 06 00 ff 09 01 a1 01 85 5c 09 01 95 0b 75 08 81 06 85 0d 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0c 09 c6 96 80 03 75 08 b1 02 85 0b 09 c7 95 82 75 08 b1 02 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 05 09 09 02 09 03 15 00 25 01 75 01 95 02 81 02 05 0d 95 01 75 04 25 0f 09 51 81 02 05 01 15 00 26 d7 0e 75 10 55 0d 65 11 09 30 35 00 46 44 2f 95 01 81 02 46 12 16 26 eb 06 26 eb 06 09 31 81 02 05 0d 15 00 25 64 95 03 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 25 01 75 01 95 08 81 03 09 c5 75 08 95 02 81 03 05 0d 85 02 09 55 09 59 75 04 95 02 25 0f b1 02 85 07 09 60 75 01 95 01 15 00 25 01 b1 02 95 0f b1 03 06 00 ff 06 00 ff 85 06 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 03 09 22 a1 00 09 52 15 00 25 0a 75 10 95 01 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 75 01 95 02 25 01 b1 02 95 0e b1 03 c0 c0 05 01 09 02 a1 01 85 2a 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 03 05 01 09 30 09 31 15 81 25 7f 35 81 45 7f 55 00 65 13 75 08 95 02 81 06 75 08 95 05 81 03 c0 c0", + ) + + +class Testelo_04e7_0080(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elo_04e7_0080", + rdesc="05 0d 09 04 a1 01 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 75 08 95 01 15 00 25 08 81 02 09 55 b1 02 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0", + ) + + +class Testilitek_222a_0015(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test ilitek_222a_0015", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 02 09 55 25 0a b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 03 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0", + ) + + +class Testilitek_222a_001c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test ilitek_222a_001c", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 02 09 55 25 0a b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 03 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0", + ) + + +class Testite_06cb_2968(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test ite_06cb_2968", + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 03 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 1b 04 75 10 55 0e 65 11 09 30 35 00 46 6c 03 95 01 81 02 46 db 01 26 3b 02 09 31 81 02 05 0d c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 1a 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 1a 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 01 b1 02 85 0e 09 07 15 00 26 ff 00 75 08 95 01 b1 02 c0", + max_contacts=5, + input_info=(0x3, 0x06CB, 0x2968), + ) + + +class Testn_trig_1b96_0c01(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0c01", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0c03(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0c03", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0f00(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0f00", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0f04(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0f04", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_1000(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_1000", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testsharp_04dd_9681(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test sharp_04dd_9681", + rdesc="06 00 ff 09 01 a1 01 75 08 26 ff 00 15 00 85 06 95 3f 09 01 91 02 85 05 95 3f 09 01 81 02 c0 05 0d 09 04 a1 01 85 81 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 56 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 81 02 09 54 95 01 75 08 15 00 25 0a 81 02 85 84 09 55 b1 02 85 87 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 09 0e a1 01 85 83 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 80 05 09 19 01 29 01 15 00 25 01 95 01 75 01 81 02 95 01 75 07 81 01 05 01 65 11 55 0f 09 30 26 80 07 35 00 46 66 00 75 10 95 01 81 02 09 31 26 38 04 35 00 46 4d 00 81 02 c0 c0", + ) + + +class Testsipodev_0603_0002(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test sipodev_0603_0002", + type="clickpad", + rdesc="05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 02 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 80 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 75 01 95 02 81 03 95 01 75 04 25 05 09 51 81 02 05 01 15 00 26 44 0a 75 0c 55 0e 65 11 09 30 35 00 46 ac 03 95 01 81 02 46 fe 01 26 34 05 75 0c 09 31 81 02 05 0d c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 0a 95 01 75 04 81 02 75 01 95 03 81 03 05 09 09 01 25 01 75 01 95 01 81 02 05 0d 85 0a 09 55 09 59 75 04 95 02 25 0f b1 02 85 0b 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 09 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 06 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 07 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 05 01 09 0c a1 01 85 08 15 00 25 01 09 c6 75 01 95 01 81 06 75 07 81 03 c0 05 01 09 80 a1 01 85 01 15 00 25 01 75 01 0a 81 00 0a 82 00 0a 83 00 95 03 81 06 95 05 81 01 c0 06 0c 00 09 01 a1 01 85 02 25 01 15 00 75 01 0a b5 00 0a b6 00 0a b7 00 0a cd 00 0a e2 00 0a a2 00 0a e9 00 0a ea 00 95 08 81 02 0a 83 01 0a 6f 00 0a 70 00 0a 88 01 0a 8a 01 0a 92 01 0a a8 02 0a 24 02 95 08 81 02 0a 21 02 0a 23 02 0a 96 01 0a 25 02 0a 26 02 0a 27 02 0a 23 02 0a b1 02 95 08 81 02 c0 06 00 ff 09 01 a1 01 85 05 15 00 26 ff 00 19 01 29 02 75 08 95 05 b1 02 c0", + ) + + +class Testsynaptics_06cb_1d10(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test synaptics_06cb_1d10", + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 75 08 95 02 15 81 25 7f 35 81 45 7f 55 0e 65 11 81 06 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 95 01 75 08 15 00 25 0f 81 02 85 08 09 55 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 3f 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 05 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 3d 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 01 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 01 b1 02 c0", + ) + + +class Testsynaptics_06cb_ce08(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test synaptics_06cb_ce08", + max_contacts=5, + physical="Vendor Usage 1", + input_info=(BusType.I2C, 0x06CB, 0xCE08), + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 01 09 02 a1 01 85 18 09 01 a1 00 05 09 19 01 29 03 46 00 00 15 00 25 01 75 01 95 03 81 02 95 05 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 06 00 ff 09 02 a1 01 85 20 09 01 a1 00 09 03 15 00 26 ff 00 35 00 46 ff 00 75 08 95 05 81 02 c0 c0 05 0d 09 05 a1 01 85 03 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 45 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 45 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 03 b1 02 85 0e 09 07 15 00 26 ff 00 75 08 95 01 b1 02 c0", + ) diff --git a/tools/testing/selftests/hid/tests/test_tablet.py b/tools/testing/selftests/hid/tests/test_tablet.py new file mode 100644 index 000000000000..303ffff9ee8d --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_tablet.py @@ -0,0 +1,872 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021 Benjamin Tissoires +# Copyright (c) 2021 Red Hat, Inc. +# + +from . import base +import copy +from enum import Enum +from hidtools.util import BusType +import libevdev +import logging +import pytest +from typing import Dict, Tuple + +logger = logging.getLogger("hidtools.test.tablet") + + +class PenState(Enum): + """Pen states according to Microsoft reference: + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + + PEN_IS_OUT_OF_RANGE = (False, None) + PEN_IS_IN_RANGE = (False, libevdev.EV_KEY.BTN_TOOL_PEN) + PEN_IS_IN_CONTACT = (True, libevdev.EV_KEY.BTN_TOOL_PEN) + PEN_IS_IN_RANGE_WITH_ERASING_INTENT = (False, libevdev.EV_KEY.BTN_TOOL_RUBBER) + PEN_IS_ERASING = (True, libevdev.EV_KEY.BTN_TOOL_RUBBER) + + def __init__(self, touch, tool): + self.touch = touch + self.tool = tool + + @classmethod + def from_evdev(cls, evdev) -> "PenState": + touch = bool(evdev.value[libevdev.EV_KEY.BTN_TOUCH]) + tool = None + if ( + evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] + and not evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN] + ): + tool = libevdev.EV_KEY.BTN_TOOL_RUBBER + elif ( + evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN] + and not evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] + ): + tool = libevdev.EV_KEY.BTN_TOOL_PEN + elif ( + evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN] + or evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] + ): + raise ValueError("2 tools are not allowed") + + return cls((touch, tool)) + + def apply(self, events) -> "PenState": + if libevdev.EV_SYN.SYN_REPORT in events: + raise ValueError("EV_SYN is in the event sequence") + touch = self.touch + touch_found = False + tool = self.tool + tool_found = False + + for ev in events: + if ev == libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH): + if touch_found: + raise ValueError(f"duplicated BTN_TOUCH in {events}") + touch_found = True + touch = bool(ev.value) + elif ev in ( + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_RUBBER), + ): + if tool_found: + raise ValueError(f"duplicated BTN_TOOL_* in {events}") + tool_found = True + if ev.value: + tool = ev.code + else: + tool = None + + new_state = PenState((touch, tool)) + assert ( + new_state in self.valid_transitions() + ), f"moving from {self} to {new_state} is forbidden" + + return new_state + + def valid_transitions(self) -> Tuple["PenState", ...]: + """Following the state machine in the URL above, with a couple of addition + for skipping the in-range state, due to historical reasons. + + Note that those transitions are from the evdev point of view, not HID""" + if self == PenState.PEN_IS_OUT_OF_RANGE: + return ( + PenState.PEN_IS_OUT_OF_RANGE, + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + ) + + if self == PenState.PEN_IS_IN_RANGE: + return ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + PenState.PEN_IS_IN_CONTACT, + ) + + if self == PenState.PEN_IS_IN_CONTACT: + return ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ) + + if self == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT: + return ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + PenState.PEN_IS_ERASING, + ) + + if self == PenState.PEN_IS_ERASING: + return ( + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ) + + return tuple() + + +class Data(object): + pass + + +class Pen(object): + def __init__(self, x, y): + self.x = x + self.y = y + self.tipswitch = False + self.tippressure = 15 + self.azimuth = 0 + self.inrange = False + self.width = 10 + self.height = 10 + self.barrelswitch = False + self.invert = False + self.eraser = False + self.x_tilt = 0 + self.y_tilt = 0 + self.twist = 0 + self._old_values = None + self.current_state = None + + def _restore(self): + if self._old_values is not None: + for i in [ + "x", + "y", + "tippressure", + "azimuth", + "width", + "height", + "twist", + "x_tilt", + "y_tilt", + ]: + setattr(self, i, getattr(self._old_values, i)) + + def move_to(self, state): + # fill in the previous values + if self.current_state == PenState.PEN_IS_OUT_OF_RANGE: + self._restore() + + print(f"\n *** pen is moving to {state} ***") + + if state == PenState.PEN_IS_OUT_OF_RANGE: + self._old_values = copy.copy(self) + self.x = 0 + self.y = 0 + self.tipswitch = False + self.tippressure = 0 + self.azimuth = 0 + self.inrange = False + self.width = 0 + self.height = 0 + self.invert = False + self.eraser = False + self.x_tilt = 0 + self.y_tilt = 0 + self.twist = 0 + elif state == PenState.PEN_IS_IN_RANGE: + self.tipswitch = False + self.inrange = True + self.invert = False + self.eraser = False + elif state == PenState.PEN_IS_IN_CONTACT: + self.tipswitch = True + self.inrange = True + self.invert = False + self.eraser = False + elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT: + self.tipswitch = False + self.inrange = True + self.invert = True + self.eraser = False + elif state == PenState.PEN_IS_ERASING: + self.tipswitch = False + self.inrange = True + self.invert = True + self.eraser = True + + self.current_state = state + + def __assert_axis(self, evdev, axis, value): + if ( + axis == libevdev.EV_KEY.BTN_TOOL_RUBBER + and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None + ): + return + + assert ( + evdev.value[axis] == value + ), f"assert evdev.value[{axis}] ({evdev.value[axis]}) != {value}" + + def assert_expected_input_events(self, evdev): + assert evdev.value[libevdev.EV_ABS.ABS_X] == self.x + assert evdev.value[libevdev.EV_ABS.ABS_Y] == self.y + assert self.current_state == PenState.from_evdev(evdev) + + @staticmethod + def legal_transitions() -> Dict[str, Tuple[PenState, ...]]: + """This is the first half of the Windows Pen Implementation state machine: + we don't have Invert nor Erase bits, so just move in/out-of-range or proximity. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "in-range": (PenState.PEN_IS_IN_RANGE,), + "in-range -> out-of-range": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "in-range -> touch": (PenState.PEN_IS_IN_RANGE, PenState.PEN_IS_IN_CONTACT), + "in-range -> touch -> release": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "in-range -> touch -> release -> out-of-range": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + def legal_transitions_with_invert() -> Dict[str, Tuple[PenState, ...]]: + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "hover-erasing": (PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,), + "hover-erasing -> out-of-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "hover-erasing -> erase": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + ), + "hover-erasing -> erase -> release": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "hover-erasing -> erase -> release -> out-of-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "hover-erasing -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_RANGE, + ), + "in-range -> hover-erasing": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + } + + @staticmethod + def tolerated_transitions() -> Dict[str, Tuple[PenState, ...]]: + """This is not adhering to the Windows Pen Implementation state machine + but we should expect the kernel to behave properly, mostly for historical + reasons.""" + return { + "direct-in-contact": (PenState.PEN_IS_IN_CONTACT,), + "direct-in-contact -> out-of-range": ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + def tolerated_transitions_with_invert() -> Dict[str, Tuple[PenState, ...]]: + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "direct-erase": (PenState.PEN_IS_ERASING,), + "direct-erase -> out-of-range": ( + PenState.PEN_IS_ERASING, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + def broken_transitions() -> Dict[str, Tuple[PenState, ...]]: + """Those tests are definitely not part of the Windows specification. + However, a half broken device might export those transitions. + For example, a pen that has the eraser button might wobble between + touching and erasing if the tablet doesn't enforce the Windows + state machine.""" + return { + "in-range -> touch -> erase -> hover-erase": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "in-range -> erase -> hover-erase": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "hover-erase -> erase -> touch -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "hover-erase -> touch -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "touch -> erase -> touch -> erase": ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + ), + } + + +class PenDigitizer(base.UHIDTestDevice): + def __init__( + self, + name, + rdesc_str=None, + rdesc=None, + application="Pen", + physical="Stylus", + input_info=(BusType.USB, 1, 2), + evdev_name_suffix=None, + ): + super().__init__(name, application, rdesc_str, rdesc, input_info) + self.physical = physical + self.cur_application = application + if evdev_name_suffix is not None: + self.name += evdev_name_suffix + + self.fields = [] + for r in self.parsed_rdesc.input_reports.values(): + if r.application_name == self.application: + physicals = [f.physical_name for f in r] + if self.physical not in physicals and None not in physicals: + continue + self.fields = [f.usage_name for f in r] + + def event(self, pen): + rs = [] + r = self.create_report(application=self.cur_application, data=pen) + self.call_input_event(r) + rs.append(r) + return rs + + def get_report(self, req, rnum, rtype): + if rtype != self.UHID_FEATURE_REPORT: + return (1, []) + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return (1, []) + + return (1, []) + + def set_report(self, req, rnum, rtype, data): + if rtype != self.UHID_FEATURE_REPORT: + return 1 + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return 1 + + return 1 + + +class BaseTest: + class TestTablet(base.BaseTestCase.TestUhid): + def create_device(self): + raise Exception("please reimplement me in subclasses") + + def post(self, uhdev, pen): + r = uhdev.event(pen) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + return events + + def validate_transitions(self, from_state, pen, evdev, events): + # check that the final state is correct + pen.assert_expected_input_events(evdev) + + # check that the transitions are valid + sync_events = [] + while libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) in events: + # split the first EV_SYN from the list + idx = events.index(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT)) + sync_events = events[:idx] + events = events[idx + 1 :] + + # now check for a valid transition + from_state = from_state.apply(sync_events) + + if events: + from_state = from_state.apply(sync_events) + + def _test_states(self, state_list, scribble): + """Internal method to test against a list of + transition between states. + state_list is a list of PenState objects + scribble is a boolean which tells if we need + to wobble a little the X,Y coordinates of the pen + between each state transition.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + cur_state = PenState.PEN_IS_OUT_OF_RANGE + + p = Pen(50, 60) + p.move_to(PenState.PEN_IS_OUT_OF_RANGE) + events = self.post(uhdev, p) + self.validate_transitions(cur_state, p, evdev, events) + + cur_state = p.current_state + + for state in state_list: + if scribble and cur_state != PenState.PEN_IS_OUT_OF_RANGE: + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + self.validate_transitions(cur_state, p, evdev, events) + assert len(events) >= 3 # X, Y, SYN + p.move_to(state) + if scribble and state != PenState.PEN_IS_OUT_OF_RANGE: + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + self.validate_transitions(cur_state, p, evdev, events) + cur_state = p.current_state + + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [pytest.param(v, id=k) for k, v in Pen.legal_transitions().items()], + ) + def test_valid_pen_states(self, state_list, scribble): + """This is the first half of the Windows Pen Implementation state machine: + we don't have Invert nor Erase bits, so just move in/out-of-range or proximity. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + self._test_states(state_list, scribble) + + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [pytest.param(v, id=k) for k, v in Pen.tolerated_transitions().items()], + ) + def test_tolerated_pen_states(self, state_list, scribble): + """This is not adhering to the Windows Pen Implementation state machine + but we should expect the kernel to behave properly, mostly for historical + reasons.""" + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Invert" not in uhdev.fields, + "Device not compatible, missing Invert usage", + ) + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [ + pytest.param(v, id=k) + for k, v in Pen.legal_transitions_with_invert().items() + ], + ) + def test_valid_invert_pen_states(self, state_list, scribble): + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Invert" not in uhdev.fields, + "Device not compatible, missing Invert usage", + ) + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [ + pytest.param(v, id=k) + for k, v in Pen.tolerated_transitions_with_invert().items() + ], + ) + def test_tolerated_invert_pen_states(self, state_list, scribble): + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Invert" not in uhdev.fields, + "Device not compatible, missing Invert usage", + ) + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [pytest.param(v, id=k) for k, v in Pen.broken_transitions().items()], + ) + def test_tolerated_broken_pen_states(self, state_list, scribble): + """Those tests are definitely not part of the Windows specification. + However, a half broken device might export those transitions. + For example, a pen that has the eraser button might wobble between + touching and erasing if the tablet doesn't enforce the Windows + state machine.""" + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Barrel Switch" not in uhdev.fields, + "Device not compatible, missing Barrel Switch usage", + ) + def test_primary_button(self): + """Primary button (stylus) pressed, reports as pressed even while hovering. + Actual reporting from the device: hid=TIPSWITCH,BARRELSWITCH,INRANGE (code=TOUCH,STYLUS,PEN): + { 0, 0, 1 } <- hover + { 0, 1, 1 } <- primary button pressed + { 0, 1, 1 } <- liftoff + { 0, 0, 0 } <- leaves + """ + + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + p = Pen(50, 60) + p.inrange = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1) in events + assert evdev.value[libevdev.EV_ABS.ABS_X] == 50 + assert evdev.value[libevdev.EV_ABS.ABS_Y] == 60 + assert not evdev.value[libevdev.EV_KEY.BTN_STYLUS] + + p.barrelswitch = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1) in events + + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + assert len(events) == 3 # X, Y, SYN + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 51) in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 59) in events + + p.barrelswitch = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0) in events + + p.inrange = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0) in events + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Barrel Switch" not in uhdev.fields, + "Device not compatible, missing Barrel Switch usage", + ) + def test_contact_primary_button(self): + """Primary button (stylus) pressed, reports as pressed even while hovering. + Actual reporting from the device: hid=TIPSWITCH,BARRELSWITCH,INRANGE (code=TOUCH,STYLUS,PEN): + { 0, 0, 1 } <- hover + { 0, 1, 1 } <- primary button pressed + { 1, 1, 1 } <- touch-down + { 1, 1, 1 } <- still touch, scribble on the screen + { 0, 1, 1 } <- liftoff + { 0, 0, 0 } <- leaves + """ + + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + p = Pen(50, 60) + p.inrange = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1) in events + assert evdev.value[libevdev.EV_ABS.ABS_X] == 50 + assert evdev.value[libevdev.EV_ABS.ABS_Y] == 60 + assert not evdev.value[libevdev.EV_KEY.BTN_STYLUS] + + p.barrelswitch = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1) in events + + p.tipswitch = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_STYLUS] + + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + assert len(events) == 3 # X, Y, SYN + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 51) in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 59) in events + + p.tipswitch = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + + p.barrelswitch = False + p.inrange = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0) in events + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0) in events + + +class GXTP_pen(PenDigitizer): + def event(self, pen): + if not hasattr(self, "prev_tip_state"): + self.prev_tip_state = False + + internal_pen = copy.copy(pen) + + # bug in the controller: when the pen touches the + # surface, in-range stays to 1, but when + # the pen moves in-range gets reverted to 0 + if pen.tipswitch and self.prev_tip_state: + internal_pen.inrange = False + + self.prev_tip_state = pen.tipswitch + + # another bug in the controller: when the pen is + # inverted, invert is set to 1, but as soon as + # the pen touches the surface, eraser is correctly + # set to 1 but invert is released + if pen.eraser: + internal_pen.invert = False + + return super().event(internal_pen) + + +class USIPen(PenDigitizer): + pass + + +################################################################################ +# +# Windows 7 compatible devices +# +################################################################################ +# class TestEgalax_capacitive_0eef_7224(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7224', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7224), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_72fa(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_72fa', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 72 22 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 87 13 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 72 22 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 87 13 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x72fa), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_7336(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7336', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 c1 20 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c2 18 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 c1 20 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c2 18 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7336), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_7337(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7337', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 ae 17 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c3 0e 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 ae 17 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c3 0e 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7337), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_7349(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7349', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7349), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_73f4(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_73f4', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 96 4e 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 23 2c 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 96 4e 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 23 2c 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x73f4), +# evdev_name_suffix=' Touchscreen') +# +# bogus: BTN_TOOL_PEN is not emitted +# class TestIrtouch_6615_0070(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test irtouch_6615_0070', +# rdesc='05 01 09 02 a1 01 85 10 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 30 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 05 0d 09 54 15 00 26 02 00 75 08 95 01 81 02 85 03 09 55 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 05 0d 09 02 a1 01 85 20 09 20 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 85 01 06 00 ff 09 01 75 08 95 01 b1 02 c0 c0', +# input_info=(BusType.USB, 0x6615, 0x0070)) + + +class TestNexio_1870_0100(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test nexio_1870_0100", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 40 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x0100), + ) + + +class TestNexio_1870_010d(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test nexio_1870_010d", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x010D), + ) + + +class TestNexio_1870_0119(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test nexio_1870_0119", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x0119), + ) + + +################################################################################ +# +# Windows 8 compatible devices +# +################################################################################ + +# bogus: application is 'undefined' +# class Testatmel_03eb_8409(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test atmel_03eb_8409', rdesc='05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 00 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 46 18 06 26 77 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0') + + +class Testatmel_03eb_840b(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test atmel_03eb_840b", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 00 0a 26 ff 0f 09 30 81 02 46 a0 05 26 ff 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0", + ) + + +class Testn_trig_1b96_0c01(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test n_trig_1b96_0c01", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0c03(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test n_trig_1b96_0c03", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0f00(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test n_trig_1b96_0f00", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0f04(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test n_trig_1b96_0f04", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_1000(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test n_trig_1b96_1000", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class TestGXTP_27c6_0113(BaseTest.TestTablet): + def create_device(self): + return GXTP_pen( + "uhid test GXTP_27c6_0113", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 55 0e 65 11 35 00 15 00 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 01 75 08 09 51 95 01 81 02 05 01 26 00 14 75 10 55 0e 65 11 09 30 35 00 46 1f 07 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 01 75 08 09 51 95 01 81 02 05 01 26 00 14 75 10 55 0e 65 11 09 30 35 00 46 1f 07 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 54 15 00 25 7f 75 08 95 01 81 02 85 02 09 55 95 01 25 0a b1 02 85 03 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 08 09 20 a1 00 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 04 81 02 95 01 81 03 09 32 81 02 95 02 81 03 95 01 75 08 09 51 81 02 05 01 09 30 75 10 95 01 a4 55 0e 65 11 35 00 26 00 14 46 1f 07 81 42 09 31 26 80 0c 46 77 04 81 42 b4 05 0d 09 30 26 ff 0f 81 02 09 3d 65 14 55 0e 36 d8 dc 46 28 23 16 d8 dc 26 28 23 81 02 09 3e 81 02 c0 c0 06 f0 ff 09 01 a1 01 85 0e 09 01 15 00 25 ff 75 08 95 40 91 02 09 01 15 00 25 ff 75 08 95 40 81 02 c0 05 01 09 06 a1 01 85 04 05 07 09 e3 15 00 25 01 75 01 95 01 81 02 95 07 81 03 c0", + ) + + +################################################################################ +# +# Windows 8 compatible devices with USI Pen +# +################################################################################ + + +class TestElan_04f3_2A49(BaseTest.TestTablet): + def create_device(self): + return USIPen( + "uhid test Elan_04f3_2A49", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 54 25 7f 96 01 00 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 16 00 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 16 00 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 20 09 01 91 02 c0 06 00 ff 09 01 a1 01 85 06 09 03 75 08 95 12 91 02 09 04 75 08 95 03 b1 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0 05 0d 09 02 a1 01 85 07 35 00 09 20 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0f 65 11 46 26 01 26 1c 48 81 42 09 31 46 a6 00 26 bc 2f 81 42 b4 05 0d 09 30 26 00 10 81 02 75 08 95 01 09 3b 25 64 81 42 09 38 15 00 25 02 81 02 09 5c 26 ff 00 81 02 09 5e 81 02 09 70 a1 02 15 01 25 06 09 72 09 73 09 74 09 75 09 76 09 77 81 20 09 5b 25 ff 75 40 81 02 c0 06 00 ff 75 08 95 02 09 01 81 02 c0 05 0d 85 60 09 81 a1 02 09 38 75 08 95 01 15 00 25 02 81 02 09 81 15 01 25 04 09 82 09 83 09 84 09 85 81 20 c0 85 61 09 5c a1 02 15 00 26 ff 00 75 08 95 01 09 38 b1 02 09 5c 26 ff 00 b1 02 09 5d 75 01 95 01 25 01 b1 02 95 07 b1 03 c0 85 62 09 5e a1 02 09 38 15 00 25 02 75 08 95 01 b1 02 09 5e 26 ff 00 b1 02 09 5f 75 01 25 01 b1 02 75 07 b1 03 c0 85 63 09 70 a1 02 75 08 95 01 15 00 25 02 09 38 b1 02 09 70 a1 02 25 06 09 72 09 73 09 74 09 75 09 76 09 77 b1 20 c0 09 71 75 01 25 01 b1 02 75 07 b1 03 c0 85 64 09 80 15 00 25 ff 75 40 95 01 b1 02 85 65 09 44 a1 02 09 38 75 08 95 01 25 02 b1 02 15 01 25 03 09 44 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 5a a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 45 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 c0 85 66 75 08 95 01 05 0d 09 90 a1 02 09 38 25 02 b1 02 09 91 75 10 26 ff 0f b1 02 09 92 75 40 25 ff b1 02 05 06 09 2a 75 08 26 ff 00 a1 02 09 2d b1 02 09 2e b1 02 c0 c0 85 67 05 06 09 2b a1 02 05 0d 25 02 09 38 b1 02 05 06 09 2b a1 02 09 2d 26 ff 00 b1 02 09 2e b1 02 c0 c0 85 68 06 00 ff 09 01 a1 02 05 0d 09 38 75 08 95 01 25 02 b1 02 06 00 ff 09 01 75 10 27 ff ff 00 00 b1 02 c0 85 69 05 0d 09 38 75 08 95 01 15 00 25 02 b1 02 c0 06 00 ff 09 81 a1 01 85 17 75 08 95 1f 09 05 81 02 c0", + input_info=(BusType.I2C, 0x04F3, 0x2A49), + ) + + +class TestGoodix_27c6_0e00(BaseTest.TestTablet): + def create_device(self): + return USIPen( + "uhid test Elan_04f3_2A49", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 55 0e 65 11 35 00 15 00 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 54 15 00 25 7f 75 08 95 01 81 02 85 02 09 55 95 01 25 0a b1 02 85 03 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 09 20 a1 00 85 08 05 01 a4 09 30 35 00 46 e6 09 15 00 26 04 20 55 0d 65 13 75 10 95 01 81 02 09 31 46 9a 06 26 60 15 81 02 b4 05 0d 09 38 95 01 75 08 15 00 25 01 81 02 09 30 75 10 26 ff 0f 81 02 09 31 81 02 09 42 09 44 09 5a 09 3c 09 45 09 32 75 01 95 06 25 01 81 02 95 02 81 03 09 3d 55 0e 65 14 36 d8 dc 46 28 23 16 d8 dc 26 28 23 95 01 75 10 81 02 09 3e 81 02 09 41 15 00 27 a0 8c 00 00 35 00 47 a0 8c 00 00 81 02 05 20 0a 53 04 65 00 16 01 f8 26 ff 07 75 10 95 01 81 02 0a 54 04 81 02 0a 55 04 81 02 0a 57 04 81 02 0a 58 04 81 02 0a 59 04 81 02 0a 72 04 81 02 0a 73 04 81 02 0a 74 04 81 02 05 0d 09 3b 15 00 25 64 75 08 81 02 09 5b 25 ff 75 40 81 02 06 00 ff 09 5b 75 20 81 02 05 0d 09 5c 26 ff 00 75 08 81 02 09 5e 81 02 09 70 a1 02 15 01 25 06 09 72 09 73 09 74 09 75 09 76 09 77 81 20 c0 06 00 ff 09 01 15 00 27 ff ff 00 00 75 10 95 01 81 02 85 09 09 81 a1 02 09 81 15 01 25 04 09 82 09 83 09 84 09 85 81 20 c0 85 10 09 5c a1 02 15 00 25 01 75 08 95 01 09 38 b1 02 09 5c 26 ff 00 b1 02 09 5d 75 01 95 01 25 01 b1 02 95 07 b1 03 c0 85 11 09 5e a1 02 09 38 15 00 25 01 75 08 95 01 b1 02 09 5e 26 ff 00 b1 02 09 5f 75 01 25 01 b1 02 75 07 b1 03 c0 85 12 09 70 a1 02 75 08 95 01 15 00 25 01 09 38 b1 02 09 70 a1 02 25 06 09 72 09 73 09 74 09 75 09 76 09 77 b1 20 c0 09 71 75 01 25 01 b1 02 75 07 b1 03 c0 85 13 09 80 15 00 25 ff 75 40 95 01 b1 02 85 14 09 44 a1 02 09 38 75 08 95 01 25 01 b1 02 15 01 25 03 09 44 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 5a a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 45 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 c0 85 15 75 08 95 01 05 0d 09 90 a1 02 09 38 25 01 b1 02 09 91 75 10 26 ff 0f b1 02 09 92 75 40 25 ff b1 02 05 06 09 2a 75 08 26 ff 00 a1 02 09 2d b1 02 09 2e b1 02 c0 c0 85 16 05 06 09 2b a1 02 05 0d 25 01 09 38 b1 02 05 06 09 2b a1 02 09 2d 26 ff 00 b1 02 09 2e b1 02 c0 c0 85 17 06 00 ff 09 01 a1 02 05 0d 09 38 75 08 95 01 25 01 b1 02 06 00 ff 09 01 75 10 27 ff ff 00 00 b1 02 c0 85 18 05 0d 09 38 75 08 95 01 15 00 25 01 b1 02 c0 c0 06 f0 ff 09 01 a1 01 85 0e 09 01 15 00 25 ff 75 08 95 40 91 02 09 01 15 00 25 ff 75 08 95 40 81 02 c0", + input_info=(BusType.I2C, 0x27C6, 0x0E00), + ) -- cgit v1.2.3-58-ga151 From 1dec39d489be13b416ab03d19355c712f1bcb21b Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 13:37:52 +0100 Subject: selftests: hid: import hid-tools wacom tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Jason Gerecke Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 3 + tools/testing/selftests/hid/hid-wacom.sh | 7 + .../selftests/hid/tests/descriptors_wacom.py | 1360 ++++++++++++++++++++ .../selftests/hid/tests/test_wacom_generic.py | 844 ++++++++++++ 5 files changed, 2215 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-wacom.sh create mode 100644 tools/testing/selftests/hid/tests/descriptors_wacom.py create mode 100644 tools/testing/selftests/hid/tests/test_wacom_generic.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index d8ad104c3863..4f11e865bbb3 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -11,6 +11,7 @@ TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh TEST_PROGS += hid-tablet.sh +TEST_PROGS += hid-wacom.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index d096d0bf74f3..71b97dacfc4e 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -20,4 +20,7 @@ CONFIG_HID=y CONFIG_HID_BPF=y CONFIG_INPUT_EVDEV=y CONFIG_UHID=y +CONFIG_USB=y +CONFIG_USB_HID=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-wacom.sh b/tools/testing/selftests/hid/hid-wacom.sh new file mode 100755 index 000000000000..1630c22726d2 --- /dev/null +++ b/tools/testing/selftests/hid/hid-wacom.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_wacom_generic.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/descriptors_wacom.py b/tools/testing/selftests/hid/tests/descriptors_wacom.py new file mode 100644 index 000000000000..91c16e005c12 --- /dev/null +++ b/tools/testing/selftests/hid/tests/descriptors_wacom.py @@ -0,0 +1,1360 @@ +# SPDX-License-Identifier: GPL-2.0 + +# fmt: off +wacom_pth660_v145 = [ + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x02, # . Usage (Mouse), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x01, # . Report ID (1), + 0x09, 0x01, # . Usage (Pointer), + 0xA1, 0x00, # . Collection (Physical), + 0x05, 0x09, # . Usage Page (Button), + 0x19, 0x01, # . Usage Minimum (01h), + 0x29, 0x03, # . Usage Maximum (03h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x03, # . Report Count (3), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x05, # . Report Count (5), + 0x81, 0x03, # . Input (Constant, Variable), + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x30, # . Usage (X), + 0x09, 0x31, # . Usage (Y), + 0x15, 0x81, # . Logical Minimum (-127), + 0x25, 0x7F, # . Logical Maximum (127), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x06, # . Input (Variable, Relative), + 0xC0, # . End Collection, + 0xC0, # . End Collection, + 0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh), + 0x09, 0x01, # . Usage (01h), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (20h), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (42h), + 0x09, 0x44, # . Usage (44h), + 0x09, 0x5A, # . Usage (5Ah), + 0x09, 0x45, # . Usage (45h), + 0x09, 0x3C, # . Usage (3Ch), + 0x09, 0x32, # . Usage (32h), + 0x09, 0x36, # . Usage (36h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (0130h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x35, 0x00, # . Physical Minimum (0), + 0x47, 0x80, 0x57, 0x00, 0x00, # . Physical Maximum (22400), + 0x15, 0x00, # . Logical Minimum (0), + 0x27, 0x00, 0xAF, 0x00, 0x00, # . Logical Maximum (44800), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (0131h), + 0x47, 0xD0, 0x39, 0x00, 0x00, # . Physical Maximum (14800), + 0x27, 0xA0, 0x73, 0x00, 0x00, # . Logical Maximum (29600), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (30h), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x1F, # . Logical Maximum (8191), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x3D, # . Usage (3Dh), + 0x09, 0x3E, # . Usage (3Eh), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0xC0, # . Physical Minimum (-64), + 0x45, 0x3F, # . Physical Maximum (63), + 0x15, 0xC0, # . Logical Minimum (-64), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x41, # . Usage (41h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x36, 0x4C, 0xFF, # . Physical Minimum (-180), + 0x46, 0xB3, 0x00, # . Physical Maximum (179), + 0x16, 0x7C, 0xFC, # . Logical Minimum (-900), + 0x26, 0x83, 0x03, # . Logical Maximum (899), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x03, 0x0D, # . Usage (0D03h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x07, # . Logical Maximum (2047), # !!! Errata: Missing Physical Min/Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x32, 0x01, # . Usage (0132h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (5Bh), + 0x09, 0x5C, # . Usage (5Ch), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (77h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x85, 0x11, # . Report ID (17), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x1A, 0x10, 0x09, # . Usage Minimum (0910h), + 0x2A, 0x17, 0x09, # . Usage Maximum (0917h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x08, # . Report Count (8), + 0x81, 0x02, # . Input (Variable), + 0x1A, 0x40, 0x09, # . Usage Minimum (0940h), + 0x2A, 0x47, 0x09, # . Usage Maximum (0947h), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x95, 0x09, # . Usage (0995h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x38, 0x01, # . Usage (0138h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0x67, 0x01, # . Physical Maximum (359), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x47, # . Logical Maximum (71), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x39, 0x01, # . Usage (0139h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x25, 0x01, # . Logical Maximum (1), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x04, # . Report Count (4), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x85, 0x13, # . Report ID (19), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x0A, 0x3B, 0x04, # . Usage (043Bh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x64, # . Logical Maximum (100), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x04, 0x04, # . Usage (0404h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x52, 0x04, # . Usage (0452h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x54, 0x04, # . Usage (0454h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x85, 0x02, # . Report ID (2), + 0x09, 0x01, # . Usage (01h), + 0x75, 0x08, # . Report Size (8), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x03, # . Report ID (3), + 0x0A, 0x03, 0x10, # . Usage (1003h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x04, # . Report ID (4), + 0x0A, 0x04, 0x10, # . Usage (1004h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x07, # . Report ID (7), + 0x0A, 0x09, 0x10, # . Usage (1009h), + 0x25, 0x02, # . Logical Maximum (2), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x0A, 0x07, 0x10, # . Usage (1007h), + 0x09, 0x00, # . Usage (00h), + 0x0A, 0x08, 0x10, # . Usage (1008h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x06, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0C, # . Report ID (12), + 0x0A, 0x30, 0x0D, # . Usage (0D30h), + 0x0A, 0x31, 0x0D, # . Usage (0D31h), + 0x0A, 0x32, 0x0D, # . Usage (0D32h), + 0x0A, 0x33, 0x0D, # . Usage (0D33h), # !!! Errata: Missing Non-zero Physical Max + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0D, # . Report ID (13), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x0A, 0x0D, 0x10, # . Usage (100Dh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x14, # . Report ID (20), + 0x0A, 0x14, 0x10, # . Usage (1014h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x31, # . Report ID (49), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x05, # . Report Count (5), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x32, # . Report ID (50), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x0A, 0x32, 0x10, # . Usage (1032h), + 0x25, 0x03, # . Logical Maximum (3), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x34, # . Report ID (52), + 0x0A, 0x34, 0x10, # . Usage (1034h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x35, # . Report ID (53), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0A, # . Report Count (10), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x36, # . Report ID (54), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x96, 0x01, 0x01, # . Report Count (257), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCC, # . Report ID (204), + 0x0A, 0xCC, 0x10, # . Usage (10CCh), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x0A, 0xAC, 0x10, # . Usage (10ACh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x75, 0x08, # . Report Size (8), + 0x85, 0xAC, # . Report ID (172), + 0x09, 0x00, # . Usage (00h), + 0x95, 0xBF, # . Report Count (191), + 0x81, 0x02, # . Input (Variable), + 0x85, 0x33, # . Report ID (51), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x12, # . Report Count (18), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x64, # . Report ID (100), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0C, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x15, # . Report ID (21), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x12, # . Report ID (18), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x16, # . Report ID (22), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x40, # . Report ID (64), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x41, # . Report ID (65), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x42, # . Report ID (66), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x43, # . Report ID (67), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x44, # . Report ID (68), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x45, # . Report ID (69), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x20, # . Report Count (32), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x60, # . Report ID (96), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x61, # . Report ID (97), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x62, # . Report ID (98), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x85, 0xD0, # . Report ID (208), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD1, # . Report ID (209), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD2, # . Report ID (210), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD3, # . Report ID (211), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD4, # . Report ID (212), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD5, # . Report ID (213), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD6, # . Report ID (214), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD7, # . Report ID (215), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD8, # . Report ID (216), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x0C, 0x00, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD9, # . Report ID (217), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x00, 0x0A, # . Report Count (2560), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDA, # . Report ID (218), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x04, # . Report Count (1028), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDB, # . Report ID (219), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x06, 0x00, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDC, # . Report ID (220), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDD, # . Report ID (221), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDE, # . Report ID (222), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDF, # . Report ID (223), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x22, 0x00, # . Report Count (34), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE0, # . Report ID (224), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x01, 0x00, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE1, # . Report ID (225), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE2, # . Report ID (226), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE3, # . Report ID (227), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE4, # . Report ID (228), + 0x09, 0x01, # . Usage (01h), + 0x96, 0xFF, 0x01, # . Report Count (511), + 0xB1, 0x02, # . Feature (Variable), + 0xC0 # . End Collection +] +# fmt: on + +# Report ID (20), Usage (1014h), Report Count (13) -> 15 +wacom_pth660_v150 = wacom_pth660_v145.copy() +wacom_pth660_v150[0x2CB] = 0x0F + +# fmt: off +wacom_pth860_v145 = [ + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x02, # . Usage (Mouse), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x01, # . Report ID (1), + 0x09, 0x01, # . Usage (Pointer), + 0xA1, 0x00, # . Collection (Physical), + 0x05, 0x09, # . Usage Page (Button), + 0x19, 0x01, # . Usage Minimum (01h), + 0x29, 0x03, # . Usage Maximum (03h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x03, # . Report Count (3), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x05, # . Report Count (5), + 0x81, 0x03, # . Input (Constant, Variable), + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x30, # . Usage (X), + 0x09, 0x31, # . Usage (Y), + 0x15, 0x80, # . Logical Minimum (-128), + 0x25, 0x7F, # . Logical Maximum (127), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x06, # . Input (Variable, Relative), + 0xC0, # . End Collection, + 0xC0, # . End Collection, + 0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh), + 0x09, 0x01, # . Usage (01h), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (20h), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (42h), + 0x09, 0x44, # . Usage (44h), + 0x09, 0x5A, # . Usage (5Ah), + 0x09, 0x45, # . Usage (45h), + 0x09, 0x3C, # . Usage (3Ch), + 0x09, 0x32, # . Usage (32h), + 0x09, 0x36, # . Usage (36h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (0130h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x35, 0x00, # . Physical Minimum (0), + 0x47, 0x7C, 0x79, 0x00, 0x00, # . Physical Maximum (31100), + 0x15, 0x00, # . Logical Minimum (0), + 0x27, 0xF8, 0xF2, 0x00, 0x00, # . Logical Maximum (62200), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (0131h), + 0x47, 0x60, 0x54, 0x00, 0x00, # . Physical Maximum (21600), + 0x27, 0xC0, 0xA8, 0x00, 0x00, # . Logical Maximum (43200), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (30h), # !!! Errata: Missing Physical Max = 0 + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x1F, # . Logical Maximum (8191), + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x3D, # . Usage (3Dh), + 0x09, 0x3E, # . Usage (3Eh), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0xC0, # . Physical Minimum (-64), + 0x45, 0x3F, # . Physical Maximum (63), + 0x15, 0xC0, # . Logical Minimum (-64), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x41, # . Usage (41h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x36, 0x4C, 0xFF, # . Physical Minimum (-180), + 0x46, 0xB3, 0x00, # . Physical Maximum (179), + 0x16, 0x7C, 0xFC, # . Logical Minimum (-900), + 0x26, 0x83, 0x03, # . Logical Maximum (899), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x03, 0x0D, # . Usage (0D03h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x07, # . Logical Maximum (2047), # !!! Errata: Missing Physical Min/Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x32, 0x01, # . Usage (0132h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (5Bh), + 0x09, 0x5C, # . Usage (5Ch), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (77h), + 0x16, 0x00, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x85, 0x11, # . Report ID (17), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x1A, 0x10, 0x09, # . Usage Minimum (0910h), + 0x2A, 0x17, 0x09, # . Usage Maximum (0917h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x08, # . Report Count (8), + 0x81, 0x02, # . Input (Variable), + 0x1A, 0x40, 0x09, # . Usage Minimum (0940h), + 0x2A, 0x47, 0x09, # . Usage Maximum (0947h), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x95, 0x09, # . Usage (0995h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x38, 0x01, # . Usage (0138h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0x67, 0x01, # . Physical Maximum (359), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x47, # . Logical Maximum (71), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x39, 0x01, # . Usage (0139h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x25, 0x01, # . Logical Maximum (1), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x04, # . Report Count (4), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x85, 0x13, # . Report ID (19), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x0A, 0x3B, 0x04, # . Usage (043Bh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x64, # . Logical Maximum (100), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x04, 0x04, # . Usage (0404h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x52, 0x04, # . Usage (0452h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x54, 0x04, # . Usage (0454h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x85, 0x02, # . Report ID (2), + 0x09, 0x01, # . Usage (01h), + 0x75, 0x08, # . Report Size (8), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x03, # . Report ID (3), + 0x0A, 0x03, 0x10, # . Usage (1003h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x04, # . Report ID (4), + 0x0A, 0x04, 0x10, # . Usage (1004h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x07, # . Report ID (7), + 0x0A, 0x09, 0x10, # . Usage (1009h), + 0x25, 0x02, # . Logical Maximum (2), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x0A, 0x07, 0x10, # . Usage (1007h), + 0x09, 0x00, # . Usage (00h), + 0x0A, 0x08, 0x10, # . Usage (1008h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x06, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0C, # . Report ID (12), + 0x0A, 0x30, 0x0D, # . Usage (0D30h), + 0x0A, 0x31, 0x0D, # . Usage (0D31h), + 0x0A, 0x32, 0x0D, # . Usage (0D32h), + 0x0A, 0x33, 0x0D, # . Usage (0D33h), # !!! Errata: Missing Non-zero Physical Max + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0D, # . Report ID (13), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x0A, 0x0D, 0x10, # . Usage (100Dh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x14, # . Report ID (20), + 0x0A, 0x14, 0x10, # . Usage (1014h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x31, # . Report ID (49), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x05, # . Report Count (5), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x32, # . Report ID (50), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x0A, 0x32, 0x10, # . Usage (1032h), + 0x25, 0x03, # . Logical Maximum (3), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x34, # . Report ID (52), + 0x0A, 0x34, 0x10, # . Usage (1034h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x35, # . Report ID (53), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0A, # . Report Count (10), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x36, # . Report ID (54), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x96, 0x01, 0x01, # . Report Count (257), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCC, # . Report ID (204), + 0x0A, 0xCC, 0x10, # . Usage (10CCh), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x0A, 0xAC, 0x10, # . Usage (10ACh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x75, 0x08, # . Report Size (8), + 0x85, 0xAC, # . Report ID (172), + 0x09, 0x00, # . Usage (00h), + 0x95, 0xBF, # . Report Count (191), + 0x81, 0x02, # . Input (Variable), + 0x85, 0x33, # . Report ID (51), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x12, # . Report Count (18), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x64, # . Report ID (100), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0C, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x15, # . Report ID (21), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x12, # . Report ID (18), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x16, # . Report ID (22), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x40, # . Report ID (64), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x41, # . Report ID (65), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x42, # . Report ID (66), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x43, # . Report ID (67), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x44, # . Report ID (68), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x45, # . Report ID (69), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x20, # . Report Count (32), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x60, # . Report ID (96), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x61, # . Report ID (97), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x62, # . Report ID (98), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x85, 0xD0, # . Report ID (208), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD1, # . Report ID (209), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD2, # . Report ID (210), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD3, # . Report ID (211), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD4, # . Report ID (212), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD5, # . Report ID (213), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD6, # . Report ID (214), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD7, # . Report ID (215), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD8, # . Report ID (216), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x0C, 0x00, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD9, # . Report ID (217), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x00, 0x0A, # . Report Count (2560), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDA, # . Report ID (218), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x04, # . Report Count (1028), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDB, # . Report ID (219), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x06, 0x00, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDC, # . Report ID (220), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDD, # . Report ID (221), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDE, # . Report ID (222), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDF, # . Report ID (223), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x22, 0x00, # . Report Count (34), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE0, # . Report ID (224), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x01, 0x00, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE1, # . Report ID (225), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE2, # . Report ID (226), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE3, # . Report ID (227), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE4, # . Report ID (228), + 0x09, 0x01, # . Usage (01h), + 0x96, 0xFF, 0x01, # . Report Count (511), + 0xB1, 0x02, # . Feature (Variable), + 0xC0 # . End Collection +] +# fmt: on + +# Report ID (20), Usage (1014h), Report Count (13) -> 15 +wacom_pth860_v150 = wacom_pth860_v145.copy() +wacom_pth860_v150[0x2CA] = 0x0F + +# fmt: off +wacom_pth460_v105 = [ + 0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh), + 0x09, 0x01, # . Usage (01h), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (20h), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (42h), + 0x09, 0x44, # . Usage (44h), + 0x09, 0x5A, # . Usage (5Ah), + 0x09, 0x45, # . Usage (45h), + 0x09, 0x3C, # . Usage (3Ch), + 0x09, 0x32, # . Usage (32h), + 0x09, 0x36, # . Usage (36h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (0130h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x47, 0x58, 0x3E, 0x00, 0x00, # . Physical Maximum (15960), + 0x27, 0xB0, 0x7C, 0x00, 0x00, # . Logical Maximum (31920), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (0131h), + 0x47, 0xF7, 0x26, 0x00, 0x00, # . Physical Maximum (9975), + 0x27, 0xEE, 0x4D, 0x00, 0x00, # . Logical Maximum (19950), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (30h), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x26, 0xFF, 0x1F, # . Logical Maximum (8191), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x3D, # . Usage (3Dh), + 0x09, 0x3E, # . Usage (3Eh), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0xC0, # . Physical Minimum (-64), + 0x45, 0x3F, # . Physical Maximum (63), + 0x15, 0xC0, # . Logical Minimum (-64), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x41, # . Usage (41h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x36, 0x4C, 0xFF, # . Physical Minimum (-180), + 0x46, 0xB3, 0x00, # . Physical Maximum (179), + 0x16, 0x7C, 0xFC, # . Logical Minimum (-900), + 0x26, 0x83, 0x03, # . Logical Maximum (899), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x03, 0x0D, # . Usage (0D03h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x07, # . Logical Maximum (2047), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x32, 0x01, # . Usage (0132h), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (5Bh), + 0x09, 0x5C, # . Usage (5Ch), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (77h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x85, 0x11, # . Report ID (17), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x1A, 0x10, 0x09, # . Usage Minimum (0910h), + 0x2A, 0x15, 0x09, # . Usage Maximum (0915h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x0A, 0x95, 0x09, # . Usage (0995h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x38, 0x01, # . Usage (0138h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0x67, 0x01, # . Physical Maximum (359), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x47, # . Logical Maximum (71), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x4A, # . Input (Variable, Wrap, Null State), + 0x0A, 0x39, 0x01, # . Usage (0139h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x75, 0x08, # . Report Size (8), + 0x95, 0x04, # . Report Count (4), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x85, 0x13, # . Report ID (19), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x3B, 0x04, # . Usage (043Bh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x64, # . Logical Maximum (100), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x04, 0x04, # . Usage (0404h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x0A, 0x52, 0x04, # . Usage (0452h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x41, 0x04, # . Usage (0441h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x07, # . Logical Maximum (7), + 0x75, 0x03, # . Report Size (3), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x54, 0x04, # . Usage (0454h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x3C, 0x04, # . Usage (043Ch), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0xFB, # . Logical Minimum (-5), + 0x25, 0x32, # . Logical Maximum (50), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x3D, 0x04, # . Usage (043Dh), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x75, 0x08, # . Report Size (8), + 0x95, 0x03, # . Report Count (3), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x85, 0x02, # . Report ID (2), + 0x0A, 0x02, 0x10, # . Usage (1002h), + 0x15, 0x02, # . Logical Minimum (2), + 0x25, 0x02, # . Logical Maximum (2), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x03, # . Report ID (3), + 0x0A, 0x03, 0x10, # . Usage (1003h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x04, # . Report ID (4), + 0x0A, 0x04, 0x10, # . Usage (1004h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x07, # . Report ID (7), + 0x0A, 0x09, 0x10, # . Usage (1009h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x0A, 0x07, 0x10, # . Usage (1007h), + 0x09, 0x00, # . Usage (00h), + 0x0A, 0x08, 0x10, # . Usage (1008h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x06, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x09, 0x00, # . Usage (00h), + 0x25, 0x00, # . Logical Maximum (0), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0C, # . Report ID (12), + 0x0A, 0x30, 0x0D, # . Usage (0D30h), + 0x0A, 0x31, 0x0D, # . Usage (0D31h), + 0x0A, 0x32, 0x0D, # . Usage (0D32h), + 0x0A, 0x33, 0x0D, # . Usage (0D33h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0xC8, 0x00, # . Physical Maximum (200), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0x90, 0x01, # . Logical Maximum (400), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x0D, # . Report ID (13), + 0x0A, 0x0D, 0x10, # . Usage (100Dh), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x14, # . Report ID (20), + 0x0A, 0x14, 0x10, # . Usage (1014h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCC, # . Report ID (204), + 0x0A, 0xCC, 0x10, # . Usage (10CCh), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x85, 0x31, # . Report ID (49), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x03, # . Report Count (3), + 0xB1, 0x02, # . Feature (Variable), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0xC0, # . End Collection, + 0x0A, 0xAC, 0x10, # . Usage (10ACh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x75, 0x08, # . Report Size (8), + 0x85, 0xAC, # . Report ID (172), + 0x09, 0x00, # . Usage (00h), + 0x96, 0xBF, 0x00, # . Report Count (191), + 0x81, 0x02, # . Input (Variable), + 0x85, 0x15, # . Report ID (21), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x33, # . Report ID (51), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x12, # . Report Count (18), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x44, # . Report ID (68), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x45, # . Report ID (69), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x20, # . Report Count (32), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x60, # . Report ID (96), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x61, # . Report ID (97), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x62, # . Report ID (98), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x65, # . Report ID (101), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x66, # . Report ID (102), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x67, # . Report ID (103), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x68, # . Report ID (104), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x11, # . Report Count (17), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x6F, # . Report ID (111), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCD, # . Report ID (205), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x16, # . Report ID (22), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x35, # . Report ID (53), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0A, # . Report Count (10), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x85, 0xD1, # . Report ID (209), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD2, # . Report ID (210), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD3, # . Report ID (211), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD4, # . Report ID (212), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD5, # . Report ID (213), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD6, # . Report ID (214), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD7, # . Report ID (215), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD8, # . Report ID (216), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x0C, 0x00, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD9, # . Report ID (217), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x00, 0x0A, # . Report Count (2560), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDA, # . Report ID (218), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x04, # . Report Count (1028), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDB, # . Report ID (219), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x06, 0x00, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDC, # . Report ID (220), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDD, # . Report ID (221), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDE, # . Report ID (222), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDF, # . Report ID (223), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x22, 0x00, # . Report Count (34), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE0, # . Report ID (224), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x01, 0x00, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE1, # . Report ID (225), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE2, # . Report ID (226), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE3, # . Report ID (227), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE4, # . Report ID (228), + 0x09, 0x01, # . Usage (01h), + 0x96, 0xFF, 0x01, # . Report Count (511), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCB, # . Report ID (203), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x1F, 0x00, # . Report Count (31), + 0xB1, 0x02, # . Feature (Variable), + 0xC0 # . End Collection +] +# fmt: on diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py new file mode 100644 index 000000000000..b1eb2bc787fc --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py @@ -0,0 +1,844 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# Copyright (c) 2020 Wacom Technology Corp. +# +# Authors: +# Jason Gerecke + +""" +Tests for the Wacom driver generic codepath. + +This module tests the function of the Wacom driver's generic codepath. +The generic codepath is used by devices which are not explicitly listed +in the driver's device table. It uses the device's HID descriptor to +decode reports sent by the device. +""" + +from .descriptors_wacom import ( + wacom_pth660_v145, + wacom_pth660_v150, + wacom_pth860_v145, + wacom_pth860_v150, + wacom_pth460_v105, +) + +import attr +from enum import Enum +from hidtools.hut import HUT +from hidtools.hid import HidUnit +from . import base +import libevdev +import pytest + +import logging + +logger = logging.getLogger("hidtools.test.wacom") + +KERNEL_MODULE = ("wacom", "wacom") + + +class ProximityState(Enum): + """ + Enumeration of allowed proximity states. + """ + + # Tool is not able to be sensed by the device + OUT = 0 + + # Tool is close enough to be sensed, but some data may be invalid + # or inaccurate + IN_PROXIMITY = 1 + + # Tool is close enough to be sensed with high accuracy. All data + # valid. + IN_RANGE = 2 + + def fill(self, reportdata): + """Fill a report with approrpiate HID properties/values.""" + reportdata.inrange = self in [ProximityState.IN_RANGE] + reportdata.wacomsense = self in [ + ProximityState.IN_PROXIMITY, + ProximityState.IN_RANGE, + ] + + +class ReportData: + """ + Placeholder for HID report values. + """ + + pass + + +@attr.s +class Buttons: + """ + Stylus button state. + + Describes the state of each of the buttons / "side switches" that + may be present on a stylus. Buttons set to 'None' indicate the + state is "unchanged" since the previous event. + """ + + primary = attr.ib(default=None) + secondary = attr.ib(default=None) + tertiary = attr.ib(default=None) + + @staticmethod + def clear(): + """Button object with all states cleared.""" + return Buttons(False, False, False) + + def fill(self, reportdata): + """Fill a report with approrpiate HID properties/values.""" + reportdata.barrelswitch = int(self.primary or 0) + reportdata.secondarybarrelswitch = int(self.secondary or 0) + reportdata.b3 = int(self.tertiary or 0) + + +@attr.s +class ToolID: + """ + Stylus tool identifiers. + + Contains values used to identify a specific stylus, e.g. its serial + number and tool-type identifier. Values of ``0`` may sometimes be + used for the out-of-range condition. + """ + + serial = attr.ib() + tooltype = attr.ib() + + @staticmethod + def clear(): + """ToolID object with all fields cleared.""" + return ToolID(0, 0) + + def fill(self, reportdata): + """Fill a report with approrpiate HID properties/values.""" + reportdata.transducerserialnumber = self.serial & 0xFFFFFFFF + reportdata.serialhi = (self.serial >> 32) & 0xFFFFFFFF + reportdata.tooltype = self.tooltype + + +@attr.s +class PhysRange: + """ + Range of HID physical values, with units. + """ + + unit = attr.ib() + min_size = attr.ib() + max_size = attr.ib() + + CENTIMETER = HidUnit.from_string("SILinear: cm") + DEGREE = HidUnit.from_string("EnglishRotation: deg") + + def contains(self, field): + """ + Check if the physical size of the provided field is in range. + + Compare the physical size described by the provided HID field + against the range of sizes described by this object. This is + an exclusive range comparison (e.g. 0 cm is not within the + range 0 cm - 5 cm) and exact unit comparison (e.g. 1 inch is + not within the range 0 cm - 5 cm). + """ + phys_size = (field.physical_max - field.physical_min) * 10 ** (field.unit_exp) + return ( + field.unit == self.unit.value + and phys_size > self.min_size + and phys_size < self.max_size + ) + + +class BaseTablet(base.UHIDTestDevice): + """ + Skeleton object for all kinds of tablet devices. + """ + + def __init__(self, rdesc, name=None, info=None): + assert rdesc is not None + super().__init__(name, "Pen", input_info=info, rdesc=rdesc) + self.buttons = Buttons.clear() + self.toolid = ToolID.clear() + self.proximity = ProximityState.OUT + self.offset = 0 + self.ring = -1 + self.ek0 = False + + def match_evdev_rule(self, application, evdev): + """ + Filter out evdev nodes based on the requested application. + + The Wacom driver may create several device nodes for each USB + interface device. It is crucial that we run tests with the + expected device node or things will obviously go off the rails. + Use the Wacom driver's usual naming conventions to apply a + sensible default filter. + """ + if application in ["Pen", "Pad"]: + return evdev.name.endswith(application) + else: + return True + + def create_report( + self, x, y, pressure, buttons=None, toolid=None, proximity=None, reportID=None + ): + """ + Return an input report for this device. + + :param x: absolute x + :param y: absolute y + :param pressure: pressure + :param buttons: stylus button state. Use ``None`` for unchanged. + :param toolid: tool identifiers. Use ``None`` for unchanged. + :param proximity: a ProximityState indicating the sensor's ability + to detect and report attributes of this tool. Use ``None`` + for unchanged. + :param reportID: the numeric report ID for this report, if needed + """ + if buttons is not None: + self.buttons = buttons + buttons = self.buttons + + if toolid is not None: + self.toolid = toolid + toolid = self.toolid + + if proximity is not None: + self.proximity = proximity + proximity = self.proximity + + reportID = reportID or self.default_reportID + + report = ReportData() + report.x = x + report.y = y + report.tippressure = pressure + report.tipswitch = pressure > 0 + buttons.fill(report) + proximity.fill(report) + toolid.fill(report) + + return super().create_report(report, reportID=reportID) + + def create_report_heartbeat(self, reportID): + """ + Return a heartbeat input report for this device. + + Heartbeat reports generally contain battery status information, + among other things. + """ + report = ReportData() + report.wacombatterycharging = 1 + return super().create_report(report, reportID=reportID) + + def create_report_pad(self, reportID, ring, ek0): + report = ReportData() + + if ring is not None: + self.ring = ring + ring = self.ring + + if ek0 is not None: + self.ek0 = ek0 + ek0 = self.ek0 + + if ring >= 0: + report.wacomtouchring = ring + report.wacomtouchringstatus = 1 + else: + report.wacomtouchring = 0x7F + report.wacomtouchringstatus = 0 + + report.wacomexpresskey00 = ek0 + return super().create_report(report, reportID=reportID) + + def event(self, x, y, pressure, buttons=None, toolid=None, proximity=None): + """ + Send an input event on the default report ID. + + :param x: absolute x + :param y: absolute y + :param buttons: stylus button state. Use ``None`` for unchanged. + :param toolid: tool identifiers. Use ``None`` for unchanged. + :param proximity: a ProximityState indicating the sensor's ability + to detect and report attributes of this tool. Use ``None`` + for unchanged. + """ + r = self.create_report(x, y, pressure, buttons, toolid, proximity) + self.call_input_event(r) + return [r] + + def event_heartbeat(self, reportID): + """ + Send a heartbeat event on the requested report ID. + """ + r = self.create_report_heartbeat(reportID) + self.call_input_event(r) + return [r] + + def event_pad(self, reportID, ring=None, ek0=None): + """ + Send a pad event on the requested report ID. + """ + r = self.create_report_pad(reportID, ring, ek0) + self.call_input_event(r) + return [r] + + def get_report(self, req, rnum, rtype): + if rtype != self.UHID_FEATURE_REPORT: + return (1, []) + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return (1, []) + + result = (1, []) + result = self.create_report_offset(rdesc) or result + return result + + def create_report_offset(self, rdesc): + require = [ + "Wacom Offset Left", + "Wacom Offset Top", + "Wacom Offset Right", + "Wacom Offset Bottom", + ] + if not set(require).issubset(set([f.usage_name for f in rdesc])): + return None + + report = ReportData() + report.wacomoffsetleft = self.offset + report.wacomoffsettop = self.offset + report.wacomoffsetright = self.offset + report.wacomoffsetbottom = self.offset + r = rdesc.create_report([report], None) + return (0, r) + + +class OpaqueTablet(BaseTablet): + """ + Bare-bones opaque tablet with a minimum of features. + + A tablet stripped down to its absolute core. It is capable of + reporting X/Y position and if the pen is in contact. No pressure, + no barrel switches, no eraser. Notably it *does* report an "In + Range" flag, but this is only because the Wacom driver expects + one to function properly. The device uses only standard HID usages, + not any of Wacom's vendor-defined pages. + """ + + # fmt: off + report_descriptor = [ + 0x05, 0x0D, # . Usage Page (Digitizer), + 0x09, 0x01, # . Usage (Digitizer), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x01, # . Report ID (1), + 0x09, 0x20, # . Usage (Stylus), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (Tip Switch), + 0x09, 0x32, # . Usage (In Range), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x30, # . Usage (X), + 0x27, 0x80, 0x3E, 0x00, 0x00, # . Logical Maximum (16000), + 0x47, 0x80, 0x3E, 0x00, 0x00, # . Physical Maximum (16000), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x31, # . Usage (Y), + 0x27, 0x28, 0x23, 0x00, 0x00, # . Logical Maximum (9000), + 0x47, 0x28, 0x23, 0x00, 0x00, # . Physical Maximum (9000), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0xC0, # . End Collection, + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)): + super().__init__(rdesc, name, info) + self.default_reportID = 1 + + +class OpaqueCTLTablet(BaseTablet): + """ + Opaque tablet similar to something in the CTL product line. + + A pen-only tablet with most basic features you would expect from + an actual device. Position, eraser, pressure, barrel buttons. + Uses the Wacom vendor-defined usage page. + """ + + # fmt: off + report_descriptor = [ + 0x06, 0x0D, 0xFF, # . Usage Page (Vnd Wacom Emr), + 0x09, 0x01, # . Usage (Digitizer), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (Stylus), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (Tip Switch), + 0x09, 0x44, # . Usage (Barrel Switch), + 0x09, 0x5A, # . Usage (Secondary Barrel Switch), + 0x09, 0x45, # . Usage (Eraser), + 0x09, 0x3C, # . Usage (Invert), + 0x09, 0x32, # . Usage (In Range), + 0x09, 0x36, # . Usage (In Proximity), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (X), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x47, 0x80, 0x3E, 0x00, 0x00, # . Physical Maximum (16000), + 0x27, 0x80, 0x3E, 0x00, 0x00, # . Logical Maximum (16000), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (Y), + 0x47, 0x28, 0x23, 0x00, 0x00, # . Physical Maximum (9000), + 0x27, 0x28, 0x23, 0x00, 0x00, # . Logical Maximum (9000), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (Tip Pressure), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x47, 0x00, 0x00, 0x00, 0x00, # . Physical Maximum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x32, 0x01, # . Usage (Z), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (Transducer Serial Number), + 0x09, 0x5C, # . Usage (Transducer Serial Number Hi), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (Tool Type), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0xC0 # . End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)): + super().__init__(rdesc, name, info) + self.default_reportID = 16 + + +class PTHX60_Pen(BaseTablet): + """ + Pen interface of a PTH-660 / PTH-860 / PTH-460 tablet. + + This generation of devices are nearly identical to each other, though + the PTH-460 uses a slightly different descriptor construction (splits + the pad among several physical collections) + """ + + def __init__(self, rdesc=None, name=None, info=None): + super().__init__(rdesc, name, info) + self.default_reportID = 16 + + +class BaseTest: + class TestTablet(base.BaseTestCase.TestUhid): + kernel_modules = [KERNEL_MODULE] + + def sync_and_assert_events( + self, report, expected_events, auto_syn=True, strict=False + ): + """ + Assert we see the expected events in response to a report. + """ + uhdev = self.uhdev + syn_event = self.syn_event + if auto_syn: + expected_events.append(syn_event) + actual_events = uhdev.next_sync_events() + self.debug_reports(report, uhdev, actual_events) + if strict: + self.assertInputEvents(expected_events, actual_events) + else: + self.assertInputEventsIn(expected_events, actual_events) + + def get_usages(self, uhdev): + def get_report_usages(report): + application = report.application + for field in report.fields: + if field.usages is not None: + for usage in field.usages: + yield (field, usage, application) + else: + yield (field, field.usage, application) + + desc = uhdev.parsed_rdesc + reports = [ + *desc.input_reports.values(), + *desc.feature_reports.values(), + *desc.output_reports.values(), + ] + for report in reports: + for usage in get_report_usages(report): + yield usage + + def assertName(self, uhdev): + """ + Assert that the name is as we expect. + + The Wacom driver applies a number of decorations to the name + provided by the hardware. We cannot rely on the definition of + this assertion from the base class to work properly. + """ + evdev = uhdev.get_evdev() + expected_name = uhdev.name + " Pen" + if "wacom" not in expected_name.lower(): + expected_name = "Wacom " + expected_name + assert evdev.name == expected_name + + def test_descriptor_physicals(self): + """ + Verify that all HID usages which should have a physical range + actually do, and those which shouldn't don't. Also verify that + the associated unit is correct and within a sensible range. + """ + + def usage_id(page_name, usage_name): + page = HUT.usage_page_from_name(page_name) + return (page.page_id << 16) | page[usage_name].usage + + required = { + usage_id("Generic Desktop", "X"): PhysRange( + PhysRange.CENTIMETER, 5, 150 + ), + usage_id("Generic Desktop", "Y"): PhysRange( + PhysRange.CENTIMETER, 5, 150 + ), + usage_id("Digitizers", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Digitizers", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Digitizers", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360), + usage_id("Wacom", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Wacom", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Wacom", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360), + usage_id("Wacom", "X"): PhysRange(PhysRange.CENTIMETER, 5, 150), + usage_id("Wacom", "Y"): PhysRange(PhysRange.CENTIMETER, 5, 150), + usage_id("Wacom", "Wacom TouchRing"): PhysRange( + PhysRange.DEGREE, 358, 360 + ), + usage_id("Wacom", "Wacom Offset Left"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + usage_id("Wacom", "Wacom Offset Top"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + usage_id("Wacom", "Wacom Offset Right"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + usage_id("Wacom", "Wacom Offset Bottom"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + } + for field, usage, application in self.get_usages(self.uhdev): + if application == usage_id("Generic Desktop", "Mouse"): + # Ignore the vestigial Mouse collection which exists + # on Wacom tablets only for backwards compatibility. + continue + + expect_physical = usage in required + + phys_set = field.physical_min != 0 or field.physical_max != 0 + assert phys_set == expect_physical + + unit_set = field.unit != 0 + assert unit_set == expect_physical + + if unit_set: + assert required[usage].contains(field) + + def test_prop_direct(self): + """ + Todo: Verify that INPUT_PROP_DIRECT is set on display devices. + """ + pass + + def test_prop_pointer(self): + """ + Todo: Verify that INPUT_PROP_POINTER is set on opaque devices. + """ + pass + + +class TestOpaqueTablet(BaseTest.TestTablet): + def create_device(self): + return OpaqueTablet() + + def test_sanity(self): + """ + Bring a pen into contact with the tablet, then remove it. + + Ensure that we get the basic tool/touch/motion events that should + be sent by the driver. + """ + uhdev = self.uhdev + + self.sync_and_assert_events( + uhdev.event( + 100, + 200, + pressure=300, + buttons=Buttons.clear(), + toolid=ToolID(serial=1, tooltype=1), + proximity=ProximityState.IN_RANGE, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(110, 220, pressure=0), + [ + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 220), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0), + ], + ) + + self.sync_and_assert_events( + uhdev.event( + 120, + 230, + pressure=0, + toolid=ToolID.clear(), + proximity=ProximityState.OUT, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0), + ], + ) + + self.sync_and_assert_events( + uhdev.event(130, 240, pressure=0), [], auto_syn=False, strict=True + ) + + +class TestOpaqueCTLTablet(TestOpaqueTablet): + def create_device(self): + return OpaqueCTLTablet() + + def test_buttons(self): + """ + Test that the barrel buttons (side switches) work as expected. + + Press and release each button individually to verify that we get + the expected events. + """ + uhdev = self.uhdev + + self.sync_and_assert_events( + uhdev.event( + 100, + 200, + pressure=0, + buttons=Buttons.clear(), + toolid=ToolID(serial=1, tooltype=1), + proximity=ProximityState.IN_RANGE, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=True)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=False)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=True)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 1), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=False)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 0), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + +PTHX60_Devices = [ + {"rdesc": wacom_pth660_v145, "info": (0x3, 0x056A, 0x0357)}, + {"rdesc": wacom_pth660_v150, "info": (0x3, 0x056A, 0x0357)}, + {"rdesc": wacom_pth860_v145, "info": (0x3, 0x056A, 0x0358)}, + {"rdesc": wacom_pth860_v150, "info": (0x3, 0x056A, 0x0358)}, + {"rdesc": wacom_pth460_v105, "info": (0x3, 0x056A, 0x0392)}, +] + +PTHX60_Names = [ + "PTH-660/v145", + "PTH-660/v150", + "PTH-860/v145", + "PTH-860/v150", + "PTH-460/v105", +] + + +class TestPTHX60_Pen(TestOpaqueCTLTablet): + @pytest.fixture( + autouse=True, scope="class", params=PTHX60_Devices, ids=PTHX60_Names + ) + def set_device_params(self, request): + request.cls.device_params = request.param + + def create_device(self): + return PTHX60_Pen(**self.device_params) + + @pytest.mark.xfail + def test_descriptor_physicals(self): + # XFAIL: Various documented errata + super().test_descriptor_physicals() + + def test_heartbeat_spurious(self): + """ + Test that the heartbeat report does not send spurious events. + """ + uhdev = self.uhdev + + self.sync_and_assert_events( + uhdev.event( + 100, + 200, + pressure=300, + buttons=Buttons.clear(), + toolid=ToolID(serial=1, tooltype=0x822), + proximity=ProximityState.IN_RANGE, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1), + ], + ) + + # Exactly zero events: not even a SYN + self.sync_and_assert_events( + uhdev.event_heartbeat(19), [], auto_syn=False, strict=True + ) + + self.sync_and_assert_events( + uhdev.event(110, 200, pressure=300), + [ + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110), + ], + ) + + def test_empty_pad_sync(self): + self.empty_pad_sync(num=3, denom=16, reverse=True) + + def empty_pad_sync(self, num, denom, reverse): + """ + Test that multiple pad collections do not trigger empty syncs. + """ + + def offset_rotation(value): + """ + Offset touchring rotation values by the same factor as the + Linux kernel. Tablets historically don't use the same origin + as HID, and it sometimes changes from tablet to tablet... + """ + evdev = self.uhdev.get_evdev() + info = evdev.absinfo[libevdev.EV_ABS.ABS_WHEEL] + delta = info.maximum - info.minimum + 1 + if reverse: + value = info.maximum - value + value += num * delta // denom + if value > info.maximum: + value -= delta + elif value < info.minimum: + value += delta + return value + + uhdev = self.uhdev + uhdev.application = "Pad" + evdev = uhdev.get_evdev() + + print(evdev.name) + self.sync_and_assert_events( + uhdev.event_pad(reportID=17, ring=0, ek0=1), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(0)), + libevdev.InputEvent(libevdev.EV_ABS.ABS_MISC, 15), + ], + ) + + self.sync_and_assert_events( + uhdev.event_pad(reportID=17, ring=1, ek0=1), + [libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(1))], + ) + + self.sync_and_assert_events( + uhdev.event_pad(reportID=17, ring=2, ek0=0), + [ + libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(2)), + libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 0), + ], + ) -- cgit v1.2.3-58-ga151 From 9898fa566596a259dee3407ba684f9ff0d514944 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 13:45:00 +0100 Subject: selftests: hid: import hid-tools hid-apple tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Roderick Colenbrander Signed-off-by: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 1 + tools/testing/selftests/hid/hid-apple.sh | 7 + .../selftests/hid/tests/test_apple_keyboard.py | 440 +++++++++++++++++++++ 4 files changed, 449 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-apple.sh create mode 100644 tools/testing/selftests/hid/tests/test_apple_keyboard.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 4f11e865bbb3..ce03c65bfba0 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -6,6 +6,7 @@ include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh +TEST_PROGS += hid-apple.sh TEST_PROGS += hid-gamepad.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index 71b97dacfc4e..46e745630f02 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -22,5 +22,6 @@ CONFIG_INPUT_EVDEV=y CONFIG_UHID=y CONFIG_USB=y CONFIG_USB_HID=y +CONFIG_HID_APPLE=y CONFIG_HID_MULTITOUCH=y CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-apple.sh b/tools/testing/selftests/hid/hid-apple.sh new file mode 100755 index 000000000000..656f2d5ae5a9 --- /dev/null +++ b/tools/testing/selftests/hid/hid-apple.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_apple_keyboard.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_apple_keyboard.py b/tools/testing/selftests/hid/tests/test_apple_keyboard.py new file mode 100644 index 000000000000..f81071d46166 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_apple_keyboard.py @@ -0,0 +1,440 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019 Benjamin Tissoires +# Copyright (c) 2019 Red Hat, Inc. +# + +from .test_keyboard import ArrayKeyboard, TestArrayKeyboard +from hidtools.util import BusType + +import libevdev +import logging + +logger = logging.getLogger("hidtools.test.apple-keyboard") + +KERNEL_MODULE = ("apple", "hid-apple") + + +class KbdData(object): + pass + + +class AppleKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x01, # .Report ID (1) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x01, # .Report Count (1) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x05, # .Report Count (5) + 0x05, 0x08, # .Usage Page (LEDs) + 0x19, 0x01, # .Usage Minimum (1) + 0x29, 0x05, # .Usage Maximum (5) + 0x91, 0x02, # .Output (Data,Var,Abs) + 0x75, 0x03, # .Report Size (3) + 0x95, 0x01, # .Report Count (1) + 0x91, 0x01, # .Output (Cnst,Arr,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x06, # .Report Count (6) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xff, 0x00, # .Logical Maximum (255) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0x00, # .Usage Minimum (0) + 0x2a, 0xff, 0x00, # .Usage Maximum (255) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xc0, # End Collection + 0x05, 0x0c, # Usage Page (Consumer Devices) + 0x09, 0x01, # Usage (Consumer Control) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x47, # .Report ID (71) + 0x05, 0x01, # .Usage Page (Generic Desktop) + 0x09, 0x06, # .Usage (Keyboard) + 0xa1, 0x02, # .Collection (Logical) + 0x05, 0x06, # ..Usage Page (Generic Device Controls) + 0x09, 0x20, # ..Usage (Battery Strength) + 0x15, 0x00, # ..Logical Minimum (0) + 0x26, 0xff, 0x00, # ..Logical Maximum (255) + 0x75, 0x08, # ..Report Size (8) + 0x95, 0x01, # ..Report Count (1) + 0x81, 0x02, # ..Input (Data,Var,Abs) + 0xc0, # .End Collection + 0xc0, # End Collection + 0x05, 0x0c, # Usage Page (Consumer Devices) + 0x09, 0x01, # Usage (Consumer Control) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x11, # .Report ID (17) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x03, # .Report Count (3) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x01, # .Report Count (1) + 0x05, 0x0c, # .Usage Page (Consumer Devices) + 0x09, 0xb8, # .Usage (Eject) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x06, 0xff, 0x00, # .Usage Page (Vendor Usage Page 0xff) + 0x09, 0x03, # .Usage (Vendor Usage 0x03) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x03, # .Report Count (3) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x05, 0x0c, # .Usage Page (Consumer Devices) + 0x85, 0x12, # .Report ID (18) + 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, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb3, # .Usage (Fast Forward) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb4, # .Usage (Rewind) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb5, # .Usage (Scan Next Track) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb6, # .Usage (Scan Previous Track) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x85, 0x13, # .Report ID (19) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x01, # .Report Count (1) + 0x06, 0x01, 0xff, # .Usage Page (Vendor Usage Page 0xff01) + 0x09, 0x0a, # .Usage (Vendor Usage 0x0a) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x06, 0x01, 0xff, # .Usage Page (Vendor Usage Page 0xff01) + 0x09, 0x0c, # .Usage (Vendor Usage 0x0c) + 0x81, 0x22, # .Input (Data,Var,Abs,NoPref) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x06, # .Report Count (6) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x85, 0x09, # .Report ID (9) + 0x09, 0x0b, # .Usage (Vendor Usage 0x0b) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x01, # .Report Count (1) + 0xb1, 0x02, # .Feature (Data,Var,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x02, # .Report Count (2) + 0xb1, 0x01, # .Feature (Cnst,Arr,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__( + self, + rdesc=report_descriptor, + name="Apple Wireless Keyboard", + input_info=(BusType.BLUETOOTH, 0x05AC, 0x0256), + ): + super().__init__(rdesc, name, input_info) + self.default_reportID = 1 + + def send_fn_state(self, state): + data = KbdData() + setattr(data, "0xff0003", state) + r = self.create_report(data, reportID=17) + self.call_input_event(r) + return [r] + + +class TestAppleKeyboard(TestArrayKeyboard): + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + return AppleKeyboard() + + def test_single_function_key(self): + """check for function key reliability.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["F4"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0 + + def test_single_fn_function_key(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["F4"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + def test_single_fn_function_key_release_first(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["F4"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + + def test_single_fn_function_key_inverted(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["F4"]) + r.extend(uhdev.send_fn_state(1)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + def test_multiple_fn_function_key_release_first(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["F4"])) + r.extend(uhdev.event(["F4", "F6"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.event(["F6"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + def test_multiple_fn_function_key_release_between(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + # press F4 + r = uhdev.event(["F4"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + # press Fn key + r = uhdev.send_fn_state(1) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + # keep F4 and press F6 + r = uhdev.event(["F4", "F6"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + # keep F4 and F6 + r = uhdev.event(["F4", "F6"]) + expected = [] + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + # release Fn key and all keys + r = uhdev.send_fn_state(0) + r.extend(uhdev.event([])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + def test_single_pageup_key_release_first(self): + """check for function key reliability with the [page] up key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["UpArrow"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 -- cgit v1.2.3-58-ga151 From ff3b2228e3c886863f13d694480e36bfdcbe33e0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 13:49:33 +0100 Subject: selftests: hid: import hid-tools hid-ite tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Cc: Roderick Colenbrander Signed-off-by: Peter Hutterer Signed-off-by: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 1 + tools/testing/selftests/hid/hid-ite.sh | 7 + .../selftests/hid/tests/test_ite_keyboard.py | 166 +++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-ite.sh create mode 100644 tools/testing/selftests/hid/tests/test_ite_keyboard.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index ce03c65bfba0..3ca696c44aab 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -8,6 +8,7 @@ include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh TEST_PROGS += hid-apple.sh TEST_PROGS += hid-gamepad.sh +TEST_PROGS += hid-ite.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index 46e745630f02..d4e3e0374ddd 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -23,5 +23,6 @@ CONFIG_UHID=y CONFIG_USB=y CONFIG_USB_HID=y CONFIG_HID_APPLE=y +CONFIG_HID_ITE=y CONFIG_HID_MULTITOUCH=y CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-ite.sh b/tools/testing/selftests/hid/hid-ite.sh new file mode 100755 index 000000000000..52c5ccf42292 --- /dev/null +++ b/tools/testing/selftests/hid/hid-ite.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_ite_keyboard.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_ite_keyboard.py b/tools/testing/selftests/hid/tests/test_ite_keyboard.py new file mode 100644 index 000000000000..38550c167bae --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_ite_keyboard.py @@ -0,0 +1,166 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020 Benjamin Tissoires +# Copyright (c) 2020 Red Hat, Inc. +# + +from .test_keyboard import ArrayKeyboard, TestArrayKeyboard +from hidtools.util import BusType + +import libevdev +import logging + +logger = logging.getLogger("hidtools.test.ite-keyboard") + +KERNEL_MODULE = ("itetech", "hid_ite") + + +class KbdData(object): + pass + + +# The ITE keyboards have an issue regarding the Wifi key: +# nothing comes in when pressing the key, but we get a null +# event on the key release. +# This test covers this case. +class ITEKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x06, 0x85, 0xff, # Usage Page (Vendor Usage Page 0xff85) + 0x09, 0x95, # Usage (Vendor Usage 0x95) 3 + 0xa1, 0x01, # Collection (Application) 5 + 0x85, 0x5a, # .Report ID (90) 7 + 0x09, 0x01, # .Usage (Vendor Usage 0x01) 9 + 0x15, 0x00, # .Logical Minimum (0) 11 + 0x26, 0xff, 0x00, # .Logical Maximum (255) 13 + 0x75, 0x08, # .Report Size (8) 16 + 0x95, 0x10, # .Report Count (16) 18 + 0xb1, 0x00, # .Feature (Data,Arr,Abs) 20 + 0xc0, # End Collection 22 + 0x05, 0x01, # Usage Page (Generic Desktop) 23 + 0x09, 0x06, # Usage (Keyboard) 25 + 0xa1, 0x01, # Collection (Application) 27 + 0x85, 0x01, # .Report ID (1) 29 + 0x75, 0x01, # .Report Size (1) 31 + 0x95, 0x08, # .Report Count (8) 33 + 0x05, 0x07, # .Usage Page (Keyboard) 35 + 0x19, 0xe0, # .Usage Minimum (224) 37 + 0x29, 0xe7, # .Usage Maximum (231) 39 + 0x15, 0x00, # .Logical Minimum (0) 41 + 0x25, 0x01, # .Logical Maximum (1) 43 + 0x81, 0x02, # .Input (Data,Var,Abs) 45 + 0x95, 0x01, # .Report Count (1) 47 + 0x75, 0x08, # .Report Size (8) 49 + 0x81, 0x03, # .Input (Cnst,Var,Abs) 51 + 0x95, 0x05, # .Report Count (5) 53 + 0x75, 0x01, # .Report Size (1) 55 + 0x05, 0x08, # .Usage Page (LEDs) 57 + 0x19, 0x01, # .Usage Minimum (1) 59 + 0x29, 0x05, # .Usage Maximum (5) 61 + 0x91, 0x02, # .Output (Data,Var,Abs) 63 + 0x95, 0x01, # .Report Count (1) 65 + 0x75, 0x03, # .Report Size (3) 67 + 0x91, 0x03, # .Output (Cnst,Var,Abs) 69 + 0x95, 0x06, # .Report Count (6) 71 + 0x75, 0x08, # .Report Size (8) 73 + 0x15, 0x00, # .Logical Minimum (0) 75 + 0x26, 0xff, 0x00, # .Logical Maximum (255) 77 + 0x05, 0x07, # .Usage Page (Keyboard) 80 + 0x19, 0x00, # .Usage Minimum (0) 82 + 0x2a, 0xff, 0x00, # .Usage Maximum (255) 84 + 0x81, 0x00, # .Input (Data,Arr,Abs) 87 + 0xc0, # End Collection 89 + 0x05, 0x0c, # Usage Page (Consumer Devices) 90 + 0x09, 0x01, # Usage (Consumer Control) 92 + 0xa1, 0x01, # Collection (Application) 94 + 0x85, 0x02, # .Report ID (2) 96 + 0x19, 0x00, # .Usage Minimum (0) 98 + 0x2a, 0x3c, 0x02, # .Usage Maximum (572) 100 + 0x15, 0x00, # .Logical Minimum (0) 103 + 0x26, 0x3c, 0x02, # .Logical Maximum (572) 105 + 0x75, 0x10, # .Report Size (16) 108 + 0x95, 0x01, # .Report Count (1) 110 + 0x81, 0x00, # .Input (Data,Arr,Abs) 112 + 0xc0, # End Collection 114 + 0x05, 0x01, # Usage Page (Generic Desktop) 115 + 0x09, 0x0c, # Usage (Wireless Radio Controls) 117 + 0xa1, 0x01, # Collection (Application) 119 + 0x85, 0x03, # .Report ID (3) 121 + 0x15, 0x00, # .Logical Minimum (0) 123 + 0x25, 0x01, # .Logical Maximum (1) 125 + 0x09, 0xc6, # .Usage (Wireless Radio Button) 127 + 0x95, 0x01, # .Report Count (1) 129 + 0x75, 0x01, # .Report Size (1) 131 + 0x81, 0x06, # .Input (Data,Var,Rel) 133 + 0x75, 0x07, # .Report Size (7) 135 + 0x81, 0x03, # .Input (Cnst,Var,Abs) 137 + 0xc0, # End Collection 139 + 0x05, 0x88, # Usage Page (Vendor Usage Page 0x88) 140 + 0x09, 0x01, # Usage (Vendor Usage 0x01) 142 + 0xa1, 0x01, # Collection (Application) 144 + 0x85, 0x04, # .Report ID (4) 146 + 0x19, 0x00, # .Usage Minimum (0) 148 + 0x2a, 0xff, 0xff, # .Usage Maximum (65535) 150 + 0x15, 0x00, # .Logical Minimum (0) 153 + 0x26, 0xff, 0xff, # .Logical Maximum (65535) 155 + 0x75, 0x08, # .Report Size (8) 158 + 0x95, 0x02, # .Report Count (2) 160 + 0x81, 0x02, # .Input (Data,Var,Abs) 162 + 0xc0, # End Collection 164 + 0x05, 0x01, # Usage Page (Generic Desktop) 165 + 0x09, 0x80, # Usage (System Control) 167 + 0xa1, 0x01, # Collection (Application) 169 + 0x85, 0x05, # .Report ID (5) 171 + 0x19, 0x81, # .Usage Minimum (129) 173 + 0x29, 0x83, # .Usage Maximum (131) 175 + 0x15, 0x00, # .Logical Minimum (0) 177 + 0x25, 0x01, # .Logical Maximum (1) 179 + 0x95, 0x08, # .Report Count (8) 181 + 0x75, 0x01, # .Report Size (1) 183 + 0x81, 0x02, # .Input (Data,Var,Abs) 185 + 0xc0, # End Collection 187 + ] + # fmt: on + + def __init__( + self, + rdesc=report_descriptor, + name=None, + input_info=(BusType.USB, 0x06CB, 0x2968), + ): + super().__init__(rdesc, name, input_info) + + def event(self, keys, reportID=None, application=None): + application = application or "Keyboard" + return super().event(keys, reportID, application) + + +class TestITEKeyboard(TestArrayKeyboard): + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + return ITEKeyboard() + + def test_wifi_key(self): + uhdev = self.uhdev + syn_event = self.syn_event + + # the following sends a 'release' event on the Wifi key. + # the kernel is supposed to translate this into Wifi key + # down and up + r = [0x03, 0x00] + uhdev.call_input_event(r) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 1)) + events = uhdev.next_sync_events() + self.debug_reports([r], uhdev, events) + self.assertInputEventsIn(expected, events) + + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 0)) + # the kernel sends the two down/up key events in a batch, no need to + # call events = uhdev.next_sync_events() + self.debug_reports([], uhdev, events) + self.assertInputEventsIn(expected, events) -- cgit v1.2.3-58-ga151 From a4ee40b6ac281125c3b7aa3e42401b271c50c7bc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 13:56:04 +0100 Subject: selftests: hid: import hid-tools hid-sony and hid-playstation tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. The code is taken from [1] to fix a change in v6.3. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Link: https://gitlab.freedesktop.org/libevdev/hid-tools/-/merge_requests/143 [1] Cc: Roderick Colenbrander Cc: Jose Torreguitar Signed-off-by: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 5 + tools/testing/selftests/hid/hid-sony.sh | 7 + tools/testing/selftests/hid/tests/test_sony.py | 342 +++++++++++++++++++++++++ 4 files changed, 355 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-sony.sh create mode 100644 tools/testing/selftests/hid/tests/test_sony.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 3ca696c44aab..dcea4f1e9369 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -12,6 +12,7 @@ TEST_PROGS += hid-ite.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh +TEST_PROGS += hid-sony.sh TEST_PROGS += hid-tablet.sh TEST_PROGS += hid-wacom.sh diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index d4e3e0374ddd..4f425178b56f 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -20,9 +20,14 @@ CONFIG_HID=y CONFIG_HID_BPF=y CONFIG_INPUT_EVDEV=y CONFIG_UHID=y +CONFIG_LEDS_CLASS_MULTICOLOR=y CONFIG_USB=y CONFIG_USB_HID=y CONFIG_HID_APPLE=y CONFIG_HID_ITE=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLAYSTATION=y +CONFIG_PLAYSTATION_FF=y +CONFIG_HID_SONY=y +CONFIG_SONY_FF=y CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-sony.sh b/tools/testing/selftests/hid/hid-sony.sh new file mode 100755 index 000000000000..c863c442686e --- /dev/null +++ b/tools/testing/selftests/hid/hid-sony.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_sony.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_sony.py b/tools/testing/selftests/hid/tests/test_sony.py new file mode 100644 index 000000000000..7e52c28e59c5 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_sony.py @@ -0,0 +1,342 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020 Benjamin Tissoires +# Copyright (c) 2020 Red Hat, Inc. +# + +from .base import application_matches +from .test_gamepad import BaseTest +from hidtools.device.sony_gamepad import ( + PS3Controller, + PS4ControllerBluetooth, + PS4ControllerUSB, + PS5ControllerBluetooth, + PS5ControllerUSB, + PSTouchPoint, +) +from hidtools.util import BusType + +import libevdev +import logging +import pytest + +logger = logging.getLogger("hidtools.test.sony") + +PS3_MODULE = ("sony", "hid_sony") +PS4_MODULE = ("playstation", "hid_playstation") +PS5_MODULE = ("playstation", "hid_playstation") + + +class SonyBaseTest: + class SonyTest(BaseTest.TestGamepad): + pass + + class SonyPS4ControllerTest(SonyTest): + kernel_modules = [PS4_MODULE] + + def test_accelerometer(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev("Accelerometer") + + for x in range(-32000, 32000, 4000): + r = uhdev.event(accel=(x, None, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X) in events + value = evdev.value[libevdev.EV_ABS.ABS_X] + # Check against range due to small loss in precision due + # to inverse calibration, followed by calibration by hid-sony. + assert x - 1 <= value <= x + 1 + + for y in range(-32000, 32000, 4000): + r = uhdev.event(accel=(None, y, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y) in events + value = evdev.value[libevdev.EV_ABS.ABS_Y] + assert y - 1 <= value <= y + 1 + + for z in range(-32000, 32000, 4000): + r = uhdev.event(accel=(None, None, z)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Z) in events + value = evdev.value[libevdev.EV_ABS.ABS_Z] + assert z - 1 <= value <= z + 1 + + def test_gyroscope(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev("Accelerometer") + + for rx in range(-2000000, 2000000, 200000): + r = uhdev.event(gyro=(rx, None, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RX) in events + value = evdev.value[libevdev.EV_ABS.ABS_RX] + # Sensor internal value is 16-bit, but calibrated is 22-bit, so + # 6-bit (64) difference, so allow a range of +/- 64. + assert rx - 64 <= value <= rx + 64 + + for ry in range(-2000000, 2000000, 200000): + r = uhdev.event(gyro=(None, ry, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RY) in events + value = evdev.value[libevdev.EV_ABS.ABS_RY] + assert ry - 64 <= value <= ry + 64 + + for rz in range(-2000000, 2000000, 200000): + r = uhdev.event(gyro=(None, None, rz)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RZ) in events + value = evdev.value[libevdev.EV_ABS.ABS_RZ] + assert rz - 64 <= value <= rz + 64 + + def test_battery(self): + uhdev = self.uhdev + + assert uhdev.power_supply_class is not None + + # DS4 capacity levels are in increments of 10. + # Battery is never below 5%. + for i in range(5, 105, 10): + uhdev.battery.capacity = i + uhdev.event() + assert uhdev.power_supply_class.capacity == i + + # Discharging tests only make sense for BlueTooth. + if uhdev.bus == BusType.BLUETOOTH: + uhdev.battery.cable_connected = False + uhdev.battery.capacity = 45 + uhdev.event() + assert uhdev.power_supply_class.status == "Discharging" + + uhdev.battery.cable_connected = True + uhdev.battery.capacity = 5 + uhdev.event() + assert uhdev.power_supply_class.status == "Charging" + + uhdev.battery.capacity = 100 + uhdev.event() + assert uhdev.power_supply_class.status == "Charging" + + uhdev.battery.full = True + uhdev.event() + assert uhdev.power_supply_class.status == "Full" + + def test_mt_single_touch(self): + """send a single touch in the first slot of the device, + and release it.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev("Touch Pad") + + t0 = PSTouchPoint(1, 50, 100) + r = uhdev.event(touch=[t0]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + + t0.tipswitch = False + r = uhdev.event(touch=[t0]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_mt_dual_touch(self): + """Send 2 touches in the first 2 slots. + Make sure the kernel sees this as a dual touch. + Release and check + + Note: PTP will send here BTN_DOUBLETAP emulation""" + uhdev = self.uhdev + evdev = uhdev.get_evdev("Touch Pad") + + t0 = PSTouchPoint(1, 50, 100) + t1 = PSTouchPoint(2, 150, 200) + + r = uhdev.event(touch=[t0]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + r = uhdev.event(touch=[t0, t1]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events + ) + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events + ) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + + t0.tipswitch = False + r = uhdev.event(touch=[t0, t1]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events + + t1.tipswitch = False + r = uhdev.event(touch=[t1]) + + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + +class TestPS3Controller(SonyBaseTest.SonyTest): + kernel_modules = [PS3_MODULE] + + def create_device(self): + controller = PS3Controller() + controller.application_matches = application_matches + return controller + + @pytest.fixture(autouse=True) + def start_controller(self): + # emulate a 'PS' button press to tell the kernel we are ready to accept events + self.assert_button(17) + + # drain any remaining udev events + while self.uhdev.dispatch(10): + pass + + def test_led(self): + for k, v in self.uhdev.led_classes.items(): + # the kernel might have set a LED for us + logger.info(f"{k}: {v.brightness}") + + idx = int(k[-1]) - 1 + assert self.uhdev.hw_leds.get_led(idx)[0] == bool(v.brightness) + + v.brightness = 0 + self.uhdev.dispatch(10) + assert self.uhdev.hw_leds.get_led(idx)[0] is False + + v.brightness = v.max_brightness + self.uhdev.dispatch(10) + assert self.uhdev.hw_leds.get_led(idx)[0] + + +class CalibratedPS4Controller(object): + # DS4 reports uncalibrated sensor data. Calibration coefficients + # can be retrieved using a feature report (0x2 USB / 0x5 BT). + # The values below are the processed calibration values for the + # DS4s matching the feature reports of PS4ControllerBluetooth/USB + # as dumped from hid-sony 'ds4_get_calibration_data'. + # + # Note we duplicate those values here in case the kernel changes them + # so we can have tests passing even if hid-tools doesn't have the + # correct values. + accelerometer_calibration_data = { + "x": {"bias": -73, "numer": 16384, "denom": 16472}, + "y": {"bias": -352, "numer": 16384, "denom": 16344}, + "z": {"bias": 81, "numer": 16384, "denom": 16319}, + } + gyroscope_calibration_data = { + "x": {"bias": 0, "numer": 1105920, "denom": 17827}, + "y": {"bias": 0, "numer": 1105920, "denom": 17777}, + "z": {"bias": 0, "numer": 1105920, "denom": 17748}, + } + + +class CalibratedPS4ControllerBluetooth(CalibratedPS4Controller, PS4ControllerBluetooth): + pass + + +class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest): + def create_device(self): + controller = CalibratedPS4ControllerBluetooth() + controller.application_matches = application_matches + return controller + + +class CalibratedPS4ControllerUSB(CalibratedPS4Controller, PS4ControllerUSB): + pass + + +class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest): + def create_device(self): + controller = CalibratedPS4ControllerUSB() + controller.application_matches = application_matches + return controller + + +class CalibratedPS5Controller(object): + # DualSense reports uncalibrated sensor data. Calibration coefficients + # can be retrieved using feature report 0x09. + # The values below are the processed calibration values for the + # DualSene matching the feature reports of PS5ControllerBluetooth/USB + # as dumped from hid-playstation 'dualsense_get_calibration_data'. + # + # Note we duplicate those values here in case the kernel changes them + # so we can have tests passing even if hid-tools doesn't have the + # correct values. + accelerometer_calibration_data = { + "x": {"bias": 0, "numer": 16384, "denom": 16374}, + "y": {"bias": -114, "numer": 16384, "denom": 16362}, + "z": {"bias": 2, "numer": 16384, "denom": 16395}, + } + gyroscope_calibration_data = { + "x": {"bias": 0, "numer": 1105920, "denom": 17727}, + "y": {"bias": 0, "numer": 1105920, "denom": 17728}, + "z": {"bias": 0, "numer": 1105920, "denom": 17769}, + } + + +class CalibratedPS5ControllerBluetooth(CalibratedPS5Controller, PS5ControllerBluetooth): + pass + + +class TestPS5ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest): + kernel_modules = [PS5_MODULE] + + def create_device(self): + controller = CalibratedPS5ControllerBluetooth() + controller.application_matches = application_matches + return controller + + +class CalibratedPS5ControllerUSB(CalibratedPS5Controller, PS5ControllerUSB): + pass + + +class TestPS5ControllerUSB(SonyBaseTest.SonyPS4ControllerTest): + kernel_modules = [PS5_MODULE] + + def create_device(self): + controller = CalibratedPS5ControllerUSB() + controller.application_matches = application_matches + return controller -- cgit v1.2.3-58-ga151 From bf81de760a8364656441a1fe067c17fa5261da5c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 17 Feb 2023 14:04:41 +0100 Subject: selftests: hid: import hid-tools usb-crash tests These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. This one gets skipped when run by vmtest.sh as we currently need to test against actual kernel modules (.ko), not built-in to fetch the list of supported devices. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-usb_crash.sh | 7 ++ .../testing/selftests/hid/tests/test_usb_crash.py | 103 +++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100755 tools/testing/selftests/hid/hid-usb_crash.sh create mode 100644 tools/testing/selftests/hid/tests/test_usb_crash.py (limited to 'tools') diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index dcea4f1e9369..01c0491d64da 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -14,6 +14,7 @@ TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh TEST_PROGS += hid-sony.sh TEST_PROGS += hid-tablet.sh +TEST_PROGS += hid-usb_crash.sh TEST_PROGS += hid-wacom.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-usb_crash.sh b/tools/testing/selftests/hid/hid-usb_crash.sh new file mode 100755 index 000000000000..3f0debe7e8fd --- /dev/null +++ b/tools/testing/selftests/hid/hid-usb_crash.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_usb_crash.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_usb_crash.py b/tools/testing/selftests/hid/tests/test_usb_crash.py new file mode 100644 index 000000000000..e98bff9197c7 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_usb_crash.py @@ -0,0 +1,103 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021 Benjamin Tissoires +# Copyright (c) 2021 Red Hat, Inc. +# + +# This is to ensure we don't crash when emulating USB devices + +from . import base +import pytest +import logging + +logger = logging.getLogger("hidtools.test.usb") + + +class USBDev(base.UHIDTestDevice): + # 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, name=None, input_info=None): + super().__init__( + name, "Mouse", input_info=input_info, rdesc=USBDev.report_descriptor + ) + + # skip witing for udev events, it's likely that the report + # descriptor is wrong + def is_ready(self): + return True + + # we don't have an evdev node here, so paper over + # the checks + def get_evdev(self, application=None): + return "OK" + + +class TestUSBDevice(base.BaseTestCase.TestUhid): + """ + Test class to test if an emulated USB device crashes + the kernel. + """ + + # conftest.py is generating the following fixture: + # + # @pytest.fixture(params=[('modulename', 1, 2)]) + # def usbVidPid(self, request): + # return request.param + + @pytest.fixture() + def new_uhdev(self, usbVidPid, request): + self.module, self.vid, self.pid = usbVidPid + self._load_kernel_module(None, self.module) + return USBDev(input_info=(3, self.vid, self.pid)) + + def test_creation(self): + """ + inject the USB dev through uhid and immediately see if there is a crash: + + uhid can create a USB device with the BUS_USB bus, and some + drivers assume that they can then access USB related structures + when they are actually provided a uhid device. This leads to + a crash because those access result in a segmentation fault. + + The kernel should not crash on any (random) user space correct + use of its API. So run through all available modules and declared + devices to see if we can generate a uhid device without a crash. + + The test is empty as the fixture `check_taint` is doing the job (and + honestly, when the kernel crashes, the whole machine freezes). + """ + assert True -- cgit v1.2.3-58-ga151