Commit ab396fb2 authored by Jiri Kosina's avatar Jiri Kosina

Merge branch 'for-6.4/tests' into for-linus

- import of bunch of HID selftests from out-of-tree hid-tools project
  (Benjamin Tissoires)
parents 67471b89 bf81de76
......@@ -5,6 +5,18 @@ include ../../../build/Build.include
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-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-usb_crash.sh
TEST_PROGS += hid-wacom.sh
CXX ?= $(CROSS_COMPILE)g++
HOSTPKG_CONFIG := pkg-config
......
......@@ -20,3 +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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
# HID tests can be long, so give a little bit more time
# to them
timeout=200
# SPDX-License-Identifier: GPL-2.0
# Just to make sphinx-apidoc document this directory
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
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
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
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)
# 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
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2019 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2019 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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()
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
#
# 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 <http://www.gnu.org/licenses/>.
#
# 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
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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)
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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()
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
#
from . import 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 == []
This source diff could not be displayed because it is too large. You can view the blob instead.
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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),
)
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
# Copyright (c) 2020 Wacom Technology Corp.
#
# Authors:
# Jason Gerecke <jason.gerecke@wacom.com>
"""
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),
],
)
......@@ -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="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests"
usage()
{
......@@ -33,9 +35,9 @@ usage()
Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>]
<command> 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=<kernel_build_path> $0 -- ./hid_bpf
O=<kernel_build_path> $0 -- ./tools/testing/selftests/hid/hid_bpf
or
KBUILD_OUTPUT=<kernel_build_path> $0 -- ./hid_bpf
KBUILD_OUTPUT=<kernel_build_path> $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 <<EOF
Could not find ${QEMU_BINARY}
......@@ -273,7 +278,7 @@ main()
fi
update_selftests "${kernel_checkout}" "${make_command}"
run_vm $b2c "${kernel_bzimage}" "${command}"
run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}"
if [[ "${debug_shell}" != "yes" ]]; then
echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
fi
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment