summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/hid
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/hid')
-rw-r--r--tools/testing/selftests/hid/Makefile11
-rw-r--r--tools/testing/selftests/hid/hid_bpf.c176
-rw-r--r--tools/testing/selftests/hid/hid_common.h25
-rw-r--r--tools/testing/selftests/hid/progs/hid.c43
-rw-r--r--tools/testing/selftests/hid/progs/hid_bpf_helpers.h23
-rw-r--r--tools/testing/selftests/hid/tests/conftest.py14
-rw-r--r--tools/testing/selftests/hid/tests/test_multitouch.py175
-rw-r--r--tools/testing/selftests/hid/tests/test_wacom_generic.py34
8 files changed, 418 insertions, 83 deletions
diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 2839d2612ce3..2f423de83147 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -24,7 +24,7 @@ CXX ?= $(CROSS_COMPILE)g++
HOSTPKG_CONFIG := pkg-config
-CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT)
+CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES)
CFLAGS += -I$(OUTPUT)/tools/include
LDLIBS += -lelf -lz -lrt -lpthread
@@ -105,13 +105,6 @@ $(MAKE_DIRS):
$(call msg,MKDIR,,$@)
$(Q)mkdir -p $@
-# LLVM's ld.lld doesn't support all the architectures, so use it only on x86
-ifeq ($(SRCARCH),x86)
-LLD := lld
-else
-LLD := ld
-endif
-
DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
TEST_GEN_PROGS_EXTENDED += $(DEFAULT_BPFTOOL)
@@ -184,6 +177,8 @@ MENDIAN=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
BPF_CFLAGS = -g -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN) \
+ -Wno-microsoft-anon-tag \
+ -fms-extensions \
-I$(INCLUDE_DIR)
CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) \
diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index 1e979fb3542b..32d81ba15a25 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -5,7 +5,7 @@
#include <bpf/bpf.h>
struct hid_hw_request_syscall_args {
- __u8 data[10];
+ __u8 data[MAX_BUF_SIZE];
unsigned int hid;
int retval;
size_t size;
@@ -54,11 +54,27 @@ FIXTURE_TEARDOWN(hid_bpf) {
hid_bpf_teardown(_metadata, self, variant); \
} while (0)
+FIXTURE_VARIANT(hid_bpf) {
+ __u8 *rdesc;
+ size_t rdesc_size;
+};
+
+FIXTURE_VARIANT_ADD(hid_bpf, numbered) {
+ .rdesc = rdesc,
+ .rdesc_size = sizeof(rdesc),
+};
+
+FIXTURE_VARIANT_ADD(hid_bpf, unnumbered) {
+ .rdesc = fido2_rdesc,
+ .rdesc_size = sizeof(fido2_rdesc),
+};
+
FIXTURE_SETUP(hid_bpf)
{
int err;
- err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36, rdesc, sizeof(rdesc));
+ err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36,
+ variant->rdesc, variant->rdesc_size);
ASSERT_OK(err);
}
@@ -67,14 +83,17 @@ struct test_program {
int insert_head;
};
#define LOAD_PROGRAMS(progs) \
- load_programs(progs, ARRAY_SIZE(progs), _metadata, self, variant)
+ load_programs(progs, ARRAY_SIZE(progs), false, _metadata, self, variant)
+#define LOAD_PROGRAMS_MAY_FAIL(progs) \
+ load_programs(progs, ARRAY_SIZE(progs), true, _metadata, self, variant)
#define LOAD_BPF \
- load_programs(NULL, 0, _metadata, self, variant)
-static void load_programs(const struct test_program programs[],
- const size_t progs_count,
- struct __test_metadata *_metadata,
- FIXTURE_DATA(hid_bpf) * self,
- const FIXTURE_VARIANT(hid_bpf) * variant)
+ load_programs(NULL, 0, false, _metadata, self, variant)
+static int load_programs(const struct test_program programs[],
+ const size_t progs_count,
+ bool load_may_fail,
+ struct __test_metadata *_metadata,
+ FIXTURE_DATA(hid_bpf) * self,
+ const FIXTURE_VARIANT(hid_bpf) * variant)
{
struct bpf_map *iter_map;
int err = -EINVAL;
@@ -86,6 +105,20 @@ static void load_programs(const struct test_program programs[],
self->skel = hid__open();
ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open");
+ /*
+ * Disable all struct_ops maps by default so libbpf does not autoload
+ * programs referenced by maps that are unrelated to the current test.
+ */
+ bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) {
+ if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) {
+ err = bpf_map__set_autocreate(iter_map, false);
+ ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'",
+ bpf_map__name(iter_map));
+ }
+
+ bpf_map__set_autoattach(iter_map, false);
+ }
+
for (int i = 0; i < progs_count; i++) {
struct bpf_program *prog;
struct bpf_map *map;
@@ -102,6 +135,10 @@ static void load_programs(const struct test_program programs[],
ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
programs[i].name + 4);
+ err = bpf_map__set_autocreate(map, true);
+ ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'",
+ programs[i].name + 4);
+
/* hid_id is the first field of struct hid_bpf_ops */
ops_hid_id = bpf_map__initial_value(map, NULL);
ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data");
@@ -109,14 +146,10 @@ static void load_programs(const struct test_program programs[],
*ops_hid_id = self->hid.hid_id;
}
- /* we disable the auto-attach feature of all maps because we
- * only want the tested one to be manually attached in the next
- * call to bpf_map__attach_struct_ops()
- */
- bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj)
- bpf_map__set_autoattach(iter_map, false);
-
err = hid__load(self->skel);
+ if (err && load_may_fail)
+ return err;
+
ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err);
for (int i = 0; i < progs_count; i++) {
@@ -136,6 +169,7 @@ static void load_programs(const struct test_program programs[],
self->hidraw_fd = open_hidraw(&self->hid);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
+ return 0;
}
/*
@@ -157,7 +191,7 @@ TEST_F(hid_bpf, raw_event)
const struct test_program progs[] = {
{ .name = "hid_first_event" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -208,7 +242,7 @@ TEST_F(hid_bpf, subprog_raw_event)
const struct test_program progs[] = {
{ .name = "hid_subprog_first_event" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -266,7 +300,7 @@ TEST_F(hid_bpf, test_attach_detach)
{ .name = "hid_second_event" },
};
struct bpf_link *link;
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err, link_fd;
LOAD_PROGRAMS(progs);
@@ -351,7 +385,7 @@ TEST_F(hid_bpf, test_hid_change_report)
const struct test_program progs[] = {
{ .name = "hid_change_report_id" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -378,21 +412,24 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
{
struct hid_hw_request_syscall_args args = {
.retval = -1,
- .size = 10,
+ .size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
.ctx_size_in = sizeof(args),
);
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err, prog_fd;
LOAD_BPF;
args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */
- args.data[1] = 2; /* report ID */
- args.data[2] = 42; /* report ID */
+ args.data[1] = 2;
+ args.data[2] = 42;
+
+ if (variant->rdesc == fido2_rdesc)
+ args.data[0] = 0;
prog_fd = bpf_program__fd(self->skel->progs.hid_user_input_report);
@@ -410,8 +447,13 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
/* read the data from hidraw */
memset(buf, 0, sizeof(buf));
err = read(self->hidraw_fd, buf, sizeof(buf));
- ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
- ASSERT_EQ(buf[0], 1);
+ if (variant->rdesc == rdesc) {
+ ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
+ } else {
+ ASSERT_EQ(err, 64)
+ TH_LOG("read_hidraw");
+ }
+ ASSERT_EQ(buf[0], args.data[0]);
ASSERT_EQ(buf[1], 2);
ASSERT_EQ(buf[2], 42);
}
@@ -424,7 +466,7 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
{
struct hid_hw_request_syscall_args args = {
.retval = -1,
- .size = 10,
+ .size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
@@ -437,8 +479,11 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */
- args.data[1] = 2; /* report ID */
- args.data[2] = 42; /* report ID */
+ args.data[1] = 2;
+ args.data[2] = 42;
+
+ if (variant->rdesc == fido2_rdesc)
+ args.data[0] = 0;
prog_fd = bpf_program__fd(self->skel->progs.hid_user_output_report);
@@ -454,9 +499,14 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
ASSERT_OK(cond_err) TH_LOG("error while calling waiting for the condition");
- ASSERT_EQ(args.retval, 3);
+ if (variant->rdesc == rdesc) {
+ ASSERT_EQ(args.retval, 3);
+ } else if (variant->rdesc == fido2_rdesc) {
+ ASSERT_EQ(args.retval, 65)
+ TH_LOG("report size error, should have 64 + 1 extra byte for the report ID 0");
+ }
- ASSERT_EQ(output_report[0], 1);
+ ASSERT_EQ(output_report[0], args.data[0]);
ASSERT_EQ(output_report[1], 2);
ASSERT_EQ(output_report[2], 42);
@@ -473,7 +523,7 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call)
.retval = -1,
.type = HID_FEATURE_REPORT,
.request_type = HID_REQ_GET_REPORT,
- .size = 10,
+ .size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
@@ -506,7 +556,7 @@ TEST_F(hid_bpf, test_hid_filter_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_filter_raw_request" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -559,7 +609,7 @@ TEST_F(hid_bpf, test_hid_change_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_hidraw_raw_request" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -585,7 +635,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_raw_request" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -608,7 +658,7 @@ TEST_F(hid_bpf, test_hid_filter_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_filter_output_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -661,7 +711,7 @@ TEST_F(hid_bpf, test_hid_change_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_hidraw_output_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -685,7 +735,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_output_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -711,7 +761,7 @@ TEST_F(hid_bpf, test_multiply_events_wq)
const struct test_program progs[] = {
{ .name = "hid_test_multiply_events_wq" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -749,7 +799,7 @@ TEST_F(hid_bpf, test_multiply_events)
const struct test_program progs[] = {
{ .name = "hid_test_multiply_events" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -783,7 +833,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_input_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_input_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -837,7 +887,7 @@ TEST_F(hid_bpf, test_hid_attach_flags)
.insert_head = 0,
},
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -868,6 +918,9 @@ TEST_F(hid_bpf, test_rdesc_fixup)
};
int err, desc_size;
+ if (variant->rdesc != rdesc)
+ SKIP(return, "not compatible report descriptor");
+
LOAD_PROGRAMS(progs);
/* check that hid_rdesc_fixup() was executed */
@@ -887,6 +940,45 @@ TEST_F(hid_bpf, test_rdesc_fixup)
ASSERT_EQ(rpt_desc.value[4], 0x42);
}
+TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
+{
+ const struct test_program progs[] = {
+ { .name = "hid_rdesc_fixup_get_data_overflow" },
+ };
+
+ /* newer verifier can detect the overflow at load time */
+ if (LOAD_PROGRAMS_MAY_FAIL(progs))
+ return;
+
+ ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
+}
+
+TEST_F(hid_bpf, test_rdesc_fixup_change_uniq_name_phys)
+{
+ const struct test_program progs[] = {
+ { .name = "hid_rdesc_fixup_change_uniq_name_phys" },
+ };
+ char expected[256], buf[256] = {};
+ int err;
+
+ LOAD_PROGRAMS(progs);
+
+ err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(buf)), buf);
+ ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWNAME");
+ ASSERT_STREQ("name coming from bpf", buf);
+
+ snprintf(expected, sizeof(expected), "%d phys:coming:from:bpf", self->hid.dev_id);
+
+ err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(buf)), buf);
+ ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWPHYS");
+ ASSERT_STREQ(expected, buf);
+
+ err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(buf)), buf);
+ ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ");
+ ASSERT_STREQ("uniq:coming:from:bpf", buf);
+
+}
+
static int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
diff --git a/tools/testing/selftests/hid/hid_common.h b/tools/testing/selftests/hid/hid_common.h
index e3b267446fa0..b7890ba2878f 100644
--- a/tools/testing/selftests/hid/hid_common.h
+++ b/tools/testing/selftests/hid/hid_common.h
@@ -13,6 +13,7 @@
#include <linux/uhid.h>
#define SHOW_UHID_DEBUG 0
+#define MAX_BUF_SIZE 128
#define min(a, b) \
({ __typeof__(a) _a = (a); \
@@ -97,6 +98,28 @@ static unsigned char rdesc[] = {
static __u8 feature_data[] = { 1, 2 };
+static __maybe_unused unsigned char fido2_rdesc[] = {
+ 0x06, 0xd0, 0xf1, /* Usage Page (FIDO Alliance) */
+ 0x09, 0x01, /* Usage (U2F Authenticator Device) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x20, /* Usage (Input Report Data) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x40, /* Report Count (64) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x21, /* Usage (Output Report Data) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x40, /* Report Count (64) */
+ 0x91, 0x02, /* Output (Data,Var,Abs) */
+ 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
+ 0x09, 0x22, /* Usage (Vendor Usage 0x22) */
+ 0xb1, 0x02, /* Feature (Data,Var,Abs) */
+ 0xc0, /* End Collection */
+};
+
#define ASSERT_OK(data) ASSERT_FALSE(data)
#define ASSERT_OK_PTR(ptr) ASSERT_NE(NULL, ptr)
@@ -110,7 +133,7 @@ static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t uhid_output_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t uhid_output_cond = PTHREAD_COND_INITIALIZER;
-static unsigned char output_report[10];
+static unsigned char output_report[MAX_BUF_SIZE];
/* no need to protect uhid_stopped, only one thread accesses it */
static bool uhid_stopped;
diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c
index 5ecc845ef792..48aa8088cc53 100644
--- a/tools/testing/selftests/hid/progs/hid.c
+++ b/tools/testing/selftests/hid/progs/hid.c
@@ -13,6 +13,7 @@ struct attach_prog_args {
__u64 callback_check = 52;
__u64 callback2_check = 52;
+__u64 get_data_overflow_check;
SEC("?struct_ops/hid_device_event")
int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
@@ -97,7 +98,7 @@ struct hid_bpf_ops change_report_id = {
struct hid_hw_request_syscall_args {
/* data needs to come at offset 0 so we can use it in calls */
- __u8 data[10];
+ __u8 data[128];
unsigned int hid;
int retval;
size_t size;
@@ -240,6 +241,46 @@ struct hid_bpf_ops rdesc_fixup = {
.hid_rdesc_fixup = (void *)hid_rdesc_fixup,
};
+SEC("?struct_ops.s/hid_rdesc_fixup")
+int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx)
+{
+ if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */))
+ get_data_overflow_check = 1;
+
+ return 0;
+}
+
+SEC(".struct_ops.link")
+struct hid_bpf_ops rdesc_fixup_get_data_overflow = {
+ .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow,
+};
+
+SEC("?struct_ops.s/hid_rdesc_fixup")
+int BPF_PROG(hid_rdesc_fixup_change_uniq_name_phys, struct hid_bpf_ctx *hid_ctx)
+{
+#define HID_BPF_MEMCPY(target, str) \
+ __builtin_memcpy(target, str, sizeof(str))
+
+ HID_BPF_MEMCPY(hid_ctx->hid->name, "name coming from bpf");
+ HID_BPF_MEMCPY(hid_ctx->hid->uniq, "uniq:coming:from:bpf");
+ /* hid_bpf relies on a phys being a rand % 1024 */
+ for (int i = 0; i < 5; i++) {
+ if (!hid_ctx->hid->phys[i]) {
+ HID_BPF_MEMCPY(hid_ctx->hid->phys + i, " phys:coming:from:bpf");
+ break;
+ }
+ }
+
+#undef HID_BPF_MEMCPY
+
+ return 0;
+}
+
+SEC(".struct_ops.link")
+struct hid_bpf_ops rdesc_fixup_change_uniq_name_phys = {
+ .hid_rdesc_fixup = (void *)hid_rdesc_fixup_change_uniq_name_phys,
+};
+
SEC("?struct_ops/hid_device_event")
int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
index 531228b849da..05698793762a 100644
--- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
+++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
@@ -6,8 +6,10 @@
#define __HID_BPF_HELPERS_H
/* "undefine" structs and enums in vmlinux.h, because we "override" them below */
+#define bpf_wq bpf_wq___not_used
#define hid_bpf_ctx hid_bpf_ctx___not_used
#define hid_bpf_ops hid_bpf_ops___not_used
+#define hid_device hid_device___not_used
#define hid_report_type hid_report_type___not_used
#define hid_class_request hid_class_request___not_used
#define hid_bpf_attach_flags hid_bpf_attach_flags___not_used
@@ -27,8 +29,10 @@
#include "vmlinux.h"
+#undef bpf_wq
#undef hid_bpf_ctx
#undef hid_bpf_ops
+#undef hid_device
#undef hid_report_type
#undef hid_class_request
#undef hid_bpf_attach_flags
@@ -55,6 +59,17 @@ enum hid_report_type {
HID_REPORT_TYPES,
};
+struct hid_device {
+ unsigned int id;
+ char name[128];
+ char phys[64];
+ char uniq[64];
+} __attribute__((preserve_access_index));
+
+struct bpf_wq {
+ __u64 __opaque[2];
+};
+
struct hid_bpf_ctx {
struct hid_device *hid;
__u32 allocated_size;
@@ -116,10 +131,8 @@ extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx,
/* bpf_wq implementation */
extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
-extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
- int (callback_fn)(void *map, int *key, void *wq),
- unsigned int flags__k, void *aux__ign) __weak __ksym;
-#define bpf_wq_set_callback(timer, cb, flags) \
- bpf_wq_set_callback_impl(timer, cb, flags, NULL)
+extern int bpf_wq_set_callback(struct bpf_wq *wq,
+ int (*callback_fn)(void *, int *, void *),
+ unsigned int flags) __weak __ksym;
#endif /* __HID_BPF_HELPERS_H */
diff --git a/tools/testing/selftests/hid/tests/conftest.py b/tools/testing/selftests/hid/tests/conftest.py
index 1361ec981db6..985a535324b2 100644
--- a/tools/testing/selftests/hid/tests/conftest.py
+++ b/tools/testing/selftests/hid/tests/conftest.py
@@ -5,6 +5,7 @@
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
+from packaging.version import Version
import platform
import pytest
import re
@@ -14,6 +15,19 @@ from .base import HIDTestUdevRule
from pathlib import Path
+@pytest.fixture(autouse=True)
+def hidtools_version_check():
+ HIDTOOLS_VERSION = "0.12"
+ try:
+ import hidtools
+
+ version = hidtools.__version__ # type: ignore
+ if Version(version) < Version(HIDTOOLS_VERSION):
+ pytest.skip(reason=f"have hidtools {version}, require >={HIDTOOLS_VERSION}")
+ except Exception:
+ pytest.skip(reason=f"hidtools >={HIDTOOLS_VERSION} required")
+
+
# 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")
diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py
index ece0ba8e7d34..7897340118b4 100644
--- a/tools/testing/selftests/hid/tests/test_multitouch.py
+++ b/tools/testing/selftests/hid/tests/test_multitouch.py
@@ -9,6 +9,7 @@
from . import base
from hidtools.hut import HUT
from hidtools.util import BusType
+import enum
import libevdev
import logging
import pytest
@@ -232,11 +233,17 @@ class Digitizer(base.UHIDTestDevice):
return 0
+class HIDButtonType(enum.IntEnum):
+ CLICKPAD = 0
+ PRESSUREPAD = 1
+ DISCRETE_BUTTONS = 2
+
+
class PTP(Digitizer):
def __init__(
self,
name,
- type="Click Pad",
+ buttontype=HIDButtonType.CLICKPAD,
rdesc_str=None,
rdesc=None,
application="Touch Pad",
@@ -244,11 +251,8 @@ class PTP(Digitizer):
max_contacts=None,
input_info=None,
):
- self.type = type.lower().replace(" ", "")
- if self.type == "clickpad":
- self.buttontype = 0
- else: # pressurepad
- self.buttontype = 1
+ self.buttontype = buttontype
+
self.clickpad_state = False
self.left_state = False
self.right_state = False
@@ -509,6 +513,79 @@ class SmartTechDigitizer(Digitizer):
return absinfo is not None and absinfo.resolution == 3
+class MinWin8TSParallelBigContactMax(Digitizer):
+ """A parallel Win8 touchscreen that advertises a ContactCountMaximum much
+ larger than the number of contacts it actually reports.
+
+ Such firmware makes the driver allocate that many input slots (up to 255)
+ while the input report only carries a few contacts. This is what used to
+ drive the per-slot bit operations on mt_io_flags out of bounds. The number
+ of contacts a HID report can describe is limited by the descriptor size,
+ so a large ContactCountMaximum can only be expressed this way, decoupled
+ from the number of finger collections."""
+
+ def __init__(self, n_fingers=5, contact_max=250):
+ self.phys_max = 120, 90
+ rdesc_finger_str = f"""
+ Usage Page (Digitizers)
+ Usage (Finger)
+ Collection (Logical)
+ Report Size (1)
+ Report Count (1)
+ Logical Minimum (0)
+ Logical Maximum (1)
+ Usage (Tip Switch)
+ Input (Data,Var,Abs)
+ Report Size (7)
+ Logical Maximum (127)
+ Input (Cnst,Var,Abs)
+ Report Size (8)
+ Logical Maximum (255)
+ Usage (Contact Id)
+ Input (Data,Var,Abs)
+ Report Size (16)
+ Unit Exponent (-1)
+ Unit (SILinear: cm)
+ Logical Maximum (4095)
+ Physical Minimum (0)
+ Physical Maximum ({self.phys_max[0]})
+ Usage Page (Generic Desktop)
+ Usage (X)
+ Input (Data,Var,Abs)
+ Physical Maximum ({self.phys_max[1]})
+ Usage (Y)
+ Input (Data,Var,Abs)
+ End Collection
+"""
+ rdesc_str = f"""
+ Usage Page (Digitizers)
+ Usage (Touch Screen)
+ Collection (Application)
+ Report ID (1)
+ {rdesc_finger_str * n_fingers}
+ Unit Exponent (-4)
+ Unit (SILinear: s)
+ Logical Maximum (65535)
+ Physical Maximum (65535)
+ Usage Page (Digitizers)
+ Usage (Scan Time)
+ Input (Data,Var,Abs)
+ Report Size (8)
+ Logical Maximum (255)
+ Usage (Contact Count)
+ Input (Data,Var,Abs)
+ Report ID (2)
+ Logical Maximum ({contact_max})
+ Usage (Contact Max)
+ Feature (Data,Var,Abs)
+ End Collection
+ {Digitizer.msCertificationBlob(68)}
+"""
+ super().__init__(
+ f"uhid test parallel big contact max {contact_max}", rdesc_str
+ )
+
+
class BaseTest:
class TestMultitouch(base.BaseTestCase.TestUhid):
kernel_modules = [KERNEL_MODULE]
@@ -975,15 +1052,36 @@ class BaseTest:
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_ORIENTATION, 90) in events
class TestPTP(TestWin8Multitouch):
+ def test_buttontype(self):
+ """Check for the right ButtonType."""
+ uhdev = self.uhdev
+ assert uhdev is not None
+ evdev = uhdev.get_evdev()
+
+ # If libevdev.so is not yet compiled with INPUT_PROP_PRESSUREPAD
+ # python-libevdev won't have it either, let's fake it
+ if not getattr(libevdev, "INPUT_PROP_PRESSUREPAD", None):
+ prop = libevdev.InputProperty(name="INPUT_PROP_PRESSUREPAD", value=0x7)
+ libevdev.INPUT_PROP_PRESSUREPAD = prop
+ libevdev.props.append(prop)
+
+ if uhdev.buttontype == HIDButtonType.CLICKPAD:
+ assert libevdev.INPUT_PROP_BUTTONPAD in evdev.properties
+ elif uhdev.buttontype == HIDButtonType.PRESSUREPAD:
+ assert libevdev.INPUT_PROP_PRESSUREPAD in evdev.properties
+ else:
+ assert libevdev.INPUT_PROP_PRESSUREPAD not in evdev.properties
+ assert libevdev.INPUT_PROP_BUTTONPAD not in evdev.properties
+
def test_ptp_buttons(self):
"""check for button reliability.
- There are 2 types of touchpads: the click pads and the pressure pads.
- Each should reliably report the BTN_LEFT events.
+ There are 3 types of touchpads: click pads + pressure pads and
+ those with discrete buttons. Each should reliably report the BTN_LEFT events.
"""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
- if uhdev.type == "clickpad":
+ if uhdev.buttontype in [HIDButtonType.CLICKPAD, HIDButtonType.PRESSUREPAD]:
r = uhdev.event(click=True)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
@@ -995,7 +1093,7 @@ class BaseTest:
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) in events
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
- else:
+ elif uhdev.buttontype == HIDButtonType.DISCRETE_BUTTONS:
r = uhdev.event(left=True)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
@@ -1710,6 +1808,47 @@ class TestMinWin8TSParallel(BaseTest.TestWin8Multitouch):
return MinWin8TSParallel(10)
+class TestMinWin8TSParallelBigContactMax(base.BaseTestCase.TestUhid):
+ """Regression test for the out-of-bounds bit operations on
+ struct mt_device.mt_io_flags.
+
+ A Win8 touchscreen may advertise a ContactCountMaximum much larger than
+ the number of contacts it reports. The driver used to keep the per-slot
+ active state in the bits of a single unsigned long while indexing
+ set_bit()/clear_bit() by the slot number, so such a device drove those bit
+ operations out of bounds. The sticky-fingers release timer made it fatal:
+ mt_release_contacts() cleared one bit per slot, overwrote the adjacent
+ struct mt_device members and panicked the kernel.
+
+ Send a single contact, let the 100ms sticky-fingers timer release it, and
+ check that the kernel reports the release cleanly instead of crashing."""
+
+ kernel_modules = [KERNEL_MODULE]
+
+ def create_device(self):
+ return MinWin8TSParallelBigContactMax()
+
+ def test_sticky_fingers_release_big_contact_max(self):
+ uhdev = self.uhdev
+ evdev = uhdev.get_evdev()
+
+ assert evdev.num_slots == uhdev.max_contacts
+
+ t0 = Touch(1, 5, 10)
+ r = uhdev.event([t0])
+ events = uhdev.next_sync_events()
+ self.debug_reports(r, uhdev, events)
+ assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
+
+ # do not release the contact; the sticky-fingers timer must do it
+ # after 100ms, which is where the out-of-bounds release used to hit
+ time.sleep(0.2)
+ events = uhdev.next_sync_events()
+ self.debug_reports(r, uhdev, events)
+ assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
+ assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+
+
class TestMinWin8TSHybrid(BaseTest.TestWin8Multitouch):
def create_device(self):
return MinWin8TSHybrid()
@@ -1918,7 +2057,7 @@ class Testdell_044e_1220(BaseTest.TestPTP):
def create_device(self):
return PTP(
"uhid test dell_044e_1220",
- type="pressurepad",
+ buttontype=HIDButtonType.DISCRETE_BUTTONS,
rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 09 38 95 01 81 06 05 0c 0a 38 02 81 06 c0 c0 05 0d 09 05 a1 01 85 08 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 af 04 75 10 55 0e 65 11 09 30 35 00 46 e8 03 95 01 81 02 26 7b 02 46 12 02 09 31 81 02 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 05 0d 09 56 81 02 09 54 25 05 95 01 75 08 81 02 05 09 19 01 29 03 25 01 75 01 95 03 81 02 95 05 81 03 05 0d 85 09 09 55 75 08 95 01 25 05 b1 02 06 00 ff 85 0a 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 01 ff 09 01 a1 01 85 03 09 01 15 00 26 ff 00 95 1b 81 02 85 04 09 02 95 50 81 02 85 05 09 03 95 07 b1 02 85 06 09 04 81 02 c0 06 02 ff 09 01 a1 01 85 07 09 02 95 86 75 08 b1 02 c0 05 0d 09 0e a1 01 85 0b 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 0c 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0",
)
@@ -2018,7 +2157,7 @@ class Testelan_04f3_313a(BaseTest.TestPTP):
def create_device(self):
return PTP(
"uhid test elan_04f3_313a",
- type="touchpad",
+ buttontype=HIDButtonType.DISCRETE_BUTTONS,
input_info=(BusType.I2C, 0x04F3, 0x313A),
rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 75 08 95 05 81 03 c0 06 00 ff 09 01 85 0e 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0a 09 c6 15 00 26 ff 00 75 08 95 04 b1 02 c0 06 00 ff 09 01 a1 01 85 5c 09 01 95 0b 75 08 81 06 85 0d 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0c 09 c6 96 80 03 75 08 b1 02 85 0b 09 c7 95 82 75 08 b1 02 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 05 09 09 02 09 03 15 00 25 01 75 01 95 02 81 02 05 0d 95 01 75 04 25 0f 09 51 81 02 05 01 15 00 26 d7 0e 75 10 55 0d 65 11 09 30 35 00 46 44 2f 95 01 81 02 46 12 16 26 eb 06 26 eb 06 09 31 81 02 05 0d 15 00 25 64 95 03 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 25 01 75 01 95 08 81 03 09 c5 75 08 95 02 81 03 05 0d 85 02 09 55 09 59 75 04 95 02 25 0f b1 02 85 07 09 60 75 01 95 01 15 00 25 01 b1 02 95 0f b1 03 06 00 ff 06 00 ff 85 06 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 03 09 22 a1 00 09 52 15 00 25 0a 75 10 95 01 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 75 01 95 02 25 01 b1 02 95 0e b1 03 c0 c0 05 01 09 02 a1 01 85 2a 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 03 05 01 09 30 09 31 15 81 25 7f 35 81 45 7f 55 00 65 13 75 08 95 02 81 06 75 08 95 05 81 03 c0 c0",
)
@@ -2058,6 +2197,16 @@ class Testite_06cb_2968(BaseTest.TestPTP):
)
+class Testven_0488_108c(BaseTest.TestPTP):
+ def create_device(self):
+ return PTP(
+ "uhid test ven_0488_108c",
+ rdesc="05 01 09 02 a1 01 85 06 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 03 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06 c0 c0 05 0d 09 05 a1 01 85 01 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 81 03 05 01 15 00 26 ba 0d 75 10 55 0e 65 11 09 30 35 00 46 d0 05 95 01 81 02 26 d0 06 46 bb 02 09 31 81 02 05 0d 95 01 75 10 26 ff 7f 46 ff 7f 09 30 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 81 03 05 01 15 00 26 ba 0d 75 10 55 0e 65 11 09 30 35 00 46 d0 05 95 01 81 02 26 d0 06 46 bb 02 09 31 81 02 05 0d 95 01 75 10 26 ff 7f 46 ff 7f 09 30 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 81 03 05 01 15 00 26 ba 0d 75 10 55 0e 65 11 09 30 35 00 46 d0 05 95 01 81 02 26 d0 06 46 bb 02 09 31 81 02 05 0d 95 01 75 10 26 ff 7f 46 ff 7f 09 30 81 02 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 05 0d 09 56 81 02 09 54 25 05 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 02 09 55 75 08 95 01 25 05 b1 02 09 59 b1 02 06 00 ff 85 03 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 05 0e 09 01 a1 02 85 13 09 23 15 00 25 64 75 08 95 01 b1 02 c0 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 01 ff 09 02 a1 01 09 00 85 07 15 00 26 ff 00 75 08 96 12 02 b1 02 c0 06 00 ff 09 01 a1 01 85 0d 15 00 26 ff 00 75 08 95 11 09 01 81 02 09 01 91 02 c0 05 0e 09 01 a1 01 85 11 09 35 15 00 26 ff 00 75 08 95 17 b1 02 c0 06 81 ff 09 01 a1 01 09 20 85 17 15 00 26 ff 00 75 08 95 3f 09 01 81 02 09 01 91 02 c0",
+ input_info=(0x18, 0x0488, 0x108C),
+ buttontype=HIDButtonType.PRESSUREPAD,
+ )
+
+
class Testn_trig_1b96_0c01(BaseTest.TestWin8Multitouch):
def create_device(self):
return Digitizer(
@@ -2110,7 +2259,7 @@ class Testsipodev_0603_0002(BaseTest.TestPTP):
def create_device(self):
return PTP(
"uhid test sipodev_0603_0002",
- type="clickpad",
+ buttontype=HIDButtonType.CLICKPAD,
rdesc="05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 02 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 80 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 75 01 95 02 81 03 95 01 75 04 25 05 09 51 81 02 05 01 15 00 26 44 0a 75 0c 55 0e 65 11 09 30 35 00 46 ac 03 95 01 81 02 46 fe 01 26 34 05 75 0c 09 31 81 02 05 0d c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 0a 95 01 75 04 81 02 75 01 95 03 81 03 05 09 09 01 25 01 75 01 95 01 81 02 05 0d 85 0a 09 55 09 59 75 04 95 02 25 0f b1 02 85 0b 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 09 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 06 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 07 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 05 01 09 0c a1 01 85 08 15 00 25 01 09 c6 75 01 95 01 81 06 75 07 81 03 c0 05 01 09 80 a1 01 85 01 15 00 25 01 75 01 0a 81 00 0a 82 00 0a 83 00 95 03 81 06 95 05 81 01 c0 06 0c 00 09 01 a1 01 85 02 25 01 15 00 75 01 0a b5 00 0a b6 00 0a b7 00 0a cd 00 0a e2 00 0a a2 00 0a e9 00 0a ea 00 95 08 81 02 0a 83 01 0a 6f 00 0a 70 00 0a 88 01 0a 8a 01 0a 92 01 0a a8 02 0a 24 02 95 08 81 02 0a 21 02 0a 23 02 0a 96 01 0a 25 02 0a 26 02 0a 27 02 0a 23 02 0a b1 02 95 08 81 02 c0 06 00 ff 09 01 a1 01 85 05 15 00 26 ff 00 19 01 29 02 75 08 95 05 b1 02 c0",
)
diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py
index 2d6d04f0ff80..3903f479b15b 100644
--- a/tools/testing/selftests/hid/tests/test_wacom_generic.py
+++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py
@@ -598,18 +598,6 @@ class BaseTest:
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 PenTabletTest(BaseTest.TestTablet):
def assertName(self, uhdev):
@@ -677,6 +665,15 @@ class TestOpaqueTablet(PenTabletTest):
uhdev.event(130, 240, pressure=0), [], auto_syn=False, strict=True
)
+ def test_prop_pointer(self):
+ """
+ Verify that INPUT_PROP_POINTER is set and INPUT_PROP_DIRECT
+ is not set on opaque devices.
+ """
+ evdev = self.uhdev.get_evdev()
+ assert libevdev.INPUT_PROP_POINTER in evdev.properties
+ assert libevdev.INPUT_PROP_DIRECT not in evdev.properties
+
class TestOpaqueCTLTablet(TestOpaqueTablet):
def create_device(self):
@@ -862,7 +859,18 @@ class TestPTHX60_Pen(TestOpaqueCTLTablet):
)
-class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest):
+class DirectTabletTest():
+ def test_prop_direct(self):
+ """
+ Verify that INPUT_PROP_DIRECT is set and INPUT_PROP_POINTER
+ is not set on display devices.
+ """
+ evdev = self.uhdev.get_evdev()
+ assert libevdev.INPUT_PROP_DIRECT in evdev.properties
+ assert libevdev.INPUT_PROP_POINTER not in evdev.properties
+
+
+class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest, DirectTabletTest):
ContactIds = namedtuple("ContactIds", "contact_id, tracking_id, slot_num")
def create_device(self):