From patchwork Fri Feb 17 16:17:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58706 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981692wrn; Fri, 17 Feb 2023 08:29:47 -0800 (PST) X-Google-Smtp-Source: AK7set8VJXJcdI66aWpPxRZ9otNxOMH1xe0E/L9/qV0fsGoXve8kLNOmPcjDkIRJ2P0PaaF+p6nM X-Received: by 2002:a05:6402:27d0:b0:4ad:6113:76fd with SMTP id c16-20020a05640227d000b004ad611376fdmr6319842ede.16.1676651386927; Fri, 17 Feb 2023 08:29:46 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651386; cv=none; d=google.com; s=arc-20160816; b=oIQD4m0bGcUrJOgCvzXmo9n+Q/mBQsawklI9F/oJlisvGQE9bnCQjnTBndCSvcPC8L lGxAQlQUXpSat+V06AWPMLrEF2JL/mgCZKSxM8myXVPEdJQYxwz7eHU742q1eI4aL3j+ SANA9ij5A/SElxZta34gdttSBDGYU12rVSap/m3xhg3nf2+V1smwXOCzL+3Q1GLAWw3f gIv8geQfh5UfvhKv7LDGjrUdMuJ2r3fi9LJikHZpqR93hnqvC96PJ0u8Kdg4Y4vW6MzG kUnE4OIOd6rMWN2TnKSXfsqbryTyMlo4x7BrC6kquInYlPZiLYDJzOytTtMS3I2gozaA oUPQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=fMDV2EKl8tvcx5W+PSppav1j+1VABqtQOAoV76jWk4k=; b=nhnYAkfZrHaW1fuqITeSujgA6SpDhfDpFoaPH5RbbEigAiFaZWNPj2NCUXDEocmXgY fwXFpw+xIHdRoCijcaglXXRQX4vrZhuQ3MpQUypWWTdPEifOuXLEcRVzUDqWVzqWY3lp Dpazquf1LYTPM2zeIr4SWBir+p+IXY/JIwdD9iPuyxlANhhpANcjBShVJv16Jmlz4Dja IoQQzShs4tiDxVmLKCnY9EFNmkr8k14e+J3lrOPZQKOvQW1lu2SIDFStWcIV7p90wm+a eM7WYxYcBfskbsAK830tW8dNsEwsmg/NVSqMdxMWLeLmI0O7Pt4g1YwND31A003BD3X1 BmXQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=NR0ZkMPR; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id fi5-20020a1709073ac500b008b12b8c6be5si4764874ejc.927.2023.02.17.08.29.24; Fri, 17 Feb 2023 08:29:46 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=NR0ZkMPR; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230229AbjBQQUK (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53154 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229598AbjBQQUF (ORCPT ); Fri, 17 Feb 2023 11:20:05 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 005BC6F3DB for ; Fri, 17 Feb 2023 08:18:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650725; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fMDV2EKl8tvcx5W+PSppav1j+1VABqtQOAoV76jWk4k=; b=NR0ZkMPRWUwtJLIctjqii7Abct7GcWu/blQZzG8usqojhfjFKLkD0dpvXgihN99OTeo0Sy QcJcjYy+apQOVyzQKva7J95pIjAEAxOnehGAQYTb1N1ll9XhTPFP9l0XVecS5YdRN2qwjp z6jtVtWUEBhFkoZbdO1TymOa1FYn410= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-83-M-AN_mGbPmigN4-vv5GaGQ-1; Fri, 17 Feb 2023 11:18:42 -0500 X-MC-Unique: M-AN_mGbPmigN4-vv5GaGQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id B49F0830F81; Fri, 17 Feb 2023 16:18:41 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 87764C15BA0; Fri, 17 Feb 2023 16:18:40 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:17:55 +0100 Subject: [PATCH 01/11] selftests: hid: make vmtest rely on make MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-1-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=3140; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=UcWZqRNXA4+z94g+BQqS902c8ut3vW6xhsKsnErfSc8=; b=nhPWBKXiBqZfd7/ArnvuPWYoIu8p3HJUbFDngVQTJj3K8jK7tDjQXoI2HXYz9FwkLFEiYneAF baZA9UqZabODnN8t5DVQkLprp7RDyaX5U+M1QJ7fhHrWmF4jiyaLW9h X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096405065160791?= X-GMAIL-MSGID: =?utf-8?q?1758096405065160791?= Having a default binary is simple enough, but this also means that we need to keep the targets in sync as we are adding them in the Makefile. So instead of doing that manual work, make vmtest.sh generic enough to actually be capable of running 'make -C tools/testing/selftests/hid'. The new image we use has make installed, which the base fedora image doesn't. Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/vmtest.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh index 90f34150f257..6346b0620dba 100755 --- a/tools/testing/selftests/hid/vmtest.sh +++ b/tools/testing/selftests/hid/vmtest.sh @@ -16,7 +16,6 @@ x86_64) exit 1 ;; esac -DEFAULT_COMMAND="./hid_bpf" SCRIPT_DIR="$(dirname $(realpath $0))" OUTPUT_DIR="$SCRIPT_DIR/results" KCONFIG_REL_PATHS=("${SCRIPT_DIR}/config" "${SCRIPT_DIR}/config.common" "${SCRIPT_DIR}/config.${ARCH}") @@ -25,7 +24,10 @@ NUM_COMPILE_JOBS="$(nproc)" LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")" LOG_FILE="${LOG_FILE_BASE}.log" EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status" -CONTAINER_IMAGE="registry.fedoraproject.org/fedora:36" +CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1" + +TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}" +DEFAULT_COMMAND="make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" usage() { @@ -33,9 +35,9 @@ usage() Usage: $0 [-i] [-s] [-d ] -- [] is the command you would normally run when you are in -tools/testing/selftests/bpf. e.g: +the source kernel direcory. e.g: - $0 -- ./hid_bpf + $0 -- ./tools/testing/selftests/hid/hid_bpf If no command is specified and a debug shell (-s) is not requested, "${DEFAULT_COMMAND}" will be run by default. @@ -43,11 +45,11 @@ If no command is specified and a debug shell (-s) is not requested, If you build your kernel using KBUILD_OUTPUT= or O= options, these can be passed as environment variables to the script: - O= $0 -- ./hid_bpf + O= $0 -- ./tools/testing/selftests/hid/hid_bpf or - KBUILD_OUTPUT= $0 -- ./hid_bpf + KBUILD_OUTPUT= $0 -- ./tools/testing/selftests/hid/hid_bpf Options: @@ -91,11 +93,14 @@ update_selftests() run_vm() { - local b2c="$1" - local kernel_bzimage="$2" - local command="$3" + local run_dir="$1" + local b2c="$2" + local kernel_bzimage="$3" + local command="$4" local post_command="" + cd "${run_dir}" + if ! which "${QEMU_BINARY}" &> /dev/null; then cat < X-Patchwork-Id: 58695 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp980861wrn; Fri, 17 Feb 2023 08:28:10 -0800 (PST) X-Google-Smtp-Source: AK7set/04tD3YthsSRjIRcdT8xNXusXlIpqYzpyvaieMf6giZ383O9p088fO/w+dYM4/w1VQMAqj X-Received: by 2002:a05:6402:344:b0:4ae:821:871f with SMTP id r4-20020a056402034400b004ae0821871fmr2376008edw.3.1676651290143; Fri, 17 Feb 2023 08:28:10 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651290; cv=none; d=google.com; s=arc-20160816; b=c5kh31KDL3ze/LmDLehVPKOw04qFvY3xDrZOiu+IsBxTMgJNtM6JORikjiWaegKX3g kpdji01BAtxs6SimJi9tucDD4Vi19BywpYlHUvurG1DhMUnqCELnREbiHrAorYmzdUPQ Mc1sMFZoDjpo0PMw1XV9hMbr/DncIQRq8/GVSCxyAE/NfNMiMkyhRybQ3jA97mjOd02h radyn8V3swV47yXYCzZULiJZSNmJCsYWFnkUA8E6YHQbLM0z3vkqEdE14HhfJIEwD3JM PsyZsyGOY1nTA3IvqhJpvqmcaHjCs9/gpdM6N5QVyj8lohsIqN1xnZvigaoMx8Q9iVOO L05A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=AYZS0Gn1WIPpf33ANhi5QjRrvUnicc/K3UlvygoHZRM=; b=dlmmIOSJYttb1G3FKiAyC9lrtNxTNhu2KgcmTo3L9OwSpPpQGuwTiecw403OW/DEFD bFurBCSKiZyJ9LeS2rkIWgOX2rlWD3twTssvX2DtMm/xK5vAnwwSMBfaIuVf0T+M/+UM DUG+AGnw1qv4AwRfdxI+4FOhMtpWGGVUq71MEJuPdPAZvvVhB4n9O1BoXJL0cUNlYP9v IpmprvCls2xFEzLCP8jl0Wiqf41mtsecgs8sqqrV+MMZ7aFhtvTXpm3YL4v6KBiXHCkm jM93JgtEar3mN6eCHcfIFO7iAFplfVKKkK0dLYVGcrtLGBH80R4Z4QM2rRUquIuWQqJ+ d25g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Bp0fGB5D; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id u26-20020aa7d99a000000b004acb7b6a25csi5682010eds.42.2023.02.17.08.27.47; Fri, 17 Feb 2023 08:28:10 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Bp0fGB5D; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229930AbjBQQUW (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52616 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231138AbjBQQUR (ORCPT ); Fri, 17 Feb 2023 11:20:17 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 79A487148F for ; Fri, 17 Feb 2023 08:18:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650727; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AYZS0Gn1WIPpf33ANhi5QjRrvUnicc/K3UlvygoHZRM=; b=Bp0fGB5DdHFIZcQ6wIBqtA+I6/isGA2+lviftY6e9jYAUxw2JGvdrCYtxYDdfAODTghCpZ N/mVhsdrQdM8wCX41Hi2Up3eVuTxGoqYH3Ho7+TeLalxEZgdDU5QY9lt5sgvvuxGrZ6y3c 8+5N/nThbgKT0GAZ0iFSRbWIbb8QvXo= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-654-OjVvDdLOOUK0W-ukcGR8og-1; Fri, 17 Feb 2023 11:18:44 -0500 X-MC-Unique: OjVvDdLOOUK0W-ukcGR8og-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id B9193802314; Fri, 17 Feb 2023 16:18:43 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 382C0C15BA0; Fri, 17 Feb 2023 16:18:41 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:17:56 +0100 Subject: [PATCH 02/11] selftests: hid: import hid-tools hid-core tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-2-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Peter Hutterer X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=27539; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=sTE4WyarvxEWCk6ipIR5lEt1QvYJr6fLEe7OoVWWw4Y=; b=M8pXL91bdk3H7DJmr8+nyGEfBl6WHxKWaeVW+5lJLeUxj+/5VtDLPU2zZlI7r+67H2YOv7EAC aDCahEPvy+DCNr/9VkFBBX447j5ddtXro2CC1w8ctC2TwMmskRsTbUC X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096303193050027?= X-GMAIL-MSGID: =?utf-8?q?1758096303193050027?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. I haven't imported all of hid-tools, the python module, but only the tests related to the kernel. We can rely on pip to fetch the latest hid-tools release, and then run the tests directly from the tree. This should now be easier to request tests when something is not behaving properly in the HID subsystem. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 2 + tools/testing/selftests/hid/hid-core.sh | 7 + tools/testing/selftests/hid/run-hid-tools-tests.sh | 28 ++ tools/testing/selftests/hid/tests/__init__.py | 2 + tools/testing/selftests/hid/tests/base.py | 345 +++++++++++++++++++++ tools/testing/selftests/hid/tests/conftest.py | 81 +++++ tools/testing/selftests/hid/tests/test_hid_core.py | 154 +++++++++ tools/testing/selftests/hid/vmtest.sh | 2 +- 8 files changed, 620 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 83e8f87d643a..bdcb36d80c8c 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -5,6 +5,8 @@ include ../../../build/Build.include include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include +TEST_PROGS := hid-core.sh + CXX ?= $(CROSS_COMPILE)g++ HOSTPKG_CONFIG := pkg-config diff --git a/tools/testing/selftests/hid/hid-core.sh b/tools/testing/selftests/hid/hid-core.sh new file mode 100755 index 000000000000..5bbabc12c34f --- /dev/null +++ b/tools/testing/selftests/hid/hid-core.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_hid_core.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/run-hid-tools-tests.sh b/tools/testing/selftests/hid/run-hid-tools-tests.sh new file mode 100755 index 000000000000..bdae8464da86 --- /dev/null +++ b/tools/testing/selftests/hid/run-hid-tools-tests.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +if ! command -v python3 > /dev/null 2>&1; then + echo "hid-tools: [SKIP] python3 not installed" + exit 77 +fi + +if ! python3 -c "import pytest" > /dev/null 2>&1; then + echo "hid: [SKIP/ pytest module not installed" + exit 77 +fi + +if ! python3 -c "import pytest_tap" > /dev/null 2>&1; then + echo "hid: [SKIP/ pytest_tap module not installed" + exit 77 +fi + +if ! python3 -c "import hidtools" > /dev/null 2>&1; then + echo "hid: [SKIP/ hid-tools module not installed" + exit 77 +fi + +TARGET=${TARGET:=.} + +echo TAP version 13 +python3 -u -m pytest $PYTEST_XDIST ./tests/$TARGET --tap-stream --udevd diff --git a/tools/testing/selftests/hid/tests/__init__.py b/tools/testing/selftests/hid/tests/__init__.py new file mode 100644 index 000000000000..c940e9275252 --- /dev/null +++ b/tools/testing/selftests/hid/tests/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +# Just to make sphinx-apidoc document this directory diff --git a/tools/testing/selftests/hid/tests/base.py b/tools/testing/selftests/hid/tests/base.py new file mode 100644 index 000000000000..1305cfc9646e --- /dev/null +++ b/tools/testing/selftests/hid/tests/base.py @@ -0,0 +1,345 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. + +import libevdev +import os +import pytest +import time + +import logging + +from hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile +from pathlib import Path +from typing import Final + +logger = logging.getLogger("hidtools.test.base") + +# application to matches +application_matches: Final = { + # pyright: ignore + "Accelerometer": EvdevMatch( + req_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ] + ), + "Game Pad": EvdevMatch( # in systemd, this is a lot more complex, but that will do + requires=[ + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + libevdev.EV_ABS.ABS_RX, + libevdev.EV_ABS.ABS_RY, + libevdev.EV_KEY.BTN_START, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Joystick": EvdevMatch( # in systemd, this is a lot more complex, but that will do + requires=[ + libevdev.EV_ABS.ABS_RX, + libevdev.EV_ABS.ABS_RY, + libevdev.EV_KEY.BTN_START, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Key": EvdevMatch( + requires=[ + libevdev.EV_KEY.KEY_A, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + libevdev.INPUT_PROP_DIRECT, + libevdev.INPUT_PROP_POINTER, + ], + ), + "Mouse": EvdevMatch( + requires=[ + libevdev.EV_REL.REL_X, + libevdev.EV_REL.REL_Y, + libevdev.EV_KEY.BTN_LEFT, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Pad": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_0, + ], + excludes=[ + libevdev.EV_KEY.BTN_TOOL_PEN, + libevdev.EV_KEY.BTN_TOUCH, + libevdev.EV_ABS.ABS_DISTANCE, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Pen": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_STYLUS, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Stylus": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_STYLUS, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Touch Pad": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_LEFT, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS], + req_properties=[ + libevdev.INPUT_PROP_POINTER, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), + "Touch Screen": EvdevMatch( + requires=[ + libevdev.EV_KEY.BTN_TOUCH, + libevdev.EV_ABS.ABS_X, + libevdev.EV_ABS.ABS_Y, + ], + excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS], + req_properties=[ + libevdev.INPUT_PROP_DIRECT, + ], + excl_properties=[ + libevdev.INPUT_PROP_ACCELEROMETER, + ], + ), +} + + +class UHIDTestDevice(BaseDevice): + def __init__(self, name, application, rdesc_str=None, rdesc=None, input_info=None): + super().__init__(name, application, rdesc_str, rdesc, input_info) + self.application_matches = application_matches + if name is None: + name = f"uhid test {self.__class__.__name__}" + if not name.startswith("uhid test "): + name = "uhid test " + self.name + self.name = name + + +class BaseTestCase: + class TestUhid(object): + syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) # type: ignore + key_event = libevdev.InputEvent(libevdev.EV_KEY) # type: ignore + abs_event = libevdev.InputEvent(libevdev.EV_ABS) # type: ignore + rel_event = libevdev.InputEvent(libevdev.EV_REL) # type: ignore + msc_event = libevdev.InputEvent(libevdev.EV_MSC.MSC_SCAN) # type: ignore + + # List of kernel modules to load before starting the test + # if any module is not available (not compiled), the test will skip. + # Each element is a tuple '(kernel driver name, kernel module)', + # for example ("playstation", "hid-playstation") + kernel_modules = [] + + def assertInputEventsIn(self, expected_events, effective_events): + effective_events = effective_events.copy() + for ev in expected_events: + assert ev in effective_events + effective_events.remove(ev) + return effective_events + + def assertInputEvents(self, expected_events, effective_events): + remaining = self.assertInputEventsIn(expected_events, effective_events) + assert remaining == [] + + @classmethod + def debug_reports(cls, reports, uhdev=None, events=None): + data = [" ".join([f"{v:02x}" for v in r]) for r in reports] + + if uhdev is not None: + human_data = [ + uhdev.parsed_rdesc.format_report(r, split_lines=True) + for r in reports + ] + try: + human_data = [ + f'\n\t {" " * h.index("/")}'.join(h.split("\n")) + for h in human_data + ] + except ValueError: + # '/' not found: not a numbered report + human_data = ["\n\t ".join(h.split("\n")) for h in human_data] + data = [f"{d}\n\t ====> {h}" for d, h in zip(data, human_data)] + + reports = data + + if len(reports) == 1: + print("sending 1 report:") + else: + print(f"sending {len(reports)} reports:") + for report in reports: + print("\t", report) + + if events is not None: + print("events received:", events) + + def create_device(self): + raise Exception("please reimplement me in subclasses") + + def _load_kernel_module(self, kernel_driver, kernel_module): + sysfs_path = Path("/sys/bus/hid/drivers") + if kernel_driver is not None: + sysfs_path /= kernel_driver + else: + # special case for when testing all available modules: + # we don't know beforehand the name of the module from modinfo + sysfs_path = Path("/sys/module") / kernel_module.replace("-", "_") + if not sysfs_path.exists(): + import subprocess + + ret = subprocess.run(["/usr/sbin/modprobe", kernel_module]) + if ret.returncode != 0: + pytest.skip( + f"module {kernel_module} could not be loaded, skipping the test" + ) + + @pytest.fixture() + def load_kernel_module(self): + for kernel_driver, kernel_module in self.kernel_modules: + self._load_kernel_module(kernel_driver, kernel_module) + yield + + @pytest.fixture() + def new_uhdev(self, load_kernel_module): + return self.create_device() + + def assertName(self, uhdev): + evdev = uhdev.get_evdev() + assert uhdev.name in evdev.name + + @pytest.fixture(autouse=True) + def context(self, new_uhdev, request): + try: + with HIDTestUdevRule.instance(): + with new_uhdev as self.uhdev: + skip_cond = request.node.get_closest_marker("skip_if_uhdev") + if skip_cond: + test, message, *rest = skip_cond.args + + if test(self.uhdev): + pytest.skip(message) + + self.uhdev.create_kernel_device() + now = time.time() + while not self.uhdev.is_ready() and time.time() - now < 5: + self.uhdev.dispatch(1) + if self.uhdev.get_evdev() is None: + logger.warning( + f"available list of input nodes: (default application is '{self.uhdev.application}')" + ) + logger.warning(self.uhdev.input_nodes) + yield + self.uhdev = None + except PermissionError: + pytest.skip("Insufficient permissions, run me as root") + + @pytest.fixture(autouse=True) + def check_taint(self): + # we are abusing SysfsFile here, it's in /proc, but meh + taint_file = SysfsFile("/proc/sys/kernel/tainted") + taint = taint_file.int_value + + yield + + assert taint_file.int_value == taint + + def test_creation(self): + """Make sure the device gets processed by the kernel and creates + the expected application input node. + + If this fail, there is something wrong in the device report + descriptors.""" + uhdev = self.uhdev + assert uhdev is not None + assert uhdev.get_evdev() is not None + self.assertName(uhdev) + assert len(uhdev.next_sync_events()) == 0 + assert uhdev.get_evdev() is not None + + +class HIDTestUdevRule(object): + _instance = None + """ + A context-manager compatible class that sets up our udev rules file and + deletes it on context exit. + + This class is tailored to our test setup: it only sets up the udev rule + on the **second** context and it cleans it up again on the last context + removed. This matches the expected pytest setup: we enter a context for + the session once, then once for each test (the first of which will + trigger the udev rule) and once the last test exited and the session + exited, we clean up after ourselves. + """ + + def __init__(self): + self.refs = 0 + self.rulesfile = None + + def __enter__(self): + self.refs += 1 + if self.refs == 2 and self.rulesfile is None: + self.create_udev_rule() + self.reload_udev_rules() + + def __exit__(self, exc_type, exc_value, traceback): + self.refs -= 1 + if self.refs == 0 and self.rulesfile: + os.remove(self.rulesfile.name) + self.reload_udev_rules() + + def reload_udev_rules(self): + import subprocess + + subprocess.run("udevadm control --reload-rules".split()) + subprocess.run("systemd-hwdb update".split()) + + def create_udev_rule(self): + import tempfile + + os.makedirs("/run/udev/rules.d", exist_ok=True) + with tempfile.NamedTemporaryFile( + prefix="91-uhid-test-device-REMOVEME-", + suffix=".rules", + mode="w+", + dir="/run/udev/rules.d", + delete=False, + ) as f: + f.write( + 'KERNELS=="*input*", ATTRS{name}=="*uhid test *", ENV{LIBINPUT_IGNORE_DEVICE}="1"\n' + ) + f.write( + 'KERNELS=="*input*", ATTRS{name}=="*uhid test * System Multi Axis", ENV{ID_INPUT_TOUCHSCREEN}="", ENV{ID_INPUT_SYSTEM_MULTIAXIS}="1"\n' + ) + self.rulesfile = f + + @classmethod + def instance(cls): + if not cls._instance: + cls._instance = HIDTestUdevRule() + return cls._instance diff --git a/tools/testing/selftests/hid/tests/conftest.py b/tools/testing/selftests/hid/tests/conftest.py new file mode 100644 index 000000000000..1361ec981db6 --- /dev/null +++ b/tools/testing/selftests/hid/tests/conftest.py @@ -0,0 +1,81 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. + +import platform +import pytest +import re +import resource +import subprocess +from .base import HIDTestUdevRule +from pathlib import Path + + +# See the comment in HIDTestUdevRule, this doesn't set up but it will clean +# up once the last test exited. +@pytest.fixture(autouse=True, scope="session") +def udev_rules_session_setup(): + with HIDTestUdevRule.instance(): + yield + + +@pytest.fixture(autouse=True, scope="session") +def setup_rlimit(): + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +@pytest.fixture(autouse=True, scope="session") +def start_udevd(pytestconfig): + if pytestconfig.getoption("udevd"): + import subprocess + + with subprocess.Popen("/usr/lib/systemd/systemd-udevd") as proc: + yield + proc.kill() + else: + yield + + +def pytest_configure(config): + config.addinivalue_line( + "markers", + "skip_if_uhdev(condition, message): mark test to skip if the condition on the uhdev device is met", + ) + + +# Generate the list of modules and modaliases +# for the tests that need to be parametrized with those +def pytest_generate_tests(metafunc): + if "usbVidPid" in metafunc.fixturenames: + modules = ( + Path("/lib/modules/") + / platform.uname().release + / "kernel" + / "drivers" + / "hid" + ) + + modalias_re = re.compile(r"alias:\s+hid:b0003g.*v([0-9a-fA-F]+)p([0-9a-fA-F]+)") + + params = [] + ids = [] + for module in modules.glob("*.ko"): + p = subprocess.run( + ["modinfo", module], capture_output=True, check=True, encoding="utf-8" + ) + for line in p.stdout.split("\n"): + m = modalias_re.match(line) + if m is not None: + vid, pid = m.groups() + vid = int(vid, 16) + pid = int(pid, 16) + params.append([module.name.replace(".ko", ""), vid, pid]) + ids.append(f"{module.name} {vid:04x}:{pid:04x}") + metafunc.parametrize("usbVidPid", params, ids=ids) + + +def pytest_addoption(parser): + parser.addoption("--udevd", action="store_true", default=False) diff --git a/tools/testing/selftests/hid/tests/test_hid_core.py b/tools/testing/selftests/hid/tests/test_hid_core.py new file mode 100644 index 000000000000..9a7fe40020d2 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_hid_core.py @@ -0,0 +1,154 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is for generic devices + +from . import base +import logging + +logger = logging.getLogger("hidtools.test.hid") + + +class TestCollectionOverflow(base.BaseTestCase.TestUhid): + """ + Test class to test re-allocation of the HID collection stack in + hid-core.c. + """ + + def create_device(self): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # .Usage Page (Generic Desktop) + 0x09, 0x02, # .Usage (Mouse) + 0xa1, 0x01, # .Collection (Application) + 0x09, 0x02, # ..Usage (Mouse) + 0xa1, 0x02, # ..Collection (Logical) + 0x09, 0x01, # ...Usage (Pointer) + 0xa1, 0x00, # ...Collection (Physical) + 0x05, 0x09, # ....Usage Page (Button) + 0x19, 0x01, # ....Usage Minimum (1) + 0x29, 0x03, # ....Usage Maximum (3) + 0x15, 0x00, # ....Logical Minimum (0) + 0x25, 0x01, # ....Logical Maximum (1) + 0x75, 0x01, # ....Report Size (1) + 0x95, 0x03, # ....Report Count (3) + 0x81, 0x02, # ....Input (Data,Var,Abs) + 0x75, 0x05, # ....Report Size (5) + 0x95, 0x01, # ....Report Count (1) + 0x81, 0x03, # ....Input (Cnst,Var,Abs) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0xa1, 0x02, # ....Collection (Logical) + 0x09, 0x01, # .....Usage (Pointer) + 0x05, 0x01, # .....Usage Page (Generic Desktop) + 0x09, 0x30, # .....Usage (X) + 0x09, 0x31, # .....Usage (Y) + 0x15, 0x81, # .....Logical Minimum (-127) + 0x25, 0x7f, # .....Logical Maximum (127) + 0x75, 0x08, # .....Report Size (8) + 0x95, 0x02, # .....Report Count (2) + 0x81, 0x06, # .....Input (Data,Var,Rel) + 0xa1, 0x02, # ...Collection (Logical) + 0x85, 0x12, # ....Report ID (18) + 0x09, 0x48, # ....Usage (Resolution Multiplier) + 0x95, 0x01, # ....Report Count (1) + 0x75, 0x02, # ....Report Size (2) + 0x15, 0x00, # ....Logical Minimum (0) + 0x25, 0x01, # ....Logical Maximum (1) + 0x35, 0x01, # ....Physical Minimum (1) + 0x45, 0x0c, # ....Physical Maximum (12) + 0xb1, 0x02, # ....Feature (Data,Var,Abs) + 0x85, 0x1a, # ....Report ID (26) + 0x09, 0x38, # ....Usage (Wheel) + 0x35, 0x00, # ....Physical Minimum (0) + 0x45, 0x00, # ....Physical Maximum (0) + 0x95, 0x01, # ....Report Count (1) + 0x75, 0x10, # ....Report Size (16) + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) + 0x81, 0x06, # ....Input (Data,Var,Rel) + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ...End Collection + 0xc0, # ..End Collection + 0xc0, # .End Collection + ] + # fmt: on + return base.UHIDTestDevice( + name=None, rdesc=report_descriptor, application="Mouse" + ) + + def test_rdesc(self): + """ + This test can only check for negatives. If the kernel crashes, you + know why. If this test passes, either the bug isn't present or just + didn't get triggered. No way to know. + + For an explanation, see kernel patch + HID: core: replace the collection tree pointers with indices + """ + pass diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh index 6346b0620dba..681b906b4853 100755 --- a/tools/testing/selftests/hid/vmtest.sh +++ b/tools/testing/selftests/hid/vmtest.sh @@ -27,7 +27,7 @@ EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status" CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1" TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}" -DEFAULT_COMMAND="make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" +DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" usage() { From patchwork Fri Feb 17 16:17:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58696 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp980892wrn; Fri, 17 Feb 2023 08:28:14 -0800 (PST) X-Google-Smtp-Source: AK7set+znZyDsxqZaR9iVNeEPQH3//iYT9ZN683rYW7LHz0jCU/8+xX6LgAk5Rrhc427jQx1txqC X-Received: by 2002:aa7:c1c6:0:b0:4ac:d2b4:5e37 with SMTP id d6-20020aa7c1c6000000b004acd2b45e37mr1972345edp.39.1676651294609; Fri, 17 Feb 2023 08:28:14 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651294; cv=none; d=google.com; s=arc-20160816; b=qHyICiVlK4yZXZyUQbY8euXqf5pqOiuav7ZSV7hdc6cL0/rOCz5DkXjmRz34lz6V07 al3iY+TzWPygzNNBwdmUD6Q/kBPWkvAgQLWs5YBvfeLM88E7ymxEM2picCyiPqKmT4nI cdHSx6B2Fp/QAaG8SB6QA9KopMAbRpmABFX+vipYUCk7UHNQThlOs+LL9T0mbaCI5Dcv VQW0UMW/A3Ok8KsEe56zdjDfSW8VgtcoAG7B1LNWwA288oLmGqSNFgoDEHh7zvsZyXap s8eDytLpofBQqYD6kOr6MyPXF8BELSUBWHSYKRB0LKSvm5hEBIIU8R6kOBOcNIssvDle cO1w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=t7mJsdBsB0TbJqoqY9x4ZhylWOnsPAyIa0tLN+Q45Uo=; b=Qg7nL1p65+j1Vbubdxdj9WOu8vH4fV9gcchYqm+vz9is9uhxGoQxV1ELXjAY8AGOuk ieH45qC6iEddG7WiUz9SWSO+evEZ9fkPw3PAgJTgd77uvsyLU4vHUhvIpxsSVoSaXfts H6fJTyGah9b7sG78Zjl8W3ge6nejUdq7wbr0bw+UR0z8JfPbCgnqRJTO9wjq4Yopr+Vz wC25+fW+0GKB0uOfpoHEpEWMj12PjvsEh6mkJiGesMPYSLoNTX0/JdTh6z26t6bhdpTa 7XPSLlOt0akibR6sj0BAx5F6NonDaQ7zC1PP+f1742YHMpHLpxweZOJD3hTUUjwGzU66 g8/g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=YvSsEgLj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id g4-20020aa7c584000000b004abdba04e7dsi5491448edq.393.2023.02.17.08.27.52; Fri, 17 Feb 2023 08:28:14 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=YvSsEgLj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231189AbjBQQU0 (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:26 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52310 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231136AbjBQQUQ (ORCPT ); Fri, 17 Feb 2023 11:20:16 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 25FD3714A2 for ; Fri, 17 Feb 2023 08:18:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650728; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=t7mJsdBsB0TbJqoqY9x4ZhylWOnsPAyIa0tLN+Q45Uo=; b=YvSsEgLjQ2WotaKrBkg++VH2Bl8rq9/LgaX4TbiIup4Mi9aBi8DUChbmjpkwySs8KjEwOP leaVAbaI0+mIHgo8TD/R0HIXF2EB0W5x/RFCAzRJcFai3l8J4AsH8no63b/knW4Vm2Cgzm 2w9dpztKne5JKSyvV8Sp6tT2jYmQvQw= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-513-VWDTAFVkMWmjTOGFTrdV5Q-1; Fri, 17 Feb 2023 11:18:46 -0500 X-MC-Unique: VWDTAFVkMWmjTOGFTrdV5Q-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E3FB41C05AD5; Fri, 17 Feb 2023 16:18:45 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id F2DE4C15BA0; Fri, 17 Feb 2023 16:18:43 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:17:57 +0100 Subject: [PATCH 03/11] selftests: hid: import hid-tools hid-gamepad tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-3-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Candle Sun , Jose Torreguitar , Peter Hutterer , Roderick Colenbrander , Silvan Jegen X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=9948; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=Sra9Q171hmNz0YtouUS6HVzlBGBdWXuPKzRZhAglX/Y=; b=xBpkb7spzYZSEqFqf+/K7Q51tPImKljgdFxsbb71xzB0IgERTWBf2C2tSTM59mJJfkGUzyqH4 bxPE163PDUQCMqmYhyhtTwU+qZFi6Tx7591YpIW+GnPoPmOXkbbOtvT X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096307641232002?= X-GMAIL-MSGID: =?utf-8?q?1758096307641232002?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Candle Sun Cc: Jose Torreguitar Cc: Peter Hutterer Cc: Roderick Colenbrander Cc: Silvan Jegen Signed-off-by: Benjamin Tissoires Signed-off-by: Silvan Jegen --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-gamepad.sh | 7 + tools/testing/selftests/hid/tests/test_gamepad.py | 209 ++++++++++++++++++++++ 3 files changed, 217 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index bdcb36d80c8c..d16a22477140 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -6,6 +6,7 @@ include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh +TEST_PROGS += hid-gamepad.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-gamepad.sh b/tools/testing/selftests/hid/hid-gamepad.sh new file mode 100755 index 000000000000..1ba00c0ca95f --- /dev/null +++ b/tools/testing/selftests/hid/hid-gamepad.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_gamepad.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_gamepad.py b/tools/testing/selftests/hid/tests/test_gamepad.py new file mode 100644 index 000000000000..26c74040b796 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_gamepad.py @@ -0,0 +1,209 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019 Benjamin Tissoires +# Copyright (c) 2019 Red Hat, Inc. +# + +from . import base +import libevdev +import pytest + +from hidtools.device.base_gamepad import AsusGamepad, SaitekGamepad + +import logging + +logger = logging.getLogger("hidtools.test.gamepad") + + +class BaseTest: + class TestGamepad(base.BaseTestCase.TestUhid): + @pytest.fixture(autouse=True) + def send_initial_state(self): + """send an empty report to initialize the axes""" + uhdev = self.uhdev + + r = uhdev.event() + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + def assert_button(self, button): + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + buttons = {} + key = libevdev.evbit(uhdev.buttons_map[button]) + + buttons[button] = True + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key] == 1 + + buttons[button] = False + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key] == 0 + + def test_buttons(self): + """check for button reliability.""" + uhdev = self.uhdev + + for b in uhdev.buttons: + self.assert_button(b) + + def test_dual_buttons(self): + """check for button reliability when pressing 2 buttons""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + # can change intended b1 b2 values + b1 = uhdev.buttons[0] + key1 = libevdev.evbit(uhdev.buttons_map[b1]) + b2 = uhdev.buttons[1] + key2 = libevdev.evbit(uhdev.buttons_map[b2]) + + buttons = {b1: True, b2: True} + r = uhdev.event(buttons=buttons) + expected_event0 = libevdev.InputEvent(key1, 1) + expected_event1 = libevdev.InputEvent(key2, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn( + (syn_event, expected_event0, expected_event1), events + ) + assert evdev.value[key1] == 1 + assert evdev.value[key2] == 1 + + buttons = {b1: False, b2: None} + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key1, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key1] == 0 + assert evdev.value[key2] == 1 + + buttons = {b1: None, b2: False} + r = uhdev.event(buttons=buttons) + expected_event = libevdev.InputEvent(key2, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[key1] == 0 + assert evdev.value[key2] == 0 + + def _get_libevdev_abs_events(self, which): + """Returns which ABS_* evdev axes are expected for the given stick""" + abs_map = self.uhdev.axes_map[which] + + x = abs_map["x"].evdev + y = abs_map["y"].evdev + + assert x + assert y + + return x, y + + def _test_joystick_press(self, which, data): + uhdev = self.uhdev + + libevdev_axes = self._get_libevdev_abs_events(which) + + r = None + if which == "left_stick": + r = uhdev.event(left=data) + else: + r = uhdev.event(right=data) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + for i, d in enumerate(data): + if d is not None and d != 127: + assert libevdev.InputEvent(libevdev_axes[i], d) in events + else: + assert libevdev.InputEvent(libevdev_axes[i]) not in events + + def test_left_joystick_press_left(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (63, None)) + self._test_joystick_press("left_stick", (0, 127)) + + def test_left_joystick_press_right(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (191, 127)) + self._test_joystick_press("left_stick", (255, None)) + + def test_left_joystick_press_up(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (None, 63)) + self._test_joystick_press("left_stick", (127, 0)) + + def test_left_joystick_press_down(self): + """check for the left joystick reliability""" + self._test_joystick_press("left_stick", (127, 191)) + self._test_joystick_press("left_stick", (None, 255)) + + def test_right_joystick_press_left(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (63, None)) + self._test_joystick_press("right_stick", (0, 127)) + + def test_right_joystick_press_right(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (191, 127)) + self._test_joystick_press("right_stick", (255, None)) + + def test_right_joystick_press_up(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (None, 63)) + self._test_joystick_press("right_stick", (127, 0)) + + def test_right_joystick_press_down(self): + """check for the right joystick reliability""" + self._test_joystick_press("right_stick", (127, 191)) + self._test_joystick_press("right_stick", (None, 255)) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Hat switch" not in uhdev.fields, + "Device not compatible, missing Hat switch usage", + ) + @pytest.mark.parametrize( + "hat_value,expected_evdev,evdev_value", + [ + (0, "ABS_HAT0Y", -1), + (2, "ABS_HAT0X", 1), + (4, "ABS_HAT0Y", 1), + (6, "ABS_HAT0X", -1), + ], + ) + def test_hat_switch(self, hat_value, expected_evdev, evdev_value): + uhdev = self.uhdev + + r = uhdev.event(hat_switch=hat_value) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert ( + libevdev.InputEvent( + libevdev.evbit("EV_ABS", expected_evdev), evdev_value + ) + in events + ) + + +class TestSaitekGamepad(BaseTest.TestGamepad): + def create_device(self): + return SaitekGamepad() + + +class TestAsusGamepad(BaseTest.TestGamepad): + def create_device(self): + return AsusGamepad() From patchwork Fri Feb 17 16:17:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58694 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp980672wrn; Fri, 17 Feb 2023 08:27:50 -0800 (PST) X-Google-Smtp-Source: AK7set/B41O9qkwVpblu1J7BGWdvOZtexpvY2SDyRuD/C+rhPU+EOhEccVRco8cWYYh58Qtaojc8 X-Received: by 2002:a17:906:594b:b0:8a6:93a4:c897 with SMTP id g11-20020a170906594b00b008a693a4c897mr10461694ejr.33.1676651270299; Fri, 17 Feb 2023 08:27:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651270; cv=none; d=google.com; s=arc-20160816; b=PbGThqlw+NxOxhBhoqvW24VsDI1Z+SlmVALbnDU0NM7muXRcrOTd1I/bmt/or1ODU2 Yx1xJcIHfWTdf5x+ZQgkublSF8XBz0MHbaSTuSpJUxRJ/sqQwZLHmm/AS+b1pmiroKej utNXagIFxcMQ9I2//UMOOW7absb6KDA8IbBOLnnxePT4ErIc2VS/BuWzWlN2RNq7JRj+ JhR3DC/k9zEmNPsfk1uZVQsEucTFBttnCPd6HsGJzkGHHAf9h8yXlsZk7mvQ6L1bvXqW nZFizj9LJz+UwMEahJvugEHA95TV0rAqyjkdMBG4upbw91RoXagalL5+5dTrx1l7qn1D QMlw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=MMnn0z4iV4PJ/axzkiucRlAm/k7ZZcvfPJCs1gXrpHQ=; b=BMOefT9yl5zRLNToGSG9gm7m+ddCyZzDappwgXjVPrJ28GuzmymtDhj9Os2qtd+EZS T4MdmF+qNF1t0Ce5GmExI+1QXCIBUadO+RJeH/lUOyvS98XjZ7QMQgRhTgZGqIMQGkce 9CGxRk4VUi3zjPeK0q9Q/Oh0GQfPZH2+izcKAKkNL+j0LGCFsWLYnWtbQrAwAyFDjRGW TWYKw+kEJIpx0rpTLZxnxNWoF8uEF5tRuxvb7Kf/91zAE0VSqJndO7yd35svfChZs1H+ c5JaL9Q8wxxshd0C/itl6BR1wRVKKIDW7cMipEVkVyEHDfv/A/uH9r0TewakcQnM7Imr lL5w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=PEAHPJw7; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id c30-20020a17090620de00b0087cc469b193si7172644ejc.836.2023.02.17.08.27.27; Fri, 17 Feb 2023 08:27:50 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=PEAHPJw7; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230024AbjBQQUT (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52340 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230049AbjBQQUM (ORCPT ); Fri, 17 Feb 2023 11:20:12 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 72E3925E16 for ; Fri, 17 Feb 2023 08:18:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650731; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=MMnn0z4iV4PJ/axzkiucRlAm/k7ZZcvfPJCs1gXrpHQ=; b=PEAHPJw7Qn713FxMzlo15ND1ylRQhtGxNIiNAqlDWBKEN6vl2uE/8FYzKXkQfEzQfdEcQU j2KWngeqHRbMm2RJEmN2zCgpdrfVCX9A8n7znpK/77H4J3bldqQd8sYIdIcVwNWU5TxAph Oz9MsUgwZkiUhSQtUwvsQezYRao/dJk= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-468-SEijkpmEPBKIXmMe_8uLOQ-1; Fri, 17 Feb 2023 11:18:48 -0500 X-MC-Unique: SEijkpmEPBKIXmMe_8uLOQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id BED57101B42B; Fri, 17 Feb 2023 16:18:47 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 41AE4C15BA0; Fri, 17 Feb 2023 16:18:46 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:17:58 +0100 Subject: [PATCH 04/11] selftests: hid: import hid-tools hid-keyboards tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-4-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Peter Hutterer , Roderick Colenbrander , Nicolas Saenz Julienne X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=22934; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=KK0MRAuOIROX7h7CQ9r1zTW58y1MCSjT9UIs2lXhrSA=; b=TVrsHjtNufCb2nH8HJ/tp2S+aor6TkZvtTks2mY7hamMf61BoYyfr+JuU17B7QIM1KoCX2pJ0 PwfzALIs66xAatBUYjGSvzAv43wjh/l+6vujP2of5np/7LYGG40tNdl X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096282612576982?= X-GMAIL-MSGID: =?utf-8?q?1758096282612576982?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Nicolas Saenz Julienne Cc: Peter Hutterer Cc: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-keyboard.sh | 7 + tools/testing/selftests/hid/tests/test_keyboard.py | 485 +++++++++++++++++++++ 3 files changed, 493 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index d16a22477140..181a594ffe92 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -7,6 +7,7 @@ include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh TEST_PROGS += hid-gamepad.sh +TEST_PROGS += hid-keyboard.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-keyboard.sh b/tools/testing/selftests/hid/hid-keyboard.sh new file mode 100755 index 000000000000..55368f17d1d5 --- /dev/null +++ b/tools/testing/selftests/hid/hid-keyboard.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_keyboard.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_keyboard.py b/tools/testing/selftests/hid/tests/test_keyboard.py new file mode 100644 index 000000000000..b3b2bdbf63b7 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_keyboard.py @@ -0,0 +1,485 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018 Benjamin Tissoires +# Copyright (c) 2018 Red Hat, Inc. +# + +from . import base +import hidtools.hid +import libevdev +import logging + +logger = logging.getLogger("hidtools.test.keyboard") + + +class InvalidHIDCommunication(Exception): + pass + + +class KeyboardData(object): + pass + + +class BaseKeyboard(base.UHIDTestDevice): + def __init__(self, rdesc, name=None, input_info=None): + assert rdesc is not None + super().__init__(name, "Key", input_info=input_info, rdesc=rdesc) + self.keystates = {} + + def _update_key_state(self, keys): + """ + Update the internal state of keys with the new state given. + + :param key: a tuple of chars for the currently pressed keys. + """ + # First remove the already released keys + unused_keys = [k for k, v in self.keystates.items() if not v] + for key in unused_keys: + del self.keystates[key] + + # self.keystates contains now the list of currently pressed keys, + # release them... + for key in self.keystates.keys(): + self.keystates[key] = False + + # ...and press those that are in parameter + for key in keys: + self.keystates[key] = True + + def _create_report_data(self): + keyboard = KeyboardData() + for key, value in self.keystates.items(): + key = key.replace(" ", "").lower() + setattr(keyboard, key, value) + return keyboard + + def create_array_report(self, keys, reportID=None, application=None): + """ + Return an input report for this device. + + :param keys: a tuple of chars for the pressed keys. The class maintains + the list of currently pressed keys, so to release a key, the caller + needs to call again this function without the key in this tuple. + :param reportID: the numeric report ID for this report, if needed + """ + self._update_key_state(keys) + reportID = reportID or self.default_reportID + + keyboard = self._create_report_data() + return self.create_report(keyboard, reportID=reportID, application=application) + + def event(self, keys, reportID=None, application=None): + """ + Send an input event on the default report ID. + + :param keys: a tuple of chars for the pressed keys. The class maintains + the list of currently pressed keys, so to release a key, the caller + needs to call again this function without the key in this tuple. + """ + r = self.create_array_report(keys, reportID, application) + self.call_input_event(r) + return [r] + + +class PlainKeyboard(BaseKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x01, # .Report ID (1) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x19, 0x00, # .Usage Minimum (0) + 0x29, 0x97, # .Usage Maximum (151) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x98, # .Report Count (152) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + self.default_reportID = 1 + + +class ArrayKeyboard(BaseKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x95, 0x06, # .Report Count (6) + 0x75, 0x08, # .Report Size (8) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xa4, 0x00, # .Logical Maximum (164) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0x00, # .Usage Minimum (0) + 0x29, 0xa4, # .Usage Maximum (164) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + + def _create_report_data(self): + data = KeyboardData() + array = [] + + hut = hidtools.hut.HUT + + # strip modifiers from the array + for k, v in self.keystates.items(): + # we ignore depressed keys + if not v: + continue + + usage = hut[0x07].from_name[k].usage + if usage >= 224 and usage <= 231: + # modifier + setattr(data, k.lower(), 1) + else: + array.append(k) + + # if array length is bigger than 6, report ErrorRollOver + if len(array) > 6: + array = ["ErrorRollOver"] * 6 + + data.keyboard = array + return data + + +class LEDKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x95, 0x01, # .Report Count (1) + 0x75, 0x08, # .Report Size (8) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x95, 0x05, # .Report Count (5) + 0x75, 0x01, # .Report Size (1) + 0x05, 0x08, # .Usage Page (LEDs) + 0x19, 0x01, # .Usage Minimum (1) + 0x29, 0x05, # .Usage Maximum (5) + 0x91, 0x02, # .Output (Data,Var,Abs) + 0x95, 0x01, # .Report Count (1) + 0x75, 0x03, # .Report Size (3) + 0x91, 0x01, # .Output (Cnst,Arr,Abs) + 0x95, 0x06, # .Report Count (6) + 0x75, 0x08, # .Report Size (8) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xa4, 0x00, # .Logical Maximum (164) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0x00, # .Usage Minimum (0) + 0x29, 0xa4, # .Usage Maximum (164) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + + +# Some Primax manufactured keyboards set the Usage Page after having defined +# some local Usages. It relies on the fact that the specification states that +# Usages are to be concatenated with Usage Pages upon finding a Main item (see +# 6.2.2.8). This test covers this case. +class PrimaxKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xA1, 0x01, # Collection (Application) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xE0, # .Usage Minimum (224) + 0x29, 0xE7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x01, # .Report Count (1) + 0x81, 0x01, # .Input (Data,Var,Abs) + 0x05, 0x08, # .Usage Page (LEDs) + 0x19, 0x01, # .Usage Minimum (1) + 0x29, 0x03, # .Usage Maximum (3) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x03, # .Report Count (3) + 0x91, 0x02, # .Output (Data,Var,Abs) + 0x95, 0x01, # .Report Count (1) + 0x75, 0x05, # .Report Size (5) + 0x91, 0x01, # .Output (Constant) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xFF, 0x00, # .Logical Maximum (255) + 0x19, 0x00, # .Usage Minimum (0) + 0x2A, 0xFF, 0x00, # .Usage Maximum (255) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x06, # .Report Count (6) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xC0, # End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + + +class BaseTest: + class TestKeyboard(base.BaseTestCase.TestUhid): + def test_single_key(self): + """check for key reliability.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["a and A"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 0 + + def test_two_keys(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["a and A", "q and Q"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_A] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_Q] == 0 + + r = uhdev.event(["c and C"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_C] == 1 + + r = uhdev.event(["c and C", "Spacebar"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.KEY_C) not in events + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_C] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1 + + r = uhdev.event(["Spacebar"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE) not in events + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_C] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 0 + + def test_modifiers(self): + # ctrl-alt-del would be very nice :) + uhdev = self.uhdev + syn_event = self.syn_event + + r = uhdev.event(["LeftControl", "LeftShift", "= and +"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTCTRL, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTSHIFT, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_EQUAL, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + +class TestPlainKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return PlainKeyboard() + + def test_10_keys(self): + uhdev = self.uhdev + syn_event = self.syn_event + + r = uhdev.event( + [ + "1 and !", + "2 and @", + "3 and #", + "4 and $", + "5 and %", + "6 and ^", + "7 and &", + "8 and *", + "9 and (", + "0 and )", + ] + ) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + +class TestArrayKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return ArrayKeyboard() + + def test_10_keys(self): + uhdev = self.uhdev + syn_event = self.syn_event + + r = uhdev.event( + [ + "1 and !", + "2 and @", + "3 and #", + "4 and $", + "5 and %", + "6 and ^", + ] + ) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1)) + events = uhdev.next_sync_events() + + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + # ErrRollOver + r = uhdev.event( + [ + "1 and !", + "2 and @", + "3 and #", + "4 and $", + "5 and %", + "6 and ^", + "7 and &", + "8 and *", + "9 and (", + "0 and )", + ] + ) + events = uhdev.next_sync_events() + + self.debug_reports(r, uhdev, events) + + assert len(events) == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + +class TestLEDKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return LEDKeyboard() + + +class TestPrimaxKeyboard(BaseTest.TestKeyboard): + def create_device(self): + return PrimaxKeyboard() From patchwork Fri Feb 17 16:17:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58699 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981086wrn; Fri, 17 Feb 2023 08:28:36 -0800 (PST) X-Google-Smtp-Source: AK7set/cimQ6tEtHXoukqumC0LBs+WS8i+rtT+Dndof14S8DVugfvs2yfkV/51gvfn+df9R0euQC X-Received: by 2002:a17:906:fa99:b0:888:1f21:4429 with SMTP id lt25-20020a170906fa9900b008881f214429mr11015876ejb.19.1676651316258; Fri, 17 Feb 2023 08:28:36 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651316; cv=none; d=google.com; s=arc-20160816; b=gOZcn8TXml6ogCwNoEQdTewJnbeMkB072cBlYgpdwH3Lr2+sm/RqySDUNKEt+HRv7z r3dTDb2ue+JkVn00ogYGaQt6wpCjqJ1uv9SS2qgxCdkHOPV9SptiKetN+XQvS28BqijI IZPQr7Q+7TEy6jD+6wL66eQ3Lwaa6hIvpx5FQ/E9yILpqbVXtQgRCKNpEZJ0QlwIelo7 Yeru6gKE4FMWC4JC16iWfK67dG6NYpIlvkfsXdjDooxhlMDrlZc/PDZqYWCGpzDgsBUL t3apbBRSSv+pThxLmcIQwyru2HMom/+4kKDJoE/1llv0dg1CncgcZADoPiPtk8pO42mk SZ8g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=7ObCfTTPLygZgTmXRi7tk2ZjPD2rLJsjHwYlvpQVDQ0=; b=FwfBYtuprqe1llGDYB31ROd2a6zMqV7vioyAIicGB1egTt51YxuVA5HNDRSX96qMAw hzeCKnG+AsnjSp7k9kMOz1800yYCQ4XnK6c8d61G1VmBe0sl56NnJY40K8BYBDUGsHwu tQs43pZzgHMVM6GD5BscsHUA8PgWU4gzAfN/usiaGwL9zSlyPPkqxPLyb0k0onUf6FYD s6DVOENH3bYxiAKC5/7kTwhJTrweF9ubY6xEDnSYtK3XeQubgBAOBGZGt7DRILe8IPVJ 6zPv+x4depmKbdDtdv7k+k7DZzMc4U9e9uui64cYl3sRfcHxSuJOjJcSOyzFpKxSDWK9 xiew== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Yww4Z9VS; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id e6-20020a170906748600b0087bdc5a77f8si7685441ejl.118.2023.02.17.08.28.13; Fri, 17 Feb 2023 08:28:36 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Yww4Z9VS; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231226AbjBQQUq (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:46 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52320 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231179AbjBQQUU (ORCPT ); Fri, 17 Feb 2023 11:20:20 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E4F03714B2 for ; Fri, 17 Feb 2023 08:18:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650734; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=7ObCfTTPLygZgTmXRi7tk2ZjPD2rLJsjHwYlvpQVDQ0=; b=Yww4Z9VSP6/RdyQDiM7YEudpy289TuslRuBcvZ+7vA0Gbd1UfAm64Y1AQPu4WMhjVKAdUL XcWENQ4BrizOXcMjc8RAFcN+FgnoZH6jxneCH8m7EOHWmLSirzBM70NMvqQOzwybkbsrmx yp4wh0H/KZKv1YeFuZUJhEwOqvNMbGg= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-463-CdYZpVB_PS2lzrfnw7NryA-1; Fri, 17 Feb 2023 11:18:50 -0500 X-MC-Unique: CdYZpVB_PS2lzrfnw7NryA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 390FA830F85; Fri, 17 Feb 2023 16:18:50 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4273EC15BA0; Fri, 17 Feb 2023 16:18:48 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:17:59 +0100 Subject: [PATCH 05/11] selftests: hid: import hid-tools hid-mouse tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-5-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Peter Hutterer , Roderick Colenbrander X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=47123; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=oWkDvsqHrqTv8jtBoQBWA+iqtGsSkwr8ZCnsvbV0fWw=; b=wiWUJPjPxQN84S+k7XcmiCAt0rnPTG5T/sC3yOBrQa5vBom+Z34fiJoxLqKydY8RZXfvqjU1w 1hNbuRQfIjLDo1yqnrWcIL6vVWS7XWK180ep1oxa7c0dRrecRWwL7DR X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096330880957152?= X-GMAIL-MSGID: =?utf-8?q?1758096330880957152?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Cc: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-mouse.sh | 7 + tools/testing/selftests/hid/tests/test_mouse.py | 977 ++++++++++++++++++++++++ 3 files changed, 985 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 181a594ffe92..00b2cc25e24c 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -8,6 +8,7 @@ include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh TEST_PROGS += hid-gamepad.sh TEST_PROGS += hid-keyboard.sh +TEST_PROGS += hid-mouse.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-mouse.sh b/tools/testing/selftests/hid/hid-mouse.sh new file mode 100755 index 000000000000..7b4ad4f646f7 --- /dev/null +++ b/tools/testing/selftests/hid/hid-mouse.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_mouse.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_mouse.py b/tools/testing/selftests/hid/tests/test_mouse.py new file mode 100644 index 000000000000..fd2ba62e783a --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_mouse.py @@ -0,0 +1,977 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# + +from . import base +import hidtools.hid +from hidtools.util import BusType +import libevdev +import logging +import pytest + +logger = logging.getLogger("hidtools.test.mouse") + +# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6 +try: + libevdev.EV_REL.REL_WHEEL_HI_RES +except AttributeError: + libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B + libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C + + +class InvalidHIDCommunication(Exception): + pass + + +class MouseData(object): + pass + + +class BaseMouse(base.UHIDTestDevice): + def __init__(self, rdesc, name=None, input_info=None): + assert rdesc is not None + super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc) + self.left = False + self.right = False + self.middle = False + + def create_report(self, x, y, buttons=None, wheels=None, reportID=None): + """ + Return an input report for this device. + + :param x: relative x + :param y: relative y + :param buttons: a (l, r, m) tuple of bools for the button states, + where ``None`` is "leave unchanged" + :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for + the two wheels + :param reportID: the numeric report ID for this report, if needed + """ + if buttons is not None: + l, r, m = buttons + if l is not None: + self.left = l + if r is not None: + self.right = r + if m is not None: + self.middle = m + left = self.left + right = self.right + middle = self.middle + # Note: the BaseMouse doesn't actually have a wheel but the + # create_report magic only fills in those fields exist, so let's + # make this generic here. + wheel, acpan = 0, 0 + if wheels is not None: + if isinstance(wheels, tuple): + wheel = wheels[0] + acpan = wheels[1] + else: + wheel = wheels + + reportID = reportID or self.default_reportID + + mouse = MouseData() + mouse.b1 = int(left) + mouse.b2 = int(right) + mouse.b3 = int(middle) + mouse.x = x + mouse.y = y + mouse.wheel = wheel + mouse.acpan = acpan + return super().create_report(mouse, reportID=reportID) + + def event(self, x, y, buttons=None, wheels=None): + """ + Send an input event on the default report ID. + + :param x: relative x + :param y: relative y + :param buttons: a (l, r, m) tuple of bools for the button states, + where ``None`` is "leave unchanged" + :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for + the two wheels + """ + r = self.create_report(x, y, buttons, wheels) + self.call_input_event(r) + return [r] + + +class ButtonMouse(BaseMouse): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # .Usage Page (Generic Desktop) 0 + 0x09, 0x02, # .Usage (Mouse) 2 + 0xa1, 0x01, # .Collection (Application) 4 + 0x09, 0x02, # ..Usage (Mouse) 6 + 0xa1, 0x02, # ..Collection (Logical) 8 + 0x09, 0x01, # ...Usage (Pointer) 10 + 0xa1, 0x00, # ...Collection (Physical) 12 + 0x05, 0x09, # ....Usage Page (Button) 14 + 0x19, 0x01, # ....Usage Minimum (1) 16 + 0x29, 0x03, # ....Usage Maximum (3) 18 + 0x15, 0x00, # ....Logical Minimum (0) 20 + 0x25, 0x01, # ....Logical Maximum (1) 22 + 0x75, 0x01, # ....Report Size (1) 24 + 0x95, 0x03, # ....Report Count (3) 26 + 0x81, 0x02, # ....Input (Data,Var,Abs) 28 + 0x75, 0x05, # ....Report Size (5) 30 + 0x95, 0x01, # ....Report Count (1) 32 + 0x81, 0x03, # ....Input (Cnst,Var,Abs) 34 + 0x05, 0x01, # ....Usage Page (Generic Desktop) 36 + 0x09, 0x30, # ....Usage (X) 38 + 0x09, 0x31, # ....Usage (Y) 40 + 0x15, 0x81, # ....Logical Minimum (-127) 42 + 0x25, 0x7f, # ....Logical Maximum (127) 44 + 0x75, 0x08, # ....Report Size (8) 46 + 0x95, 0x02, # ....Report Count (2) 48 + 0x81, 0x06, # ....Input (Data,Var,Rel) 50 + 0xc0, # ...End Collection 52 + 0xc0, # ..End Collection 53 + 0xc0, # .End Collection 54 + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + + def fake_report(self, x, y, buttons): + if buttons is not None: + left, right, middle = buttons + if left is None: + left = self.left + if right is None: + right = self.right + if middle is None: + middle = self.middle + else: + left = self.left + right = self.right + middle = self.middle + + button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b) + x = max(-127, min(127, x)) + y = max(-127, min(127, y)) + x = hidtools.util.to_twos_comp(x, 8) + y = hidtools.util.to_twos_comp(y, 8) + return [button_mask, x, y] + + +class WheelMouse(ButtonMouse): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) 0 + 0x09, 0x02, # Usage (Mouse) 2 + 0xa1, 0x01, # Collection (Application) 4 + 0x05, 0x09, # .Usage Page (Button) 6 + 0x19, 0x01, # .Usage Minimum (1) 8 + 0x29, 0x03, # .Usage Maximum (3) 10 + 0x15, 0x00, # .Logical Minimum (0) 12 + 0x25, 0x01, # .Logical Maximum (1) 14 + 0x95, 0x03, # .Report Count (3) 16 + 0x75, 0x01, # .Report Size (1) 18 + 0x81, 0x02, # .Input (Data,Var,Abs) 20 + 0x95, 0x01, # .Report Count (1) 22 + 0x75, 0x05, # .Report Size (5) 24 + 0x81, 0x03, # .Input (Cnst,Var,Abs) 26 + 0x05, 0x01, # .Usage Page (Generic Desktop) 28 + 0x09, 0x01, # .Usage (Pointer) 30 + 0xa1, 0x00, # .Collection (Physical) 32 + 0x09, 0x30, # ..Usage (X) 34 + 0x09, 0x31, # ..Usage (Y) 36 + 0x15, 0x81, # ..Logical Minimum (-127) 38 + 0x25, 0x7f, # ..Logical Maximum (127) 40 + 0x75, 0x08, # ..Report Size (8) 42 + 0x95, 0x02, # ..Report Count (2) 44 + 0x81, 0x06, # ..Input (Data,Var,Rel) 46 + 0xc0, # .End Collection 48 + 0x09, 0x38, # .Usage (Wheel) 49 + 0x15, 0x81, # .Logical Minimum (-127) 51 + 0x25, 0x7f, # .Logical Maximum (127) 53 + 0x75, 0x08, # .Report Size (8) 55 + 0x95, 0x01, # .Report Count (1) 57 + 0x81, 0x06, # .Input (Data,Var,Rel) 59 + 0xc0, # End Collection 61 + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + self.wheel_multiplier = 1 + + +class TwoWheelMouse(WheelMouse): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) 0 + 0x09, 0x02, # Usage (Mouse) 2 + 0xa1, 0x01, # Collection (Application) 4 + 0x09, 0x01, # .Usage (Pointer) 6 + 0xa1, 0x00, # .Collection (Physical) 8 + 0x05, 0x09, # ..Usage Page (Button) 10 + 0x19, 0x01, # ..Usage Minimum (1) 12 + 0x29, 0x10, # ..Usage Maximum (16) 14 + 0x15, 0x00, # ..Logical Minimum (0) 16 + 0x25, 0x01, # ..Logical Maximum (1) 18 + 0x95, 0x10, # ..Report Count (16) 20 + 0x75, 0x01, # ..Report Size (1) 22 + 0x81, 0x02, # ..Input (Data,Var,Abs) 24 + 0x05, 0x01, # ..Usage Page (Generic Desktop) 26 + 0x16, 0x01, 0x80, # ..Logical Minimum (-32767) 28 + 0x26, 0xff, 0x7f, # ..Logical Maximum (32767) 31 + 0x75, 0x10, # ..Report Size (16) 34 + 0x95, 0x02, # ..Report Count (2) 36 + 0x09, 0x30, # ..Usage (X) 38 + 0x09, 0x31, # ..Usage (Y) 40 + 0x81, 0x06, # ..Input (Data,Var,Rel) 42 + 0x15, 0x81, # ..Logical Minimum (-127) 44 + 0x25, 0x7f, # ..Logical Maximum (127) 46 + 0x75, 0x08, # ..Report Size (8) 48 + 0x95, 0x01, # ..Report Count (1) 50 + 0x09, 0x38, # ..Usage (Wheel) 52 + 0x81, 0x06, # ..Input (Data,Var,Rel) 54 + 0x05, 0x0c, # ..Usage Page (Consumer Devices) 56 + 0x0a, 0x38, 0x02, # ..Usage (AC Pan) 58 + 0x95, 0x01, # ..Report Count (1) 61 + 0x81, 0x06, # ..Input (Data,Var,Rel) 63 + 0xc0, # .End Collection 65 + 0xc0, # End Collection 66 + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + self.hwheel_multiplier = 1 + + +class MIDongleMIWirelessMouse(TwoWheelMouse): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x02, # Usage (Mouse) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x01, # .Report ID (1) + 0x09, 0x01, # .Usage (Pointer) + 0xa1, 0x00, # .Collection (Physical) + 0x95, 0x05, # ..Report Count (5) + 0x75, 0x01, # ..Report Size (1) + 0x05, 0x09, # ..Usage Page (Button) + 0x19, 0x01, # ..Usage Minimum (1) + 0x29, 0x05, # ..Usage Maximum (5) + 0x15, 0x00, # ..Logical Minimum (0) + 0x25, 0x01, # ..Logical Maximum (1) + 0x81, 0x02, # ..Input (Data,Var,Abs) + 0x95, 0x01, # ..Report Count (1) + 0x75, 0x03, # ..Report Size (3) + 0x81, 0x01, # ..Input (Cnst,Arr,Abs) + 0x75, 0x08, # ..Report Size (8) + 0x95, 0x01, # ..Report Count (1) + 0x05, 0x01, # ..Usage Page (Generic Desktop) + 0x09, 0x38, # ..Usage (Wheel) + 0x15, 0x81, # ..Logical Minimum (-127) + 0x25, 0x7f, # ..Logical Maximum (127) + 0x81, 0x06, # ..Input (Data,Var,Rel) + 0x05, 0x0c, # ..Usage Page (Consumer Devices) + 0x0a, 0x38, 0x02, # ..Usage (AC Pan) + 0x95, 0x01, # ..Report Count (1) + 0x81, 0x06, # ..Input (Data,Var,Rel) + 0xc0, # .End Collection + 0x85, 0x02, # .Report ID (2) + 0x09, 0x01, # .Usage (Consumer Control) + 0xa1, 0x00, # .Collection (Physical) + 0x75, 0x0c, # ..Report Size (12) + 0x95, 0x02, # ..Report Count (2) + 0x05, 0x01, # ..Usage Page (Generic Desktop) + 0x09, 0x30, # ..Usage (X) + 0x09, 0x31, # ..Usage (Y) + 0x16, 0x01, 0xf8, # ..Logical Minimum (-2047) + 0x26, 0xff, 0x07, # ..Logical Maximum (2047) + 0x81, 0x06, # ..Input (Data,Var,Rel) + 0xc0, # .End Collection + 0xc0, # End Collection + 0x05, 0x0c, # Usage Page (Consumer Devices) + 0x09, 0x01, # Usage (Consumer Control) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x03, # .Report ID (3) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x01, # .Report Count (1) + 0x09, 0xcd, # .Usage (Play/Pause) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0x0a, 0x83, 0x01, # .Usage (AL Consumer Control Config) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0x09, 0xb5, # .Usage (Scan Next Track) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0x09, 0xb6, # .Usage (Scan Previous Track) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0x09, 0xea, # .Usage (Volume Down) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0x09, 0xe9, # .Usage (Volume Up) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0x0a, 0x25, 0x02, # .Usage (AC Forward) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0x0a, 0x24, 0x02, # .Usage (AC Back) + 0x81, 0x06, # .Input (Data,Var,Rel) + 0xc0, # End Collection + ] + # fmt: on + device_input_info = (BusType.USB, 0x2717, 0x003B) + device_name = "uhid test MI Dongle MI Wireless Mouse" + + def __init__( + self, rdesc=report_descriptor, name=device_name, input_info=device_input_info + ): + super().__init__(rdesc, name, input_info) + + def event(self, x, y, buttons=None, wheels=None): + # this mouse spreads the relative pointer and the mouse buttons + # onto 2 distinct reports + rs = [] + r = self.create_report(x, y, buttons, wheels, reportID=1) + self.call_input_event(r) + rs.append(r) + r = self.create_report(x, y, buttons, reportID=2) + self.call_input_event(r) + rs.append(r) + return rs + + +class ResolutionMultiplierMouse(TwoWheelMouse): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) 83 + 0x09, 0x02, # Usage (Mouse) 85 + 0xa1, 0x01, # Collection (Application) 87 + 0x05, 0x01, # .Usage Page (Generic Desktop) 89 + 0x09, 0x02, # .Usage (Mouse) 91 + 0xa1, 0x02, # .Collection (Logical) 93 + 0x85, 0x11, # ..Report ID (17) 95 + 0x09, 0x01, # ..Usage (Pointer) 97 + 0xa1, 0x00, # ..Collection (Physical) 99 + 0x05, 0x09, # ...Usage Page (Button) 101 + 0x19, 0x01, # ...Usage Minimum (1) 103 + 0x29, 0x03, # ...Usage Maximum (3) 105 + 0x95, 0x03, # ...Report Count (3) 107 + 0x75, 0x01, # ...Report Size (1) 109 + 0x25, 0x01, # ...Logical Maximum (1) 111 + 0x81, 0x02, # ...Input (Data,Var,Abs) 113 + 0x95, 0x01, # ...Report Count (1) 115 + 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 117 + 0x09, 0x05, # ...Usage (Vendor Usage 0x05) 119 + 0x81, 0x02, # ...Input (Data,Var,Abs) 121 + 0x95, 0x03, # ...Report Count (3) 123 + 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 125 + 0x05, 0x01, # ...Usage Page (Generic Desktop) 127 + 0x09, 0x30, # ...Usage (X) 129 + 0x09, 0x31, # ...Usage (Y) 131 + 0x95, 0x02, # ...Report Count (2) 133 + 0x75, 0x08, # ...Report Size (8) 135 + 0x15, 0x81, # ...Logical Minimum (-127) 137 + 0x25, 0x7f, # ...Logical Maximum (127) 139 + 0x81, 0x06, # ...Input (Data,Var,Rel) 141 + 0xa1, 0x02, # ...Collection (Logical) 143 + 0x85, 0x12, # ....Report ID (18) 145 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 147 + 0x95, 0x01, # ....Report Count (1) 149 + 0x75, 0x02, # ....Report Size (2) 151 + 0x15, 0x00, # ....Logical Minimum (0) 153 + 0x25, 0x01, # ....Logical Maximum (1) 155 + 0x35, 0x01, # ....Physical Minimum (1) 157 + 0x45, 0x04, # ....Physical Maximum (4) 159 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 161 + 0x35, 0x00, # ....Physical Minimum (0) 163 + 0x45, 0x00, # ....Physical Maximum (0) 165 + 0x75, 0x06, # ....Report Size (6) 167 + 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 169 + 0x85, 0x11, # ....Report ID (17) 171 + 0x09, 0x38, # ....Usage (Wheel) 173 + 0x15, 0x81, # ....Logical Minimum (-127) 175 + 0x25, 0x7f, # ....Logical Maximum (127) 177 + 0x75, 0x08, # ....Report Size (8) 179 + 0x81, 0x06, # ....Input (Data,Var,Rel) 181 + 0xc0, # ...End Collection 183 + 0x05, 0x0c, # ...Usage Page (Consumer Devices) 184 + 0x75, 0x08, # ...Report Size (8) 186 + 0x0a, 0x38, 0x02, # ...Usage (AC Pan) 188 + 0x81, 0x06, # ...Input (Data,Var,Rel) 191 + 0xc0, # ..End Collection 193 + 0xc0, # .End Collection 194 + 0xc0, # End Collection 195 + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + self.default_reportID = 0x11 + + # Feature Report 12, multiplier Feature value must be set to 0b01, + # i.e. 1. We should extract that from the descriptor instead + # of hardcoding it here, but meanwhile this will do. + self.set_feature_report = [0x12, 0x1] + + def set_report(self, req, rnum, rtype, data): + if rtype != self.UHID_FEATURE_REPORT: + raise InvalidHIDCommunication(f"Unexpected report type: {rtype}") + if rnum != 0x12: + raise InvalidHIDCommunication(f"Unexpected report number: {rnum}") + + if data != self.set_feature_report: + raise InvalidHIDCommunication( + f"Unexpected data: {data}, expected {self.set_feature_report}" + ) + + self.wheel_multiplier = 4 + + return 0 + + +class BadResolutionMultiplierMouse(ResolutionMultiplierMouse): + def set_report(self, req, rnum, rtype, data): + super().set_report(req, rnum, rtype, data) + + self.wheel_multiplier = 1 + self.hwheel_multiplier = 1 + return 32 # EPIPE + + +class ResolutionMultiplierHWheelMouse(TwoWheelMouse): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) 0 + 0x09, 0x02, # Usage (Mouse) 2 + 0xa1, 0x01, # Collection (Application) 4 + 0x05, 0x01, # .Usage Page (Generic Desktop) 6 + 0x09, 0x02, # .Usage (Mouse) 8 + 0xa1, 0x02, # .Collection (Logical) 10 + 0x85, 0x1a, # ..Report ID (26) 12 + 0x09, 0x01, # ..Usage (Pointer) 14 + 0xa1, 0x00, # ..Collection (Physical) 16 + 0x05, 0x09, # ...Usage Page (Button) 18 + 0x19, 0x01, # ...Usage Minimum (1) 20 + 0x29, 0x05, # ...Usage Maximum (5) 22 + 0x95, 0x05, # ...Report Count (5) 24 + 0x75, 0x01, # ...Report Size (1) 26 + 0x15, 0x00, # ...Logical Minimum (0) 28 + 0x25, 0x01, # ...Logical Maximum (1) 30 + 0x81, 0x02, # ...Input (Data,Var,Abs) 32 + 0x75, 0x03, # ...Report Size (3) 34 + 0x95, 0x01, # ...Report Count (1) 36 + 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38 + 0x05, 0x01, # ...Usage Page (Generic Desktop) 40 + 0x09, 0x30, # ...Usage (X) 42 + 0x09, 0x31, # ...Usage (Y) 44 + 0x95, 0x02, # ...Report Count (2) 46 + 0x75, 0x10, # ...Report Size (16) 48 + 0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50 + 0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53 + 0x81, 0x06, # ...Input (Data,Var,Rel) 56 + 0xa1, 0x02, # ...Collection (Logical) 58 + 0x85, 0x12, # ....Report ID (18) 60 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 62 + 0x95, 0x01, # ....Report Count (1) 64 + 0x75, 0x02, # ....Report Size (2) 66 + 0x15, 0x00, # ....Logical Minimum (0) 68 + 0x25, 0x01, # ....Logical Maximum (1) 70 + 0x35, 0x01, # ....Physical Minimum (1) 72 + 0x45, 0x0c, # ....Physical Maximum (12) 74 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 76 + 0x85, 0x1a, # ....Report ID (26) 78 + 0x09, 0x38, # ....Usage (Wheel) 80 + 0x35, 0x00, # ....Physical Minimum (0) 82 + 0x45, 0x00, # ....Physical Maximum (0) 84 + 0x95, 0x01, # ....Report Count (1) 86 + 0x75, 0x10, # ....Report Size (16) 88 + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90 + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93 + 0x81, 0x06, # ....Input (Data,Var,Rel) 96 + 0xc0, # ...End Collection 98 + 0xa1, 0x02, # ...Collection (Logical) 99 + 0x85, 0x12, # ....Report ID (18) 101 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 103 + 0x75, 0x02, # ....Report Size (2) 105 + 0x15, 0x00, # ....Logical Minimum (0) 107 + 0x25, 0x01, # ....Logical Maximum (1) 109 + 0x35, 0x01, # ....Physical Minimum (1) 111 + 0x45, 0x0c, # ....Physical Maximum (12) 113 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 115 + 0x35, 0x00, # ....Physical Minimum (0) 117 + 0x45, 0x00, # ....Physical Maximum (0) 119 + 0x75, 0x04, # ....Report Size (4) 121 + 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123 + 0x85, 0x1a, # ....Report ID (26) 125 + 0x05, 0x0c, # ....Usage Page (Consumer Devices) 127 + 0x95, 0x01, # ....Report Count (1) 129 + 0x75, 0x10, # ....Report Size (16) 131 + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133 + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136 + 0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139 + 0x81, 0x06, # ....Input (Data,Var,Rel) 142 + 0xc0, # ...End Collection 144 + 0xc0, # ..End Collection 145 + 0xc0, # .End Collection 146 + 0xc0, # End Collection 147 + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): + super().__init__(rdesc, name, input_info) + self.default_reportID = 0x1A + + # Feature Report 12, multiplier Feature value must be set to 0b0101, + # i.e. 5. We should extract that from the descriptor instead + # of hardcoding it here, but meanwhile this will do. + self.set_feature_report = [0x12, 0x5] + + def set_report(self, req, rnum, rtype, data): + super().set_report(req, rnum, rtype, data) + + self.wheel_multiplier = 12 + self.hwheel_multiplier = 12 + + return 0 + + +class BaseTest: + class TestMouse(base.BaseTestCase.TestUhid): + def test_buttons(self): + """check for button reliability.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(0, 0, (None, True, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + + r = uhdev.event(0, 0, (None, False, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0 + + r = uhdev.event(0, 0, (None, None, True)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1 + + r = uhdev.event(0, 0, (None, None, False)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0 + + r = uhdev.event(0, 0, (True, None, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(0, 0, (False, None, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + r = uhdev.event(0, 0, (True, True, None)) + expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) + expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn( + (syn_event, expected_event0, expected_event1), events + ) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(0, 0, (False, None, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + r = uhdev.event(0, 0, (None, False, None)) + expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn((syn_event, expected_event), events) + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + def test_relative(self): + """Check for relative events.""" + uhdev = self.uhdev + + syn_event = self.syn_event + + r = uhdev.event(0, -1) + expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents((syn_event, expected_event), events) + + r = uhdev.event(1, 0) + expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents((syn_event, expected_event), events) + + r = uhdev.event(-1, 2) + expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1) + expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents( + (syn_event, expected_event0, expected_event1), events + ) + + +class TestSimpleMouse(BaseTest.TestMouse): + def create_device(self): + return ButtonMouse() + + def test_rdesc(self): + """Check that the testsuite actually manages to format the + reports according to the report descriptors. + No kernel device is used here""" + uhdev = self.uhdev + + event = (0, 0, (None, None, None)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, 0, (None, True, None)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, 0, (True, True, None)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, 0, (False, False, False)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (1, 0, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (-1, 0, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (-5, 5, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (-127, 127, (True, False, True)) + assert uhdev.fake_report(*event) == uhdev.create_report(*event) + + event = (0, -128, (True, False, True)) + with pytest.raises(hidtools.hid.RangeError): + uhdev.create_report(*event) + + +class TestWheelMouse(BaseTest.TestMouse): + def create_device(self): + return WheelMouse() + + def is_wheel_highres(self, uhdev): + evdev = uhdev.get_evdev() + assert evdev.has(libevdev.EV_REL.REL_WHEEL) + return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES) + + def test_wheel(self): + uhdev = self.uhdev + + # check if the kernel is high res wheel compatible + high_res_wheel = self.is_wheel_highres(uhdev) + + syn_event = self.syn_event + # The Resolution Multiplier is applied to the HID reports, so we + # need to pre-multiply too. + mult = uhdev.wheel_multiplier + + r = uhdev.event(0, 0, wheels=1 * mult) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=-1 * mult) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(-1, 2, wheels=3 * mult) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestTwoWheelMouse(TestWheelMouse): + def create_device(self): + return TwoWheelMouse() + + def is_hwheel_highres(self, uhdev): + evdev = uhdev.get_evdev() + assert evdev.has(libevdev.EV_REL.REL_HWHEEL) + return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES) + + def test_ac_pan(self): + uhdev = self.uhdev + + # check if the kernel is high res wheel compatible + high_res_wheel = self.is_wheel_highres(uhdev) + high_res_hwheel = self.is_hwheel_highres(uhdev) + assert high_res_wheel == high_res_hwheel + + syn_event = self.syn_event + # The Resolution Multiplier is applied to the HID reports, so we + # need to pre-multiply too. + hmult = uhdev.hwheel_multiplier + vmult = uhdev.wheel_multiplier + + r = uhdev.event(0, 0, wheels=(0, 1 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1)) + if high_res_hwheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=(0, -1 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1)) + if high_res_hwheel: + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(-1, 2, wheels=(0, 3 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3)) + if high_res_hwheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4)) + if high_res_wheel: + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestResolutionMultiplierMouse(TestTwoWheelMouse): + def create_device(self): + return ResolutionMultiplierMouse() + + def is_wheel_highres(self, uhdev): + high_res = super().is_wheel_highres(uhdev) + + if not high_res: + # the kernel doesn't seem to support the high res wheel mice, + # make sure we haven't triggered the feature + assert uhdev.wheel_multiplier == 1 + + return high_res + + def test_resolution_multiplier_wheel(self): + uhdev = self.uhdev + + if not self.is_wheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.wheel_multiplier > 1 + assert 120 % uhdev.wheel_multiplier == 0 + + def test_wheel_with_multiplier(self): + uhdev = self.uhdev + + if not self.is_wheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.wheel_multiplier > 1 + + syn_event = self.syn_event + mult = uhdev.wheel_multiplier + + r = uhdev.event(0, 0, wheels=1) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=-1) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2)) + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult) + ) + + for _ in range(mult - 1): + r = uhdev.event(1, -2, wheels=1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(1, -2, wheels=1) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestBadResolutionMultiplierMouse(TestTwoWheelMouse): + def create_device(self): + return BadResolutionMultiplierMouse() + + def is_wheel_highres(self, uhdev): + high_res = super().is_wheel_highres(uhdev) + + assert uhdev.wheel_multiplier == 1 + + return high_res + + def test_resolution_multiplier_wheel(self): + uhdev = self.uhdev + + assert uhdev.wheel_multiplier == 1 + + +class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse): + def create_device(self): + return ResolutionMultiplierHWheelMouse() + + def is_hwheel_highres(self, uhdev): + high_res = super().is_hwheel_highres(uhdev) + + if not high_res: + # the kernel doesn't seem to support the high res wheel mice, + # make sure we haven't triggered the feature + assert uhdev.hwheel_multiplier == 1 + + return high_res + + def test_resolution_multiplier_ac_pan(self): + uhdev = self.uhdev + + if not self.is_hwheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.hwheel_multiplier > 1 + assert 120 % uhdev.hwheel_multiplier == 0 + + def test_ac_pan_with_multiplier(self): + uhdev = self.uhdev + + if not self.is_hwheel_highres(uhdev): + pytest.skip("Kernel not compatible, we can not trigger the conditions") + + assert uhdev.hwheel_multiplier > 1 + + syn_event = self.syn_event + hmult = uhdev.hwheel_multiplier + + r = uhdev.event(0, 0, wheels=(0, 1)) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(0, 0, wheels=(0, -1)) + expected = [syn_event] + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult) + ) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2)) + expected.append( + libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult) + ) + + for _ in range(hmult - 1): + r = uhdev.event(1, -2, wheels=(0, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + r = uhdev.event(1, -2, wheels=(0, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEvents(expected, events) + + +class TestMiMouse(TestWheelMouse): + def create_device(self): + return MIDongleMIWirelessMouse() + + def assertInputEvents(self, expected_events, effective_events): + # Buttons and x/y are spread over two HID reports, so we can get two + # event frames for this device. + remaining = self.assertInputEventsIn(expected_events, effective_events) + try: + remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0)) + except ValueError: + # If there's no SYN_REPORT in the list, continue and let the + # assert below print out the real error + pass + assert remaining == [] From patchwork Fri Feb 17 16:18:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58698 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981008wrn; Fri, 17 Feb 2023 08:28:27 -0800 (PST) X-Google-Smtp-Source: AK7set8N48jsJK2KtJsNOkDScsgLoJEDUrfux7jpg6zGXDFtydACboDqr0z7LfW2vYas8V6UJW3v X-Received: by 2002:a17:907:8a29:b0:86a:316:d107 with SMTP id sc41-20020a1709078a2900b0086a0316d107mr13952377ejc.72.1676651306978; Fri, 17 Feb 2023 08:28:26 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651306; cv=none; d=google.com; s=arc-20160816; b=kKH841kzmNgOWJPaknE4+wfSKJaE8kZPvqqSmZ2FW1pzsufAbU61C7gGgukXK9UBzA 5LcsdfbUd+F8TcNPJqYys1iTd6kCLeJj4pMD1BZuS/LmHjX6B71+f5R0zIwzttxHriF8 Y0xnSAHYyav+gqVgIbjX+Jz7NmSqm0y2kKFIvOMlQg06PHqVFkU8q0w2j0YuevDtA/bN Q5eof9Eoa9pamgEUIScYaNm1AxjcPz6uyGZyWrfPyhlI6GLyF+o8Mresp7kTVjLaJ/Mz tSXY7sXvqwu7aa/Y5WvnWIMotRSNIqPiv18ryaHWiGoTPK0fhfmEziMl31vxSBRG+j6+ 3MhA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=j9kW3KJvvZYn9osS248EVp0eeZaaPLIpe6jiMVABsHs=; b=Zu9vLIuhfBaxdUiTt5QQCgZtIQ/qNK/bSm+GTnhNsoxK02mhbcQDRuEHc/IcIySyMn FmXybzmrWZqHq0msrGETeOhLmwNinaMQzj3H4jCyKi9kjoUAcftd3/JUx/7deAjc7j6G SK1kECm2mSK0tQE9uW2USbeWZceNxn5ID1cCLmqCCkbCfClnV0Q14M/J2P1Iu86pntye SVVyotWKLn9fQWPNCh0YByW1Uq1Nwr1O5Ixd4k/lCqKx/EezuKF4ujqehhVaq+JGiNJd nCVd9atytoG8LfhzAEBl5snYA8ntKWwKE9Czoeqhn9ht2O6d4ZrdIInfzOjLs1YTtV8C Si9Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=BQTm7mV6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id iy8-20020a170907818800b0088ba826c0ccsi4985597ejc.513.2023.02.17.08.28.03; Fri, 17 Feb 2023 08:28:26 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=BQTm7mV6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231169AbjBQQUk (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53118 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231178AbjBQQUU (ORCPT ); Fri, 17 Feb 2023 11:20:20 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 43BB471CA1 for ; Fri, 17 Feb 2023 08:19:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650739; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=j9kW3KJvvZYn9osS248EVp0eeZaaPLIpe6jiMVABsHs=; b=BQTm7mV6dV8HtQSwhyOudrTNrbFo77R3jk4x85vWnS+A6levkW2O/nT8TFLPyDA+ZU4O03 GGl9so1k81T31Eg4AWK9h8TOL8XfimEdu2GnooRno7i4lPjUtcqwYBF7VVTNltMs0+8zXd jHAJphghoUPT7H55vA2LUyGeFooymb0= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-347-YVIPYbQKPAyDApLuuVQyWw-1; Fri, 17 Feb 2023 11:18:55 -0500 X-MC-Unique: YVIPYbQKPAyDApLuuVQyWw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 9EA29381459E; Fri, 17 Feb 2023 16:18:54 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 99281C15BA0; Fri, 17 Feb 2023 16:18:50 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:18:00 +0100 Subject: [PATCH 06/11] selftests: hid: import hid-tools hid-multitouch and hid-tablets tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-6-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Peter Hutterer , Kai-Heng Feng , Roderick Colenbrander , =?utf-8?b?0L3QsNCx?= , =?utf-8?q?Bla?= =?utf-8?q?=C5=BE_Hrastnik?= X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=305421; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=Ic5bGQ6Yzyvl6hiM9/9E1J1yR/IZMmkp8xVEROMNLcs=; b=2khJjA2VD8UD6T4eaN+inlZ3N8No0DLW217HMgA9qjlEsrFDju/Zlu8MBgmwTNj/rG7sLs5rG bpfe6JvF9alBlPuIspNwFCwhJZjnJ6y4oYnGZ6fsPL+fPl3YCsilpcj X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096320619558646?= X-GMAIL-MSGID: =?utf-8?q?1758096320619558646?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. There are a lot of multitouch tests, and the default timeout of 45 seconds is not big enough. Bump it to 200 seconds. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Cc: Kai-Heng Feng Cc: Roderick Colenbrander Cc: наб Cc: Blaž Hrastnik Signed-off-by: Benjamin Tissoires Signed-off-by: Ahelenia Ziemiańska --- tools/testing/selftests/hid/Makefile | 2 + tools/testing/selftests/hid/config | 1 + tools/testing/selftests/hid/hid-multitouch.sh | 7 + tools/testing/selftests/hid/hid-tablet.sh | 7 + tools/testing/selftests/hid/settings | 3 + .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++ tools/testing/selftests/hid/tests/test_tablet.py | 872 ++++++++ 7 files changed, 2980 insertions(+) +################################################################################ +# +# 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 4 6 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 0 0 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), + ) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 00b2cc25e24c..d8ad104c3863 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -9,6 +9,8 @@ TEST_PROGS := hid-core.sh TEST_PROGS += hid-gamepad.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh +TEST_PROGS += hid-multitouch.sh +TEST_PROGS += hid-tablet.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index 9c5a55abca6b..96ae99efcbaa 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -19,3 +19,4 @@ CONFIG_HIDRAW=y CONFIG_HID=y CONFIG_INPUT_EVDEV=y CONFIG_UHID=y +CONFIG_HID_MULTITOUCH=y diff --git a/tools/testing/selftests/hid/hid-multitouch.sh b/tools/testing/selftests/hid/hid-multitouch.sh new file mode 100755 index 000000000000..d03a1ddbfb1f --- /dev/null +++ b/tools/testing/selftests/hid/hid-multitouch.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_multitouch.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/hid-tablet.sh b/tools/testing/selftests/hid/hid-tablet.sh new file mode 100755 index 000000000000..e86b3fedafd9 --- /dev/null +++ b/tools/testing/selftests/hid/hid-tablet.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_tablet.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/settings b/tools/testing/selftests/hid/settings new file mode 100644 index 000000000000..b3cbfc521b10 --- /dev/null +++ b/tools/testing/selftests/hid/settings @@ -0,0 +1,3 @@ +# HID tests can be long, so give a little bit more time +# to them +timeout=200 diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py new file mode 100644 index 000000000000..4265012231c6 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_multitouch.py @@ -0,0 +1,2088 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# + +from . import base +from hidtools.hut import HUT +from hidtools.util import BusType +import libevdev +import logging +import pytest +import sys +import time + +logger = logging.getLogger("hidtools.test.multitouch") + +KERNEL_MODULE = ("hid-multitouch", "hid_multitouch") + + +def BIT(x): + return 1 << x + + +mt_quirks = { + "NOT_SEEN_MEANS_UP": BIT(0), + "SLOT_IS_CONTACTID": BIT(1), + "CYPRESS": BIT(2), + "SLOT_IS_CONTACTNUMBER": BIT(3), + "ALWAYS_VALID": BIT(4), + "VALID_IS_INRANGE": BIT(5), + "VALID_IS_CONFIDENCE": BIT(6), + "CONFIDENCE": BIT(7), + "SLOT_IS_CONTACTID_MINUS_ONE": BIT(8), + "NO_AREA": BIT(9), + "IGNORE_DUPLICATES": BIT(10), + "HOVERING": BIT(11), + "CONTACT_CNT_ACCURATE": BIT(12), + "FORCE_GET_FEATURE": BIT(13), + "FIX_CONST_CONTACT_ID": BIT(14), + "TOUCH_SIZE_SCALING": BIT(15), + "STICKY_FINGERS": BIT(16), + "ASUS_CUSTOM_UP": BIT(17), + "WIN8_PTP_BUTTONS": BIT(18), + "SEPARATE_APP_REPORT": BIT(19), + "MT_QUIRK_FORCE_MULTI_INPUT": BIT(20), +} + + +class Data(object): + pass + + +class Touch(object): + def __init__(self, id, x, y): + self.contactid = id + self.x = x + self.y = y + self.cx = x + self.cy = y + self.tipswitch = True + self.confidence = True + self.tippressure = 15 + self.azimuth = 0 + self.inrange = True + self.width = 10 + self.height = 10 + + +class Pen(Touch): + def __init__(self, x, y): + super().__init__(0, x, y) + self.barrel = False + self.invert = False + self.eraser = False + self.x_tilt = False + self.y_tilt = False + self.twist = 0 + + +class Digitizer(base.UHIDTestDevice): + @classmethod + def msCertificationBlob(cls, reportID): + return f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID ({reportID}) + Usage Page (0xff00) + Usage (0xc5) + Logical Minimum (0) + Logical Maximum (255) + Report Size (8) + Report Count (256) + Feature (Data,Var,Abs) + End Collection + """ + + def __init__( + self, + name, + rdesc_str=None, + rdesc=None, + application="Touch Screen", + physical="Finger", + max_contacts=None, + input_info=(BusType.USB, 1, 2), + quirks=None, + ): + super().__init__(name, application, rdesc_str, rdesc, input_info) + self.scantime = 0 + self.quirks = quirks + if max_contacts is None: + self.max_contacts = sys.maxsize + for features in self.parsed_rdesc.feature_reports.values(): + for feature in features: + if feature.usage_name in ["Contact Max"]: + self.max_contacts = feature.logical_max + for inputs in self.parsed_rdesc.input_reports.values(): + for i in inputs: + if ( + i.usage_name in ["Contact Count"] + and i.logical_max > 0 + and self.max_contacts > i.logical_max + ): + self.max_contacts = i.logical_max + if self.max_contacts == sys.maxsize: + self.max_contacts = 1 + else: + self.max_contacts = max_contacts + self.physical = physical + self.cur_application = application + + for features in self.parsed_rdesc.feature_reports.values(): + for feature in features: + if feature.usage_name == "Inputmode": + self.cur_application = "Mouse" + + self.fields = [] + for r in self.parsed_rdesc.input_reports.values(): + if r.application_name == self.application: + physicals = [f.physical_name for f in r] + if self.physical not in physicals and None not in physicals: + continue + self.fields = [f.usage_name for f in r] + + @property + def touches_in_a_report(self): + return self.fields.count("Contact Id") + + def event(self, slots, global_data=None, contact_count=None, incr_scantime=True): + if incr_scantime: + self.scantime += 1 + rs = [] + # make sure we have only the required number of available slots + slots = slots[: self.max_contacts] + + if global_data is None: + global_data = Data() + if contact_count is None: + global_data.contactcount = len(slots) + else: + global_data.contactcount = contact_count + global_data.scantime = self.scantime + + while len(slots): + r = self.create_report( + application=self.cur_application, data=slots, global_data=global_data + ) + self.call_input_event(r) + rs.append(r) + global_data.contactcount = 0 + return rs + + def get_report(self, req, rnum, rtype): + if rtype != self.UHID_FEATURE_REPORT: + return (1, []) + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return (1, []) + + if "Contact Max" not in [f.usage_name for f in rdesc]: + return (1, []) + + self.contactmax = self.max_contacts + r = rdesc.create_report([self], None) + return (0, r) + + def set_report(self, req, rnum, rtype, data): + if rtype != self.UHID_FEATURE_REPORT: + return 1 + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return 1 + + if "Inputmode" not in [f.usage_name for f in rdesc]: + return 0 + + Inputmode_seen = False + for f in rdesc: + if "Inputmode" == f.usage_name: + values = f.get_values(data) + assert len(values) == 1 + value = values[0] + + if not Inputmode_seen: + Inputmode_seen = True + if value == 0: + self.cur_application = "Mouse" + elif value == 2: + self.cur_application = "Touch Screen" + elif value == 3: + self.cur_application = "Touch Pad" + else: + if value != 0: + # Elan bug where the device doesn't work properly + # if we set twice an Input Mode in the same Feature + self.cur_application = "Mouse" + + return 0 + + +class PTP(Digitizer): + def __init__( + self, + name, + type="Click Pad", + rdesc_str=None, + rdesc=None, + application="Touch Pad", + physical="Pointer", + max_contacts=None, + input_info=None, + ): + self.type = type.lower().replace(" ", "") + if self.type == "clickpad": + self.buttontype = 0 + else: # pressurepad + self.buttontype = 1 + self.clickpad_state = False + self.left_state = False + self.right_state = False + super().__init__( + name, rdesc_str, rdesc, application, physical, max_contacts, input_info + ) + + def event( + self, + slots=None, + click=None, + left=None, + right=None, + contact_count=None, + incr_scantime=True, + ): + # update our internal state + if click is not None: + self.clickpad_state = click + if left is not None: + self.left_state = left + if right is not None: + self.right_state = right + + # now create the global data + global_data = Data() + global_data.b1 = 1 if self.clickpad_state else 0 + global_data.b2 = 1 if self.left_state else 0 + global_data.b3 = 1 if self.right_state else 0 + + if slots is None: + slots = [Data()] + + return super().event(slots, global_data, contact_count, incr_scantime) + + +class MinWin8TSParallel(Digitizer): + def __init__(self, max_slots): + self.max_slots = max_slots + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Report Size (7) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + Usage Page (Digitizers) + Usage (Azimuth) + Logical Maximum (360) + Unit (SILinear: deg) + Report Size (16) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * self.max_slots} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({self.max_slots}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__(f"uhid test parallel {self.max_slots}", rdesc_str) + + +class MinWin8TSHybrid(Digitizer): + def __init__(self): + self.max_slots = 10 + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Report Size (7) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * 2} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({self.max_slots}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__("uhid test hybrid", rdesc_str) + + +class Win8TSConfidence(Digitizer): + def __init__(self, max_slots): + self.max_slots = max_slots + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Usage (Confidence) + Input (Data,Var,Abs) + Report Size (6) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + Usage Page (Digitizers) + Usage (Azimuth) + Logical Maximum (360) + Unit (SILinear: deg) + Report Size (16) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * self.max_slots} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({self.max_slots}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__(f"uhid test confidence {self.max_slots}", rdesc_str) + + +class SmartTechDigitizer(Digitizer): + def __init__(self, name, input_info): + super().__init__( + name, + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 05 81 03 05 01 15 00 26 ff 0f 55 0e 65 11 75 10 95 01 35 00 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 06 15 00 26 ff 00 a1 01 85 02 75 08 95 3f 09 00 82 02 01 95 3f 09 00 92 02 01 c0 05 0d 09 04 a1 01 85 05 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 75 08 95 01 15 00 25 0a 09 54 81 02 09 55 b1 02 c0 05 0d 09 0e a1 01 85 04 09 23 a1 02 15 00 25 02 75 08 95 02 09 52 09 53 b1 02 c0 c0 05 0d 09 04 a1 01 85 03 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a 0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 75 08 95 01 15 00 25 0a 09 54 81 02 09 55 b1 02 c0 05 0d 09 04 a1 01 85 06 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 06 81 03 95 01 75 10 65 11 55 0e 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 07 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 08 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 09 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 0a 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0", + input_info=input_info, + ) + + def create_report(self, data, global_data=None, reportID=None, application=None): + # this device has *a lot* of different reports, and most of them + # have the Touch Screen application. But the first one is a stylus + # report (as stated in the physical type), so we simply override + # the report ID to use what the device sends + return super().create_report(data, global_data=global_data, reportID=3) + + def match_evdev_rule(self, application, evdev): + # we need to select the correct evdev node, as the device has multiple + # Touch Screen application collections + if application != "Touch Screen": + return True + absinfo = evdev.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_X] + return absinfo is not None and absinfo.resolution == 3 + + +class BaseTest: + class TestMultitouch(base.BaseTestCase.TestUhid): + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + raise Exception("please reimplement me in subclasses") + + def get_slot(self, uhdev, t, default): + if uhdev.quirks is None: + return default + + if "SLOT_IS_CONTACTID" in uhdev.quirks: + return t.contactid + + if "SLOT_IS_CONTACTID_MINUS_ONE" in uhdev.quirks: + return t.contactid - 1 + + return default + + def test_creation(self): + """Make sure the device gets processed by the kernel and creates + the expected application input node. + + If this fail, there is something wrong in the device report + descriptors.""" + super().test_creation() + + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + # some sanity checking for the quirks + if uhdev.quirks is not None: + for q in uhdev.quirks: + assert q in mt_quirks + + assert evdev.num_slots == uhdev.max_contacts + + if uhdev.max_contacts > 1: + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + if uhdev.max_contacts > 2: + assert evdev.slots[2][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_required_usages(self): + """Make sure the device exports the correct required features and + inputs.""" + uhdev = self.uhdev + rdesc = uhdev.parsed_rdesc + for feature in rdesc.feature_reports.values(): + for field in feature: + page_id = field.usage >> 16 + value = field.usage & 0xFF + try: + if HUT[page_id][value] == "Contact Max": + assert HUT[page_id][field.application] in [ + "Touch Screen", + "Touch Pad", + "System Multi-Axis Controller", + ] + except KeyError: + pass + + try: + if HUT[page_id][value] == "Inputmode": + assert HUT[page_id][field.application] in [ + "Touch Screen", + "Touch Pad", + "Device Configuration", + ] + except KeyError: + pass + + def test_mt_single_touch(self): + """send a single touch in the first slot of the device, + and release it.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + slot = self.get_slot(uhdev, t0, 0) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + + t0.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t0.inrange = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_mt_dual_touch(self): + """Send 2 touches in the first 2 slots. + Make sure the kernel sees this as a dual touch. + Release and check + + Note: PTP will send here BTN_DOUBLETAP emulation""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + t1 = Touch(2, 150, 200) + + if uhdev.quirks is not None and ( + "SLOT_IS_CONTACTID" in uhdev.quirks + or "SLOT_IS_CONTACTNUMBER" in uhdev.quirks + ): + t1.contactid = 0 + + slot0 = self.get_slot(uhdev, t0, 0) + slot1 = self.get_slot(uhdev, t1, 1) + + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + r = uhdev.event([t0, t1]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events + ) + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events + ) + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + + t0.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t0.inrange = False + r = uhdev.event([t0, t1]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events + + t1.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t1.inrange = False + + if uhdev.quirks is not None and "SLOT_IS_CONTACTNUMBER" in uhdev.quirks: + r = uhdev.event([t0, t1]) + else: + r = uhdev.event([t1]) + + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.max_contacts <= 2, "Device not compatible" + ) + def test_mt_triple_tap(self): + """Send 3 touches in the first 3 slots. + Make sure the kernel sees this as a triple touch. + Release and check + + Note: PTP will send here BTN_TRIPLETAP emulation""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + t1 = Touch(2, 150, 200) + t2 = Touch(3, 250, 300) + r = uhdev.event([t0, t1, t2]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + slot0 = self.get_slot(uhdev, t0, 0) + slot1 = self.get_slot(uhdev, t1, 1) + slot2 = self.get_slot(uhdev, t2, 2) + + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 2 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_POSITION_X] == 250 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 300 + + t0.tipswitch = False + t1.tipswitch = False + t2.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t0.inrange = False + t1.inrange = False + t2.inrange = False + r = uhdev.event([t0, t1, t2]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[slot2][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.max_contacts <= 2, "Device not compatible" + ) + def test_mt_max_contact(self): + """send the maximum number of contact as reported by the device. + Make sure all contacts are forwarded and that there is no miss. + Release and check.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + touches = [ + Touch(i, (i + 3) * 20, (i + 3) * 20 + 5) + for i in range(uhdev.max_contacts) + ] + if ( + uhdev.quirks is not None + and "SLOT_IS_CONTACTID_MINUS_ONE" in uhdev.quirks + ): + for t in touches: + t.contactid += 1 + r = uhdev.event(touches) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + for i, t in enumerate(touches): + slot = self.get_slot(uhdev, t, i) + + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == i + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_X] == t.x + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_Y] == t.y + + for t in touches: + t.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t.inrange = False + + r = uhdev.event(touches) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + for i, t in enumerate(touches): + slot = self.get_slot(uhdev, t, i) + + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: ( + uhdev.touches_in_a_report == 1 + or uhdev.quirks is not None + and "CONTACT_CNT_ACCURATE" not in uhdev.quirks + ), + "Device not compatible, we can not trigger the conditions", + ) + def test_mt_contact_count_accurate(self): + """Test the MT_QUIRK_CONTACT_CNT_ACCURATE from the kernel. + A report should forward an accurate contact count and the kernel + should ignore any data provided after we have reached this + contact count.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 50, 100) + t1 = Touch(2, 150, 200) + + slot0 = self.get_slot(uhdev, t0, 0) + slot1 = self.get_slot(uhdev, t1, 1) + + r = uhdev.event([t0, t1], contact_count=1) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TRACKING_ID, 0) in events + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[slot1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + class TestWin8Multitouch(TestMultitouch): + def test_required_usages8(self): + """Make sure the device exports the correct required features and + inputs.""" + uhdev = self.uhdev + rdesc = uhdev.parsed_rdesc + for feature in rdesc.feature_reports.values(): + for field in feature: + page_id = field.usage >> 16 + value = field.usage & 0xFF + try: + if HUT[page_id][value] == "Inputmode": + assert HUT[field.application] not in ["Touch Screen"] + except KeyError: + pass + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.fields.count("X") == uhdev.touches_in_a_report, + "Device not compatible, we can not trigger the conditions", + ) + def test_mt_tx_cx(self): + """send a single touch in the first slot of the device, with + different values of Tx and Cx. Make sure the kernel reports Tx.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 5, 10) + t0.cx = 50 + t0.cy = 100 + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 5 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TOOL_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 10 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TOOL_Y] == 100 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "In Range" not in uhdev.fields, + "Device not compatible, missing In Range usage", + ) + def test_mt_inrange(self): + """Send one contact that has the InRange bit set before/after + tipswitch. + Kernel is supposed to mark the contact with a distance > 0 + when inrange is set but not tipswitch. + + This tests the hovering capability of devices (MT_QUIRK_HOVERING). + + Make sure the contact is only released from the kernel POV + when the inrange bit is set to 0.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 150, 200) + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TRACKING_ID, 0) in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_DISTANCE) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_DISTANCE] > 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + t0.tipswitch = True + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_DISTANCE, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_DISTANCE] == 0 + + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_DISTANCE) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_DISTANCE] > 0 + + t0.inrange = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_mt_duplicates(self): + """Test the MT_QUIRK_IGNORE_DUPLICATES from the kernel. + If a touch is reported more than once with the same Contact ID, + we should only handle the first touch. + + Note: this is not in MS spec, but the current kernel behaves + like that""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 5, 10) + t1 = Touch(1, 15, 20) + t2 = Touch(2, 50, 100) + + r = uhdev.event([t0, t1, t2], contact_count=2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TRACKING_ID, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 5 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 10 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + + def test_mt_release_miss(self): + """send a single touch in the first slot of the device, and + forget to release it. The kernel is supposed to release by itself + the touch in 100ms. + Make sure that we are dealing with a new touch by resending the + same touch after the timeout expired, and check that the kernel + considers it as a separate touch (different tracking ID)""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 5, 10) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + + time.sleep(0.2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Azimuth" not in uhdev.fields, + "Device not compatible, missing Azimuth usage", + ) + def test_mt_azimuth(self): + """Check for the azimtuh information bit. + When azimuth is presented by the device, it should be exported + as ABS_MT_ORIENTATION and the exported value should report a quarter + of circle.""" + uhdev = self.uhdev + + t0 = Touch(1, 5, 10) + t0.azimuth = 270 + + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + # orientation is clockwise, while Azimuth is counter clockwise + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_ORIENTATION, 90) in events + + class TestPTP(TestWin8Multitouch): + def test_ptp_buttons(self): + """check for button reliability. + There are 2 types of touchpads: the click pads and the pressure pads. + Each should reliably report the BTN_LEFT events. + """ + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + if uhdev.type == "clickpad": + r = uhdev.event(click=True) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(click=False) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + else: + r = uhdev.event(left=True) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + r = uhdev.event(left=False) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 + + r = uhdev.event(right=True) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 + + r = uhdev.event(right=False) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) in events + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Confidence" not in uhdev.fields, + "Device not compatible, missing Confidence usage", + ) + def test_ptp_confidence(self): + """Check for the validity of the confidence bit. + When a contact is marked as not confident, it should be detected + as a palm from the kernel POV and released. + + Note: if the kernel exports ABS_MT_TOOL_TYPE, it shouldn't release + the touch but instead convert it to ABS_MT_TOOL_PALM.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 150, 200) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + t0.confidence = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + if evdev.absinfo[libevdev.EV_ABS.ABS_MT_TOOL_TYPE] is not None: + # the kernel exports MT_TOOL_PALM + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TOOL_TYPE, 2) in events + ) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] != -1 + + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + @pytest.mark.skip_if_uhdev( + lambda uhdev: uhdev.touches_in_a_report >= uhdev.max_contacts, + "Device not compatible, we can not trigger the conditions", + ) + def test_ptp_non_touch_data(self): + """Some single finger hybrid touchpads might not provide the + button information in subsequent reports (only in the first report). + + Emulate this and make sure we do not release the buttons in the + middle of the event.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + touches = [Touch(i, i * 10, i * 10 + 5) for i in range(uhdev.max_contacts)] + contact_count = uhdev.max_contacts + incr_scantime = True + btn_state = True + events = None + while touches: + t = touches[: uhdev.touches_in_a_report] + touches = touches[uhdev.touches_in_a_report :] + r = uhdev.event( + t, + click=btn_state, + left=btn_state, + contact_count=contact_count, + incr_scantime=incr_scantime, + ) + contact_count = 0 + incr_scantime = False + btn_state = False + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + if touches: + assert len(events) == 0 + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) in events + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) not in events + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 + + +################################################################################ +# +# Windows 7 compatible devices +# +################################################################################ +class Test3m_0596_0500(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test 3m_0596_0500", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 00 00 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 01 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 10 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 8 1 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 3a 06 81 02 09 31 46 e8 03 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 85 12 09 55 95 01 75 08 15 00 25 0a b1 02 06 00 ff 15 00 26 ff 00 85 03 09 01 75 08 95 07 b1 02 85 04 09 01 75 08 95 17 b1 02 85 05 09 01 75 08 95 47 b1 02 85 06 09 01 75 08 95 07 b1 02 85 07 09 01 75 08 95 07 b1 02 85 08 09 01 75 08 95 07 b1 02 85 09 09 01 75 08 95 3f b1 02 c0", + input_info=(BusType.USB, 0x0596, 0x0500), + max_contacts=60, + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID", "TOUCH_SIZE_SCALING"), + ) + + +class Test3m_0596_0506(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test 3m_0596_0506", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 00 00 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 03 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 13 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 d6 0a 81 02 09 31 46 22 06 81 02 05 0d 75 10 95 01 09 48 81 02 09 49 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 3c 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 02 81 03 05 0d 85 12 09 55 95 01 75 08 15 00 25 3c b1 02 06 00 ff 15 00 26 ff 00 85 03 09 01 75 08 95 07 b1 02 85 04 0 9 01 75 08 95 17 b1 02 85 05 09 01 75 08 95 47 b1 02 85 06 09 01 75 08 95 07 b1 02 85 73 09 01 75 08 95 07 b1 02 85 08 09 01 75 08 95 07 b1 02 85 09 09 01 75 08 95 3f b1 02 85 0f 09 01 75 08 96 07 02 b1 02 c0", + input_info=(BusType.USB, 0x0596, 0x0506), + max_contacts=60, + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID", "TOUCH_SIZE_SCALING"), + ) + + +class TestActionStar_2101_1011(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ActionStar_2101_1011", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4d 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4d 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x2101, 0x1011), + ) + + def test_mt_actionstar_inrange(self): + """Special sequence that might not be handled properly""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + # fmt: off + sequence = [ + # t0 = Touch(1, 6999, 2441) | t1 = Touch(2, 15227, 2026) + '01 ff 01 57 1b 89 09 ff 02 7b 3b ea 07 02', + # t0.xy = (6996, 2450) | t1.y = 2028 + '01 ff 01 54 1b 92 09 ff 02 7b 3b ec 07 02', + # t1.xy = (15233, 2040) | t0.tipswitch = False + '01 ff 02 81 3b f8 07 fe 01 54 1b 92 09 02', + # t1 | t0.inrange = False + '01 ff 02 81 3b f8 07 fc 01 54 1b 92 09 02', + ] + # fmt: on + + for num, r_str in enumerate(sequence): + r = [int(i, 16) for i in r_str.split()] + uhdev.call_input_event(r) + events = uhdev.next_sync_events() + self.debug_reports([r], uhdev) + for e in events: + print(e) + if num == 2: + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + +class TestAsus_computers_0486_0185(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test asus-computers_0486_0185", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 95 01 75 01 81 02 09 32 81 02 09 47 81 02 75 05 81 03 09 30 26 ff 00 75 08 81 02 09 51 25 02 81 02 26 96 0d 05 01 75 10 55 0d 65 33 09 30 35 00 46 fd 1d 81 02 09 31 46 60 11 81 02 c0 09 22 a1 02 05 0d 35 00 45 00 55 00 65 00 09 42 25 01 75 01 81 02 09 32 81 02 09 47 81 02 75 05 81 03 09 30 26 ff 00 75 08 81 02 09 51 25 02 81 02 26 96 0d 05 01 75 10 55 0d 65 33 09 30 46 fd 1d 81 02 09 31 46 60 11 81 02 c0 35 00 45 00 55 00 65 00 05 0d 09 54 75 08 25 02 81 02 85 08 09 55 b1 02 c0 09 0e a1 01 85 07 09 22 a1 00 09 52 25 0a b1 02 c0 05 0c 09 01 a1 01 85 06 09 01 26 ff 00 95 08 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 02 25 01 75 01 95 02 81 02 95 06 81 03 26 96 0d 05 01 75 10 95 01 55 0d 65 33 09 30 46 fd 1d 81 02 09 31 46 60 11 81 02 c0 c0 06 ff 01 09 01 a1 01 26 ff 00 35 00 45 00 55 00 65 00 85 05 75 08 95 3f 09 00 81 02 c0", + input_info=(BusType.USB, 0x0486, 0x0185), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID_MINUS_ONE"), + ) + + +class TestAtmel_03eb_201c(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_201c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x03EB, 0x201C), + ) + + +class TestAtmel_03eb_211c(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_211c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 37 81 02 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 46 56 0a 26 ff 0f 09 30 81 02 46 b2 05 26 ff 0f 09 31 81 02 05 0d 75 08 85 02 09 55 25 10 b1 02 c0 c0", + input_info=(BusType.USB, 0x03EB, 0x211C), + ) + + +class TestCando_2087_0a02(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cando_2087_0a02", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 0e 65 33 09 30 35 00 46 6d 03 81 02 46 ec 01 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 0e 65 33 09 30 35 00 46 6d 03 81 02 46 ec 01 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 02 81 02 85 02 09 55 b1 02 c0 06 00 ff 09 01 a1 01 85 a6 95 22 75 08 26 ff 00 15 00 09 01 81 02 85 a5 95 06 75 08 26 ff 00 15 00 09 01 91 02 c0", + input_info=(BusType.USB, 0x2087, 0x0A02), + ) + + +class TestCando_2087_0b03(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cando_2087_0b03", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 49 46 f2 03 81 02 09 31 26 ff 29 46 39 02 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 49 46 f2 03 81 02 09 31 26 ff 29 46 39 02 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x2087, 0x0B03), + ) + + +class TestCVTouch_1ff7_0013(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cvtouch_1ff7_0013", + rdesc="06 00 ff 09 00 a1 01 85 fd 06 00 ff 09 01 09 02 09 03 09 04 09 05 09 06 15 00 26 ff 00 75 08 95 06 81 02 85 fe 06 00 ff 09 01 09 02 09 03 09 04 15 00 26 ff 00 75 08 95 04 b1 02 c0 05 01 09 02 a1 01 09 01 a1 00 85 01 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 ff 7f 75 10 95 02 81 02 05 0d 09 33 15 00 26 ff 00 35 00 46 ff 00 75 08 95 01 81 02 05 01 09 38 15 81 25 7f 35 81 45 7f 95 01 81 06 c0 c0 06 00 ff 09 00 a1 01 85 fc 15 00 26 ff 00 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 06 00 ff 09 00 a1 01 85 fb 15 00 26 ff 00 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 85 03 09 55 15 00 25 02 b1 02 c0 09 0e a1 01 85 04 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x1FF7, 0x0013), + quirks=("NOT_SEEN_MEANS_UP",), + ) + + +class TestCvtouch_1ff7_0017(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cvtouch_1ff7_0017", + rdesc="06 00 ff 09 00 a1 01 85 fd 06 00 ff 09 01 09 02 09 03 09 04 09 05 09 06 15 00 26 ff 00 75 08 95 06 81 02 85 fe 06 00 ff 09 01 09 02 09 03 09 04 15 00 26 ff 00 75 08 95 04 b1 02 c0 05 01 09 02 a1 01 09 01 a1 00 85 01 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 03 05 01 09 30 09 31 15 00 26 ff 0f 35 00 46 ff 0f 75 10 95 02 81 02 09 00 15 00 25 ff 35 00 45 ff 75 08 95 01 81 02 09 38 15 81 25 7f 95 01 81 06 c0 c0 06 00 ff 09 00 a1 01 85 fc 15 00 25 ff 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 06 00 ff 09 00 a1 01 85 fb 15 00 25 ff 19 01 29 3f 75 08 95 3f 81 02 19 01 29 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 09 31 81 02 c0 05 0d 09 54 95 01 75 08 81 02 85 03 09 55 25 02 b1 02 c0 09 0e a1 01 85 04 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x1FF7, 0x0017), + ) + + +class TestCypress_04b4_c001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test cypress_04b4_c001", + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 04 a1 01 85 02 09 22 09 53 95 01 75 08 81 02 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 15 00 25 20 09 48 81 02 09 49 81 02 05 01 15 00 26 d0 07 75 10 55 00 65 00 09 30 15 00 26 d0 07 35 00 45 00 81 02 09 31 45 00 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 09 55 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x04B4, 0xC001), + ) + + +class TestData_modul_7374_1232(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test data-modul_7374_1232", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 37 81 02 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 46 d0 07 26 ff 0f 09 30 81 02 46 40 06 09 31 81 02 05 0d 75 08 85 02 09 55 25 10 b1 02 c0 c0", + input_info=(BusType.USB, 0x7374, 0x1232), + ) + + +class TestData_modul_7374_1252(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test data-modul_7374_1252", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 37 81 02 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 46 d0 07 26 ff 0f 09 30 81 02 46 40 06 09 31 81 02 05 0d 75 08 85 02 09 55 25 10 b1 02 c0 c0", + input_info=(BusType.USB, 0x7374, 0x1252), + ) + + +class TestE4_2219_044c(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test e4_2219_044c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 09 55 b1 02 c0 09 0e a1 01 85 02 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 05 01 09 38 15 81 25 7f 75 08 95 01 81 06 c0 c0", + input_info=(BusType.USB, 0x2219, 0x044C), + ) + + +class TestEgalax_capacitive_0eef_7224(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7224", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7224), + quirks=("SLOT_IS_CONTACTID", "ALWAYS_VALID"), + ) + + +class TestEgalax_capacitive_0eef_72fa(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_72fa", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 72 22 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 87 13 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 72 22 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 87 13 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x72FA), + quirks=("SLOT_IS_CONTACTID", "VALID_IS_INRANGE"), + ) + + +class TestEgalax_capacitive_0eef_7336(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7336", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 c1 20 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c2 18 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 c1 20 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c2 18 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7336), + ) + + +class TestEgalax_capacitive_0eef_7337(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7337", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 ae 17 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c3 0e 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 ae 17 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c3 0e 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7337), + ) + + +class TestEgalax_capacitive_0eef_7349(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_7349", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x7349), + quirks=("SLOT_IS_CONTACTID", "ALWAYS_VALID"), + ) + + +class TestEgalax_capacitive_0eef_73f4(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_73f4", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 96 4e 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 23 2c 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 96 4e 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 23 2c 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0x73F4), + ) + + +class TestEgalax_capacitive_0eef_a001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test egalax-capacitive_0eef_a001", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 23 28 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 11 19 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x0EEF, 0xA001), + quirks=("SLOT_IS_CONTACTID", "VALID_IS_INRANGE"), + ) + + +class TestElo_touchsystems_04e7_0022(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test elo-touchsystems_04e7_0022", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 0e 65 33 09 30 35 00 46 ff 0f 81 02 46 ff 0f 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 0f 75 10 55 00 65 00 09 30 35 00 46 ff 0f 81 02 46 ff 0f 09 31 81 02 c0 05 0d 09 54 25 10 95 01 75 08 81 02 85 08 09 55 25 02 b1 02 c0 09 0e a1 01 85 07 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 06 00 ff 09 55 85 80 15 00 26 ff 00 75 08 95 01 b1 82 c0 05 01 09 02 a1 01 85 54 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 15 00 26 ff 0f 75 10 95 01 81 02 09 31 75 10 95 01 81 02 09 3b 16 00 00 26 00 01 36 00 00 46 00 01 66 00 00 75 10 95 01 81 62 c0 c0", + input_info=(BusType.USB, 0x04E7, 0x0022), + ) + + +class TestElo_touchsystems_04e7_0080(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test elo-touchsystems_04e7_0080", + rdesc="05 0d 09 04 a1 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 7c 24 75 10 95 01 09 30 81 02 09 31 46 96 14 81 02 c0 05 0d 09 54 75 08 95 01 15 00 25 08 81 02 09 55 b1 02 c0", + input_info=(BusType.USB, 0x04E7, 0x0080), + ) + + +class TestFlatfrog_25b5_0002(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test flatfrog_25b5_0002", + rdesc="05 0d 09 04 a1 01 85 05 09 22 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 0 9 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 9 5 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 05 0d 09 22 65 00 55 00 a1 02 05 0d 15 00 25 01 75 01 95 01 09 42 81 02 09 32 81 02 95 06 81 03 75 08 95 01 25 7f 09 51 81 02 05 01 65 11 55 0e 75 10 35 00 26 a6 2b 46 48 1b 09 30 81 02 26 90 18 46 59 0f 09 31 81 02 05 0d 65 11 55 0f 75 08 25 7f 45 7f 09 48 81 02 09 49 81 02 65 00 55 00 75 10 26 00 04 46 00 04 09 30 81 02 c0 65 00 55 00 05 0d 55 0c 66 01 10 75 20 95 01 27 ff ff ff 7f 45 00 09 56 81 02 75 08 95 01 15 00 25 28 09 54 81 02 09 55 85 06 25 28 b1 02 c0 65 00 55 00 45 00 09 0e a1 01 85 03 09 23 a1 02 09 52 15 02 25 02 75 08 95 01 b1 02 09 53 15 00 25 0a 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x25B5, 0x0002), + quirks=("NOT_SEEN_MEANS_UP", "NO_AREA"), + max_contacts=40, + ) + + +class TestFocaltech_10c4_81b9(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test focaltech_10c4_81b9", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 04 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 26 58 02 09 31 46 00 00 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 85 02 09 55 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x10C4, 0x81B9), + quirks=("ALWAYS_VALID",), + max_contacts=5, + ) + + +class TestHanvon_20b3_0a18(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test hanvon_20b3_0a18", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 26 ff 4b 46 70 03 81 02 09 31 26 ff 2b 46 f1 01 81 02 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 02 09 55 25 02 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x20B3, 0x0A18), + ) + + +class TestHuitoo_03f7_0003(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test huitoo_03f7_0003", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 00 65 00 35 00 46 ff 0f 09 30 26 ff 0f 81 02 09 31 26 ff 0f 81 02 05 0d 09 48 26 ff 0f 81 02 09 49 26 ff 0f 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 09 55 b1 02 c0 09 0e a1 01 85 02 09 23 a1 02 09 52 09 53 15 00 25 10 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 15 00 26 ff 0f 35 00 46 ff 0f 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 3f 09 02 81 02 95 3f 09 02 91 02 c0", + input_info=(BusType.USB, 0x03F7, 0x0003), + ) + + +class TestIdeacom_1cb6_6650(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ideacom_1cb6_6650", + rdesc="05 0d 09 04 a1 01 85 0a 09 22 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 26 ff 1f 75 10 95 01 55 0d 65 33 09 31 35 00 46 61 13 81 02 09 30 46 73 22 81 02 05 0d 75 08 95 01 09 30 26 ff 00 81 02 09 51 81 02 85 0c 09 55 25 02 95 01 b1 02 c0 06 00 ff 85 02 09 01 75 08 95 07 b1 02 85 03 09 02 75 08 95 07 b1 02 85 04 09 03 75 08 95 07 b1 02 85 05 09 04 75 08 95 07 b1 02 85 06 09 05 75 08 96 27 00 b1 02 85 07 09 06 75 08 96 27 00 b1 02 85 08 09 07 75 08 95 07 b1 02 85 09 09 08 75 08 95 07 b1 02 85 0b 09 09 75 08 96 07 00 b1 02 85 0d 09 0a 75 08 96 27 00 b1 02 c0 09 0e a1 01 85 0e 09 52 09 53 95 07 b1 02 c0 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 75 06 95 01 81 01 05 01 09 31 09 30 15 00 27 ff 1f 00 00 75 10 95 02 81 02 c0 09 01 a1 02 15 00 26 ff 00 95 02 75 08 81 03 c0 c0", + input_info=(BusType.USB, 0x1CB6, 0x6650), + ) + + +class TestIdeacom_1cb6_6651(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ideacom_1cb6_6651", + rdesc="05 0d 09 04 a1 01 85 0a 09 22 a1 02 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 26 ff 1f 75 10 95 01 55 0d 65 33 09 31 35 00 46 39 13 81 02 09 30 46 24 22 81 02 05 0d 75 08 95 01 09 30 26 ff 00 81 02 09 51 81 02 85 0c 09 55 25 02 95 01 b1 02 c0 06 00 ff 85 02 09 01 75 08 95 07 b1 02 85 03 09 02 75 08 95 07 b1 02 85 04 09 03 75 08 95 07 b1 02 85 05 09 04 75 08 95 07 b1 02 85 06 09 05 75 08 95 1f b1 02 85 07 09 06 75 08 96 1f 00 b1 02 85 08 09 07 75 08 95 07 b1 02 85 09 09 08 75 08 95 07 b1 02 85 0b 09 09 75 08 95 07 b1 02 85 0d 09 0a 75 08 96 1f 00 b1 02 c0 09 0e a1 01 85 0e 09 52 09 53 95 07 b1 02 c0 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 75 06 95 01 81 01 05 01 09 31 09 30 15 00 27 ff 1f 00 00 75 10 95 02 81 02 c0 09 01 a1 02 15 00 26 ff 00 95 02 75 08 81 03 c0 c0", + input_info=(BusType.USB, 0x1CB6, 0x6651), + ) + + +class TestIkaist_2793_0001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test ikaist_2793_0001", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 00 00 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 03 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 13 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 0 9 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 33 09 30 35 00 46 51 07 81 02 09 31 46 96 04 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 3c 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 02 81 03 05 0d 85 12 09 55 95 01 75 08 15 00 25 3c b1 02 06 00 ff 15 00 26 ff 00 85 1e 09 01 75 08 95 80 b1 02 85 1f 09 01 75 08 96 3f 01 b1 02 c0", + input_info=(BusType.USB, 0x2793, 0x0001), + ) + + +class TestIrmtouch_23c9_5666(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test irmtouch_23c9_5666", + rdesc="05 0d 09 04 a1 01 85 0a 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 15 00 26 ff 7f 75 10 09 30 81 02 09 31 81 02 05 0d 09 48 09 49 95 02 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 0c 09 23 a1 02 09 52 15 00 25 06 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x23C9, 0x5666), + ) + + +class TestIrtouch_6615_0070(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test irtouch_6615_0070", + rdesc="05 01 09 02 a1 01 85 10 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 30 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 05 0d 09 54 15 00 26 02 00 75 08 95 01 81 02 85 03 09 55 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 05 0d 09 02 a1 01 85 20 09 20 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 85 01 06 00 ff 09 01 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x6615, 0x0070), + ) + + +class TestIrtouch_6615_0081(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test irtouch_6615_0081", + rdesc="05 0d 09 04 a1 01 85 30 09 22 09 00 15 00 26 ff 00 75 08 95 05 81 02 a1 00 05 0d 09 51 15 00 26 ff 00 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0e 65 13 35 00 46 b5 04 75 10 95 01 81 02 09 31 35 00 46 8a 03 81 02 09 32 35 00 46 8a 03 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 a1 00 05 0d 09 51 15 00 26 ff 00 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0e 65 13 35 00 46 b5 04 75 10 95 01 81 02 09 31 35 00 46 8a 03 81 02 09 32 35 00 46 8a 03 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 a1 00 05 0d 09 51 15 00 26 ff 00 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0e 65 13 35 00 46 b5 04 75 10 95 01 81 02 09 31 35 00 46 8a 03 81 02 09 32 35 00 46 8a 03 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 09 00 15 00 26 ff 7f 75 10 95 01 81 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 a1 00 05 0d 09 54 15 00 25 1f 75 05 95 01 81 02 09 00 15 00 25 07 75 03 95 01 81 02 09 00 15 00 26 ff 00 75 08 95 01 81 02 c0 09 55 85 03 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 06 00 ff 09 00 a1 01 09 02 a1 00 85 aa 09 06 15 00 26 ff 00 35 00 46 ff 00 75 08 95 3f b1 02 c0 c0 05 01 09 02 a1 01 85 10 09 01 a1 00 05 01 09 00 15 00 26 ff 00 75 08 95 05 81 02 09 30 09 31 09 32 15 00 26 ff 7f 75 10 95 03 81 02 05 09 19 01 29 08 15 00 25 01 95 08 75 01 81 02 09 00 15 00 26 ff 00 75 08 95 02 81 02 c0 c0 06 00 ff 09 00 a1 01 85 40 09 00 15 00 26 ff 00 75 08 95 2e 81 02 c0", + input_info=(BusType.USB, 0x6615, 0x0081), + ) + + +class TestLG_043e_9aa1(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lg_043e_9aa1", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 09 31 46 78 0a 26 38 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 0 0 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 25 0a 09 55 b1 02 c0 09 0e a1 01 85 03 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 04 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 75 10 95 01 15 00 26 7f 07 81 02 09 31 26 37 04 81 02 c0 c0 06 00 ff 09 01 a1 01 85 05 15 00 26 ff 00 75 08 95 19 09 01 b1 02 c0 05 14 09 2b a1 02 85 07 09 2b 15 00 25 0a 75 08 95 40 b1 02 09 4b 15 00 25 0a 75 08 95 02 91 02 c0 05 14 09 2c a1 02 85 08 09 2b 15 00 25 0a 75 08 95 05 81 02 09 4b 15 00 25 0a 75 08 95 47 91 02 c0", + input_info=(BusType.USB, 0x043E, 0x9AA1), + ) + + +class TestLG_043e_9aa3(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lg_043e_9aa3", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 09 31 46 78 0a 26 38 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 0 0 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 25 0a 09 55 b1 02 c0 09 0e a1 01 85 03 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 04 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 75 10 95 01 15 00 26 7f 07 81 02 09 31 26 37 04 81 02 c0 c0 06 00 ff 09 01 a1 01 85 05 15 00 26 ff 00 75 08 95 19 09 01 b1 02 c0 05 14 09 2b a1 02 85 07 09 2b 15 00 25 0a 75 08 95 40 b1 02 09 4b 15 00 25 0a 75 08 95 02 91 02 c0 05 14 09 2c a1 02 85 08 09 2b 15 00 25 0a 75 08 95 05 81 02 09 4b 15 00 25 0a 75 08 95 47 91 02 c0", + input_info=(BusType.USB, 0x043E, 0x9AA3), + ) + + +class TestLG_1fd2_0064(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lg_1fd2_0064", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 a1 00 05 01 26 80 07 75 10 55 0e 65 33 09 30 35 00 46 53 07 81 02 26 38 04 46 20 04 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 a1 00 05 01 26 80 07 75 10 55 0e 65 33 09 30 35 00 46 53 07 81 02 26 38 04 46 20 04 09 31 81 02 45 00 c0 c0 05 0d 09 54 95 01 75 08 81 02 85 08 09 55 95 01 25 02 b1 02 c0 09 0e a1 01 85 07 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 75 10 95 02 15 00 26 ff 7f 81 02 c0 c0", + input_info=(BusType.USB, 0x1FD2, 0x0064), + ) + + +class TestLumio_202e_0006(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lumio_202e_0006", + rdesc="05 0d 09 04 a1 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 46 b0 0e 75 10 95 01 09 30 81 02 09 31 46 c2 0b 81 02 c0 05 0d 09 54 75 08 95 01 15 00 25 08 81 02 09 55 b1 02 c0", + input_info=(BusType.USB, 0x202E, 0x0006), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID_MINUS_ONE"), + ) + + +class TestLumio_202e_0007(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test lumio_202e_0007", + rdesc="05 0d 09 04 a1 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 09 47 95 02 81 02 95 0a 81 03 05 01 26 ff 7f 65 11 55 0e 46 ba 0e 75 10 95 01 09 30 81 02 09 31 46 ea 0b 81 02 05 0d 09 51 75 10 95 01 81 02 09 55 15 00 25 08 75 08 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x202E, 0x0007), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID_MINUS_ONE"), + ) + + +class TestNexio_1870_0100(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test nexio_1870_0100", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 40 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x0100), + ) + + +class TestNexio_1870_010d(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test nexio_1870_010d", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 9 5 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x010D), + ) + + +class TestNexio_1870_0119(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test nexio_1870_0119", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 9 5 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x0119), + ) + + +class TestPenmount_14e1_3500(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test penmount_14e1_3500", + rdesc="05 0d 09 04 a1 01 09 22 a1 00 09 51 15 00 25 0f 75 04 95 01 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 81 01 05 01 75 10 95 01 09 30 26 ff 07 81 02 09 31 26 ff 07 81 02 05 0d 09 55 75 08 95 05 b1 02 c0 c0", + input_info=(BusType.USB, 0x14E1, 0x3500), + quirks=("VALID_IS_CONFIDENCE",), + max_contacts=10, + ) + + +class TestPixart_093a_8002(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test pixart_093a_8002", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 5a 14 26 ff 7f 09 30 81 22 46 72 0b 26 ff 7f 09 31 81 22 95 08 75 08 81 03 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 5a 14 26 ff 7f 81 02 09 31 46 72 0b 26 ff 7f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 5a 14 26 ff 7f 81 02 46 72 0b 26 ff 7f 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x093A, 0x8002), + quirks=("VALID_IS_INRANGE", "SLOT_IS_CONTACTNUMBER"), + ) + + +class TestPqlabs_1ef1_0001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test pqlabs_1ef1_0001", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 3f 81 02 c0 c0 05 8c 09 07 a1 01 85 11 09 02 15 00 26 ff 00 75 08 95 3f 81 02 85 10 09 10 91 02 c0", + input_info=(BusType.USB, 0x1EF1, 0x0001), + ) + + +class TestQuanta_0408_3000(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3000", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 e3 13 26 7f 07 81 02 09 31 46 2f 0b 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 e3 13 26 7f 07 81 02 46 2f 0b 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 06 00 ff 09 01 26 ff 00 75 08 95 2f 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 c0", + input_info=(BusType.USB, 0x0408, 0x3000), + ) + + +class TestQuanta_0408_3001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3001", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 09 31 46 78 0a 26 38 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 80 07 81 02 46 78 0a 26 38 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 0f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 c0", + input_info=(BusType.USB, 0x0408, 0x3001), + quirks=("VALID_IS_CONFIDENCE", "SLOT_IS_CONTACTID"), + ) + + +class TestQuanta_0408_3008_1(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3008_1", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 4c 11 26 7f 07 09 30 81 22 46 bb 09 26 37 04 09 31 81 22 95 08 75 08 81 03 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 4c 11 26 7f 07 81 02 09 31 46 bb 09 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 4c 11 26 7f 07 81 02 46 bb 09 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x0408, 0x3008), + ) + + +class TestQuanta_0408_3008(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test quanta_0408_3008", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 98 12 26 7f 07 09 30 81 22 46 78 0a 26 37 04 09 31 81 22 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 09 31 46 78 0a 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 46 78 0a 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x0408, 0x3008), + ) + + +class TestRafi_05bd_0107(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test rafi_05bd_0107", + rdesc="05 0d 09 04 a1 01 85 01 09 22 65 00 55 00 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 03 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 25 09 95 01 81 02 05 01 46 9c 01 26 ff 03 35 00 75 10 09 30 81 02 46 e7 00 26 ff 0 3 09 31 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 09 81 02 05 0d 85 02 95 01 75 08 09 55 25 0a b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 05 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 65 11 55 0f 09 30 26 ff 03 35 00 46 9c 01 75 10 95 01 81 02 09 31 26 ff 03 35 00 46 e7 00 81 02 c0 c0", + input_info=(BusType.USB, 0x05BD, 0x0107), + ) + + +class TestRndplus_2512_5003(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test rndplus_2512_5003", + rdesc="05 0d 09 04 a1 01 85 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 85 08 09 55 b1 02 c0 09 0e a1 01 85 07 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 16 00 00 26 ff 3f 36 00 00 46 ff 3f 66 00 00 75 10 95 02 81 62 c0 c0", + input_info=(BusType.USB, 0x2512, 0x5003), + ) + + +class TestRndplus_2512_5004(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test rndplus_2512_5004", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 00 65 00 09 30 35 00 46 00 00 81 02 09 31 46 00 00 81 02 05 0d 09 48 09 49 75 10 95 02 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 08 81 02 85 05 09 55 b1 02 c0 09 0e a1 01 85 06 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 16 00 00 26 ff 3f 36 00 00 46 ff 3f 66 00 00 75 10 95 02 81 62 c0 c0 06 00 ff 09 01 a1 01 85 01 09 01 15 00 26 ff 00 75 08 95 3f 82 00 01 85 02 09 01 15 00 26 ff 00 75 08 95 3f 92 00 01 c0", + input_info=(BusType.USB, 0x2512, 0x5004), + ) + + +class TestSitronix_1403_5001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test sitronix_1403_5001", + rdesc="05 0d 09 04 a1 01 85 01 09 54 95 01 75 08 81 02 09 22 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 05 01 26 90 04 75 0c 95 01 55 0f 65 11 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 a4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 02 09 48 09 49 81 02 c0 a1 02 09 51 75 06 95 01 81 02 09 42 09 32 15 00 25 01 75 01 95 02 81 02 b4 09 30 46 e1 00 81 02 26 50 03 09 31 45 7d 81 02 05 0d 75 08 95 04 09 48 09 49 81 02 c0 85 02 09 55 26 ff 00 75 08 95 01 b1 02 09 04 15 00 25 ff 75 08 95 07 91 02 c0 09 0e a1 01 85 03 09 23 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + input_info=(BusType.USB, 0x1403, 0x5001), + max_contacts=10, + ) + + +class TestSmart_0b8c_0092(BaseTest.TestMultitouch): + def create_device(self): + return SmartTechDigitizer( + "uhid test smart_0b8c_0092", input_info=(BusType.USB, 0x0B8C, 0x0092) + ) + + +class TestStantum_1f87_0002(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test stantum_1f87_0002", + rdesc="05 0d 09 04 a1 01 85 03 05 0d 09 54 95 01 75 08 81 02 06 00 ff 75 02 09 01 81 01 75 0e 09 02 81 02 05 0d 09 22 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 0 1 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 a1 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 30 81 02 05 0d 25 1f 75 05 09 48 81 02 05 01 16 00 00 26 ff 07 75 0b 55 00 65 00 09 31 81 02 05 0d 25 1f 75 05 09 49 81 02 75 08 09 51 95 01 81 02 09 30 75 05 81 02 09 42 15 00 25 01 75 01 95 01 81 02 09 47 81 02 09 32 81 02 c0 85 08 05 0d 09 55 95 01 75 08 25 0a b1 02 c0", + input_info=(BusType.USB, 0x1F87, 0x0002), + ) + + +class TestTopseed_1784_0016(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test topseed_1784_0016", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 04 75 10 55 00 65 00 09 30 35 00 46 ff 04 81 02 09 31 46 ff 04 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 0a 81 02 09 55 b1 02 c0 05 0c 09 01 a1 01 85 03 a1 02 09 b5 15 00 25 01 75 01 95 01 81 02 09 b6 81 02 09 b7 81 02 09 cd 81 02 09 e2 81 02 09 e9 81 02 09 ea 81 02 05 01 09 82 81 02 c0 c0", + input_info=(BusType.USB, 0x1784, 0x0016), + max_contacts=2, + ) + + +class TestTpv_25aa_8883(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test tpv_25aa_8883", + rdesc="05 01 09 02 a1 01 85 0d 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 05 0d 09 32 95 01 75 01 81 02 95 01 75 05 81 03 05 01 55 0e 65 11 75 10 95 01 35 00 46 98 12 26 7f 07 09 30 81 22 46 78 0a 26 37 04 09 31 81 22 35 00 45 00 15 81 25 7f 75 08 95 01 09 38 81 06 09 00 75 08 95 07 81 03 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 09 31 46 78 0a 26 37 04 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 75 10 55 0e 65 11 09 30 35 00 46 98 12 26 7f 07 81 02 46 78 0a 26 37 04 09 31 81 02 c0 05 0d 09 54 15 00 26 ff 00 95 01 75 08 81 02 09 55 25 02 95 01 85 02 b1 02 c0 05 0d 09 0e a1 01 06 00 ff 09 01 26 ff 00 75 08 95 47 85 03 b1 02 09 01 96 ff 03 85 04 b1 02 09 01 95 0b 85 05 b1 02 09 01 96 ff 03 85 06 b1 02 09 01 95 0f 85 07 b1 02 09 01 96 ff 03 85 08 b1 02 09 01 96 ff 03 85 09 b1 02 09 01 95 3f 85 0a b1 02 09 01 96 ff 03 85 0b b1 02 09 01 96 c3 03 85 0e b1 02 09 01 96 ff 03 85 0f b1 02 09 01 96 83 03 85 10 b1 02 09 01 96 93 00 85 11 b1 02 09 01 96 ff 03 85 12 b1 02 05 0d 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 85 0c b1 02 c0 c0", + input_info=(BusType.USB, 0x25AA, 0x8883), + ) + + +class TestTrs_star_238f_0001(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test trs-star_238f_0001", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 95 01 81 03 09 37 95 01 81 03 95 01 81 03 15 00 25 0f 75 04 09 51 95 01 81 02 09 54 95 01 81 02 09 55 95 01 81 02 05 01 26 ff 03 15 00 75 10 65 00 09 30 95 01 81 02 09 31 81 02 c0 05 0d 09 0e 85 02 09 23 a1 02 15 00 25 0a 09 52 75 08 95 01 b1 02 09 53 95 01 b1 02 09 55 95 01 b1 02 c0 c0", + input_info=(BusType.USB, 0x238F, 0x0001), + ) + + +class TestUnitec_227d_0103(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test unitec_227d_0103", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 35 00 55 0e 65 33 75 10 95 01 09 30 16 00 00 26 ff 4f 36 00 00 46 6c 03 81 02 09 31 16 00 00 26 ff 3b 36 00 00 46 ed 01 81 02 26 00 00 46 00 00 c0 05 0d 09 54 75 08 95 01 81 02 05 0d 85 03 09 55 25 05 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 53 15 00 25 05 75 08 95 01 b1 02 c0", + input_info=(BusType.USB, 0x227D, 0x0103), + ) + + +class TestZytronic_14c8_0005(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test zytronic_14c8_0005", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 95 01 81 02 95 06 81 01 05 01 26 00 10 75 10 95 01 65 00 09 30 81 02 09 31 46 00 10 81 02 05 0d 09 51 26 ff 00 75 08 95 01 81 02 c0 85 02 09 55 15 00 25 08 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 03 a1 02 09 23 09 52 09 53 15 00 25 08 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 15 00 26 00 10 35 00 46 00 10 65 00 75 10 95 02 81 62 c0 c0 06 00 ff 09 01 a1 01 85 05 09 00 15 00 26 ff 00 75 08 95 3f b1 02 c0 06 00 ff 09 01 a1 01 85 06 09 00 15 00 26 ff 00 75 08 95 3f 81 02 c0", + input_info=(BusType.USB, 0x14C8, 0x0005), + ) + + +class TestZytronic_14c8_0006(BaseTest.TestMultitouch): + def create_device(self): + return Digitizer( + "uhid test zytronic_14c8_0006", + rdesc="05 0d 09 04 a1 01 85 01 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 00 10 75 10 09 30 81 02 09 31 81 02 05 0d c0 05 0d 09 54 95 01 75 08 15 00 25 3c 81 02 05 0d 85 02 09 55 95 01 75 08 15 00 25 3c b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 15 00 26 00 10 35 00 46 00 10 65 00 75 10 95 02 81 62 c0 c0 06 00 ff 09 01 a1 01 85 05 09 00 15 00 2 6 ff 00 75 08 95 3f b1 02 c0 06 00 ff 09 01 a1 01 85 06 09 00 15 00 26 ff 00 75 08 95 3f 81 02 c0", + input_info=(BusType.USB, 0x14C8, 0x0006), + ) + + +################################################################################ +# +# Windows 8 compatible devices +# +################################################################################ + + +class TestMinWin8TSParallelTriple(BaseTest.TestWin8Multitouch): + def create_device(self): + return MinWin8TSParallel(3) + + +class TestMinWin8TSParallel(BaseTest.TestWin8Multitouch): + def create_device(self): + return MinWin8TSParallel(10) + + +class TestMinWin8TSHybrid(BaseTest.TestWin8Multitouch): + def create_device(self): + return MinWin8TSHybrid() + + +class TestWin8TSConfidence(BaseTest.TestWin8Multitouch): + def create_device(self): + return Win8TSConfidence(5) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Confidence" not in uhdev.fields, + "Device not compatible, missing Confidence usage", + ) + def test_mt_confidence_bad_release(self): + """Check for the validity of the confidence bit. + When a contact is marked as not confident, it should be detected + as a palm from the kernel POV and released. + + Note: if the kernel exports ABS_MT_TOOL_TYPE, it shouldn't release + the touch but instead convert it to ABS_MT_TOOL_PALM.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = Touch(1, 150, 200) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + t0.confidence = False + t0.tipswitch = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + if evdev.absinfo[libevdev.EV_ABS.ABS_MT_TOOL_TYPE] is not None: + # the kernel exports MT_TOOL_PALM + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_TOOL_TYPE, 2) in events + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + +class TestElanXPS9360(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test ElanXPS9360", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b 4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 a4 26 20 0d 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 50 07 46 a6 00 09 31 81 02 b4 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class TestTouchpadXPS9360(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test TouchpadXPS9360", + max_contacts=5, + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 03 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c0 04 75 10 55 0e 65 11 09 30 35 00 46 f5 03 95 01 81 02 46 36 02 26 a8 02 09 31 81 02 c0 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 3d 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 3d 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 03 b1 02 85 0e 09 07 15 00 26 ff 00 7 5 08 95 01 b1 02 c0", + ) + + +class TestSurfaceBook2(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test SurfaceBook2", + max_contacts=5, + rdesc="05 01 09 06 A1 01 85 01 14 25 01 75 01 95 08 05 07 19 E0 29 E7 81 02 75 08 95 0A 18 29 91 26 FF 00 80 05 0C 0A C0 02 A1 02 1A C1 02 2A C6 02 95 06 B1 03 C0 05 08 19 01 29 03 75 01 95 03 25 01 91 02 95 05 91 01 C0 05 01 09 02 A1 01 85 02 05 09 19 01 29 05 81 02 95 01 75 03 81 03 15 81 25 7F 75 08 95 02 05 01 09 30 09 31 81 06 A1 02 09 48 14 25 01 35 01 45 10 75 02 95 01 A4 B1 02 09 38 15 81 25 7F 34 44 75 08 81 06 C0 A1 02 09 48 B4 B1 02 34 44 75 04 B1 03 05 0C 0A 38 02 15 81 25 7F 75 08 81 06 C0 C0 05 0C 09 01 A1 01 85 03 75 10 14 26 FF 03 18 2A FF 03 80 C0 06 05 FF 09 01 A1 01 85 0D 25 FF 95 02 75 08 09 20 81 02 09 22 91 02 15 81 25 7F 95 20 75 08 09 21 81 02 09 23 91 02 C0 09 02 A1 01 85 0C 14 25 FF 95 01 08 91 02 C0 05 0D 09 05 A1 01 85 04 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 44 54 64 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 44 54 64 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 44 54 64 C0 05 0D 09 22 A1 02 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 03 09 51 81 02 75 01 95 03 81 03 05 01 26 E4 07 75 10 55 0E 65 11 09 30 46 F2 03 95 01 81 02 46 94 02 26 29 05 09 31 81 02 C0 05 0D 55 0C 66 01 10 47 FF FF 00 00 27 FF FF 00 00 09 56 81 02 09 54 25 7F 75 08 81 02 05 09 09 01 25 01 75 01 81 02 95 07 81 03 05 0D 85 04 09 55 09 59 75 04 95 02 25 0F B1 02 06 00 FF 09 C6 85 05 14 25 08 75 08 95 01 B1 02 0 9 C7 26 FF 00 75 08 95 20 B1 02 C0 05 0D 09 0E A1 01 85 07 09 22 A1 02 09 52 14 25 0A 75 08 95 01 B1 02 C0 09 22 A0 85 08 09 57 09 58 75 01 95 02 25 01 B1 02 95 06 B1 03 C0 C0 06 07 FF 09 01 A1 01 85 0A 09 02 26 FF 00 75 08 95 14 91 02 85 09 09 03 91 02 85 0A 09 04 95 26 81 02 85 09 09 05 81 02 85 09 09 06 95 01 B1 02 85 0B 09 07 B1 02 C0 06 05 FF 09 04 A1 01 85 0E 09 31 91 02 09 31 81 03 09 30 91 02 09 30 81 02 95 39 09 32 92 02 01 09 32 82 02 01 C0 06 05 FF 09 50 A1 01 85 20 14 25 FF 75 08 95 3C 09 60 82 02 01 09 61 92 02 01 09 62 B2 02 01 85 21 09 63 82 02 01 09 64 92 02 01 09 65 B2 02 01 85 22 25 FF 75 20 95 04 19 66 29 69 81 02 19 6A 29 6D 91 02 19 6E 29 71 B1 02 85 23 19 72 29 75 81 02 19 76 29 79 91 02 19 7A 29 7D B1 02 85 24 19 7E 29 81 81 02 19 82 29 85 91 02 19 86 29 89 B1 02 85 25 19 8A 29 8D 81 02 19 8E 29 91 91 02 19 92 29 95 B1 02 85 26 19 96 29 99 81 02 19 9A 29 9D 91 02 19 9E 29 A1 B1 02 85 27 19 A2 29 A5 81 02 19 A6 29 A9 91 02 19 AA 29 AD B1 02 85 28 19 AE 29 B1 81 02 19 B2 29 B5 91 02 19 B6 29 B9 B1 02 85 29 19 BA 29 BD 81 02 19 BE 29 C1 91 02 19 C2 29 C5 B1 02 C0 06 00 FF 0A 00 F9 A1 01 85 32 75 10 95 02 14 27 FF FF 00 00 0A 01 F9 B1 02 75 20 95 01 25 FF 0A 02 F9 B1 02 75 08 95 08 26 FF 00 0A 03 F9 B2 02 01 95 10 0A 04 F9 B2 02 01 0A 05 F9 B2 02 01 95 01 75 10 27 FF FF 00 00 0A 06 F9 81 02 C0", + ) + + +class Test3m_0596_051c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test 3m_0596_051c", + rdesc="05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 09 01 95 01 75 01 15 00 25 01 81 02 95 07 75 01 81 03 95 01 75 08 81 03 05 01 09 30 09 31 15 00 26 ff 7f 35 00 46 ff 7f 95 02 75 10 81 02 c0 a1 02 15 00 26 ff 00 09 01 95 39 75 08 81 03 c0 c0 05 0d 09 0e a1 01 85 11 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 09 04 a1 01 85 13 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 81 03 09 47 81 02 95 05 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 d1 12 81 02 09 31 46 b2 0b 81 02 06 00 ff 75 10 95 02 09 01 81 02 c0 05 0d 09 54 95 01 75 08 15 00 25 14 81 02 05 0d 55 0c 66 01 10 35 00 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 05 0d 09 55 85 12 15 00 25 14 75 08 95 01 b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 06 00 ff 15 00 26 ff 00 85 03 09 01 75 08 95 07 b1 02 85 04 0 9 01 75 08 95 17 b1 02 85 05 09 01 75 08 95 47 b1 02 85 06 09 01 75 08 95 07 b1 02 85 73 09 01 75 08 95 07 b1 02 85 08 09 01 75 08 95 07 b1 02 85 09 09 01 75 08 95 3f b1 02 85 0f 09 01 75 08 96 07 02 b1 02 c0", + ) + + +class Testadvanced_silicon_04e8_2084(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_04e8_2084", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 c0 14 81 02 46 ae 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 4 4 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_2306(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_2306", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_230a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_230a", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f6 13 81 02 46 40 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_231c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_231c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 e2 13 81 02 46 32 0b 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_2703(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_2703", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 66 17 81 02 46 34 0d 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2149_270b(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2149_270b", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 52 17 81 02 46 20 0d 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 85 f3 09 08 95 3d b1 02 c0", + ) + + +class Testadvanced_silicon_2575_0204(BaseTest.TestWin8Multitouch): + """found on the Dell Canvas 27""" + + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2575_0204", + rdesc="05 0d 09 04 a1 01 85 01 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 51 75 07 95 01 81 02 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 4f 17 81 02 46 1d 0d 09 31 81 02 45 00 c0 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 42 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 c0 05 01 09 0e a1 01 85 05 05 01 09 08 a1 00 09 30 55 0e 65 11 15 0 0 26 ff 7f 35 00 46 4f 17 75 10 95 01 81 42 09 31 46 1d 0d 81 42 06 00 ff 09 01 75 20 81 03 05 01 09 37 55 00 65 14 16 98 fe 26 68 01 36 98 fe 46 68 01 75 0f 81 06 05 09 09 01 65 00 15 00 25 01 35 00 45 00 75 01 81 02 05 0d 09 42 81 02 09 51 75 07 25 7f 81 02 05 0d 09 48 55 0e 65 11 15 00 26 ff 7f 35 00 46 ff 7f 75 10 81 02 09 49 81 02 09 3f 55 00 65 14 15 00 26 67 01 35 00 46 67 01 81 0a c0 65 00 35 00 45 00 05 0d 15 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 05 75 08 09 54 81 02 85 47 09 55 25 05 b1 02 c0 06 00 ff 09 04 a1 01 85 f0 09 01 75 08 95 04 b1 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 85 c0 09 01 95 03 b1 02 85 c2 09 01 95 0f b1 02 85 c4 09 01 95 3e b1 02 85 c5 09 01 95 7e b1 02 85 c6 09 01 95 fe b1 02 85 c8 09 01 96 fe 03 b1 02 85 0a 09 01 95 3f b1 02 c0", + ) + + +class Testadvanced_silicon_2619_5610(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test advanced_silicon_2619_5610", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 02 81 03 09 51 25 1f 75 05 95 01 81 02 a1 00 05 01 26 ff 7f 75 10 55 0e 65 11 09 30 35 00 46 f9 15 81 02 46 73 0c 09 31 81 02 45 00 c0 c0 05 0d 1 5 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 25 0a 75 08 09 54 81 02 85 44 09 55 b1 02 85 44 06 00 ff 09 c5 26 ff 00 96 00 01 b1 02 85 f0 09 01 95 04 81 02 85 f2 09 03 b1 02 09 04 b1 02 09 05 b1 02 95 01 09 06 b1 02 09 07 b1 02 85 f1 09 02 95 07 91 02 c0", + ) + + +class Testatmel_03eb_8409(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_8409", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 00 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 46 18 06 26 77 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0", + ) + + +class Testatmel_03eb_840b(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test atmel_03eb_840b", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 00 0a 26 ff 0f 09 30 81 02 46 a0 05 26 ff 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0", + ) + + +class Testdell_044e_1220(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test dell_044e_1220", + type="pressurepad", + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 09 38 95 01 81 06 05 0c 0a 38 02 81 06 c0 c0 05 0d 09 05 a1 01 85 08 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 af 04 75 10 55 0e 65 11 09 30 35 00 46 e8 03 95 01 81 02 26 7b 02 46 12 02 09 31 81 02 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 05 0d 09 56 81 02 09 54 25 05 95 01 75 08 81 02 05 09 19 01 29 03 25 01 75 01 95 03 81 02 95 05 81 03 05 0d 85 09 09 55 75 08 95 01 25 05 b1 02 06 00 ff 85 0a 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 01 ff 09 01 a1 01 85 03 09 01 15 00 26 ff 00 95 1b 81 02 85 04 09 02 95 50 81 02 85 05 09 03 95 07 b1 02 85 06 09 04 81 02 c0 06 02 ff 09 01 a1 01 85 07 09 02 95 86 75 08 b1 02 c0 05 0d 09 0e a1 01 85 0b 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 0c 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0", + ) + + +class Testdell_06cb_75db(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test dell_06cb_75db", + max_contacts=3, + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 03 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c8 04 75 10 55 0e 65 11 09 30 35 00 46 fb 03 95 01 81 02 46 6c 02 26 e8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c8 04 75 10 55 0e 65 11 09 30 35 00 46 fb 03 95 01 81 02 46 6c 02 26 e8 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 c8 04 75 10 55 0e 65 11 09 30 35 00 46 fb 03 95 01 81 02 46 6c 02 26 e8 02 09 31 81 02 05 0d c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 1a 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 1a 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 01 b1 02 85 0e 09 07 15 00 26 ff 00 75 08 95 01 b1 02 c0", + ) + + +class Testegalax_capacitive_0eef_790a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test egalax_capacitive_0eef_790a", + max_contacts=10, + rdesc="05 0d 09 04 a1 01 85 06 05 0d 09 54 75 08 15 00 25 0c 95 01 81 02 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 15 00 25 20 81 02 05 01 26 ff 0f 75 10 55 0e 65 11 09 30 35 00 46 13 0c 81 02 46 cb 06 09 31 81 02 75 08 95 02 81 03 81 03 c0 05 0d 17 00 00 00 00 27 ff ff ff 7f 75 20 95 01 55 00 65 00 09 56 81 02 09 55 09 53 75 08 95 02 26 ff 00 b1 02 06 00 ff 09 c5 85 07 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 01 a1 01 85 01 09 01 a1 02 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 0e a1 01 85 05 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0", + ) + + +class Testelan_04f3_000a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_000a", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 7 5 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 00 08 46 a6 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_000c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_000c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 7 5 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 40 0e 75 10 55 0f 65 11 09 30 35 00 46 01 01 95 02 81 02 26 00 08 46 91 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_010c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_010c", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 7 5 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c2 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_0125(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_0125", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 7 5 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 f0 0c 75 10 55 0f 65 11 09 30 35 00 46 58 01 95 02 81 02 26 50 07 46 c1 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_016f(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_016f", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 7 5 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0", + ) + + +class Testelan_04f3_0732(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_0732", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 8 1 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0b 75 10 55 0f 65 11 09 30 35 00 46 ff 00 95 02 81 02 26 40 07 46 85 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff 00 00 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 15 00 25 ff 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 1f 09 01 91 02 c0", + ) + + +class Testelan_04f3_200a(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elan_04f3_200a", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 75 06 09 51 25 3f 81 02 26 ff 00 75 08 09 48 81 02 09 49 81 02 95 01 05 01 26 c0 0e 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 02 81 02 26 40 08 46 a6 00 09 31 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff 00 00 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 0a 09 55 25 0a b1 02 85 0e 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0", + ) + + +class Testelan_04f3_300b(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test elan_04f3_300b", + max_contacts=3, + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06 05 0c 0a 38 02 95 01 81 06 75 08 95 03 81 03 c0 06 00 ff 85 0d 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0c 09 c6 96 76 02 75 08 b1 02 85 0b 09 c7 95 42 75 08 b1 02 09 01 85 5d 95 1f 75 08 81 06 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 02 25 02 09 51 81 02 75 01 95 04 81 03 05 01 15 00 26 a7 0c 75 10 55 0e 65 13 09 30 35 00 46 9d 01 95 01 81 02 46 25 01 26 2b 09 26 2b 09 09 31 81 02 05 0d 15 00 25 64 95 03 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 02 09 55 09 59 75 04 95 02 25 0f b1 02 85 07 09 60 75 01 95 01 15 00 25 01 b1 02 95 0f b1 03 06 00 ff 06 00 ff 85 06 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 03 09 22 a1 00 09 52 15 00 25 0a 75 08 95 02 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 15 00 75 01 95 02 25 03 b1 02 95 0e b1 03 c0 c0", + ) + + +class Testelan_04f3_3045(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test elan_04f3_3045", + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06 05 0c 0a 38 02 95 01 81 06 75 08 95 03 81 03 c0 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 75 01 95 02 81 03 95 01 75 04 25 0f 09 51 81 02 05 01 15 00 26 80 0c 75 10 55 0e 65 13 09 30 35 00 46 90 01 95 01 81 02 46 13 01 26 96 08 26 96 08 09 31 81 02 05 0d 15 00 25 64 95 03 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 09 c5 75 08 95 04 81 03 05 0d 85 02 09 55 09 59 75 04 95 02 25 0f b1 02 85 07 09 60 75 01 95 01 15 00 25 01 b1 02 95 0f b1 03 06 00 ff 06 00 ff 85 06 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 85 0d 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0c 09 c6 96 8a 02 75 08 b1 02 85 0b 09 c7 95 80 75 08 b1 02 c0 05 0d 09 0e a1 01 85 03 09 22 a1 00 09 52 15 00 25 0a 75 08 95 02 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 15 00 75 01 95 02 25 03 b1 02 95 0e b1 03 c0 c0", + ) + + +class Testelan_04f3_313a(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test elan_04f3_313a", + type="touchpad", + input_info=(BusType.I2C, 0x04F3, 0x313A), + rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 75 08 95 05 81 03 c0 06 00 ff 09 01 85 0e 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0a 09 c6 15 00 26 ff 00 75 08 95 04 b1 02 c0 06 00 ff 09 01 a1 01 85 5c 09 01 95 0b 75 08 81 06 85 0d 09 c5 15 00 26 ff 00 75 08 95 04 b1 02 85 0c 09 c6 96 80 03 75 08 b1 02 85 0b 09 c7 95 82 75 08 b1 02 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 05 09 09 02 09 03 15 00 25 01 75 01 95 02 81 02 05 0d 95 01 75 04 25 0f 09 51 81 02 05 01 15 00 26 d7 0e 75 10 55 0d 65 11 09 30 35 00 46 44 2f 95 01 81 02 46 12 16 26 eb 06 26 eb 06 09 31 81 02 05 0d 15 00 25 64 95 03 c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 25 01 75 01 95 08 81 03 09 c5 75 08 95 02 81 03 05 0d 85 02 09 55 09 59 75 04 95 02 25 0f b1 02 85 07 09 60 75 01 95 01 15 00 25 01 b1 02 95 0f b1 03 06 00 ff 06 00 ff 85 06 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 03 09 22 a1 00 09 52 15 00 25 0a 75 10 95 01 b1 02 c0 09 22 a1 00 85 05 09 57 09 58 75 01 95 02 25 01 b1 02 95 0e b1 03 c0 c0 05 01 09 02 a1 01 85 2a 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 95 05 81 03 05 01 09 30 09 31 15 81 25 7f 35 81 45 7f 55 00 65 13 75 08 95 02 81 06 75 08 95 05 81 03 c0 c0", + ) + + +class Testelo_04e7_0080(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test elo_04e7_0080", + rdesc="05 0d 09 04 a1 01 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 03 81 03 09 32 95 02 81 02 95 02 81 03 09 51 75 08 95 01 81 02 05 01 26 ff 7f 65 11 55 0e 75 10 09 30 46 7c 24 81 02 09 31 46 96 14 81 02 c0 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 75 08 95 01 15 00 25 08 81 02 09 55 b1 02 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0", + ) + + +class Testilitek_222a_0015(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test ilitek_222a_0015", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 35 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 c2 16 3 5 00 46 b3 08 81 42 09 31 26 c2 0c 46 e4 04 81 42 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 02 09 55 25 0a b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 03 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0", + ) + + +class Testilitek_222a_001c(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test ilitek_222a_001c", + rdesc="05 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 35 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 74 1d 3 5 00 46 70 0d 81 42 09 31 26 74 10 46 8f 07 81 42 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 85 02 09 55 25 0a b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 03 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0", + ) + + +class Testite_06cb_2968(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test ite_06cb_2968", + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 03 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 1b 04 75 10 55 0e 65 11 09 30 35 00 46 6c 03 95 01 81 02 46 db 01 26 3b 02 09 31 81 02 05 0d c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 1a 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 1a 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 01 b1 02 85 0e 09 07 15 00 26 ff 00 75 08 95 01 b1 02 c0", + max_contacts=5, + input_info=(0x3, 0x06CB, 0x2968), + ) + + +class Testn_trig_1b96_0c01(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0c01", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 5 5 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0c03(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0c03", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0f00(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0f00", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_0f04(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_0f04", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testn_trig_1b96_1000(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test n_trig_1b96_1000", + rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0", + ) + + +class Testsharp_04dd_9681(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test sharp_04dd_9681", + rdesc="06 00 ff 09 01 a1 01 75 08 26 ff 00 15 00 85 06 95 3f 09 01 91 02 85 05 95 3f 09 01 81 02 c0 05 0d 09 04 a1 01 85 81 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 95 01 81 02 05 01 65 11 55 0f 35 00 46 b0 01 26 80 07 75 10 09 30 81 02 46 f3 00 26 38 04 09 31 81 02 05 0d 09 48 09 49 26 ff 00 95 02 75 08 81 02 c0 05 0d 09 56 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 81 02 09 54 95 01 75 08 15 00 25 0a 81 02 85 84 09 55 b1 02 85 87 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 09 0e a1 01 85 83 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 80 05 09 19 01 29 01 15 00 25 01 95 01 75 01 81 02 95 01 75 07 81 01 05 01 65 11 55 0f 09 30 26 80 07 35 00 46 66 00 75 10 95 01 81 02 09 31 26 38 04 35 00 46 4d 00 81 02 c0 c0", + ) + + +class Testsipodev_0603_0002(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test sipodev_0603_0002", + type="clickpad", + rdesc="05 01 09 02 a1 01 85 03 09 01 a1 00 05 09 19 01 29 02 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 80 25 7f 75 08 95 02 81 06 c0 c0 05 0d 09 05 a1 01 85 04 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 75 01 95 02 81 03 95 01 75 04 25 05 09 51 81 02 05 01 15 00 26 44 0a 75 0c 55 0e 65 11 09 30 35 00 46 ac 03 95 01 81 02 46 fe 01 26 34 05 75 0c 09 31 81 02 05 0d c0 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 0a 95 01 75 04 81 02 75 01 95 03 81 03 05 09 09 01 25 01 75 01 95 01 81 02 05 0d 85 0a 09 55 09 59 75 04 95 02 25 0f b1 02 85 0b 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 09 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 06 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 07 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 05 01 09 0c a1 01 85 08 15 00 25 01 09 c6 75 01 95 01 81 06 75 07 81 03 c0 05 01 09 80 a1 01 85 01 15 00 25 01 75 01 0a 81 00 0a 82 00 0a 83 00 95 03 81 06 95 05 81 01 c0 06 0c 00 09 01 a1 01 85 02 25 01 15 00 75 01 0a b5 00 0a b6 00 0a b7 00 0a cd 00 0a e2 00 0a a2 00 0a e9 00 0a ea 00 95 08 81 02 0a 83 01 0a 6f 00 0a 70 00 0a 88 01 0a 8a 01 0a 92 01 0a a8 02 0a 24 02 95 08 81 02 0a 21 02 0a 23 02 0a 96 01 0a 25 02 0a 26 02 0a 27 02 0a 23 02 0a b1 02 95 08 81 02 c0 06 00 ff 09 01 a1 01 85 05 15 00 26 ff 00 19 01 29 02 75 08 95 05 b1 02 c0", + ) + + +class Testsynaptics_06cb_1d10(BaseTest.TestWin8Multitouch): + def create_device(self): + return Digitizer( + "uhid test synaptics_06cb_1d10", + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 75 08 95 02 15 81 25 7f 35 81 45 7f 55 0e 65 11 81 06 c0 c0 05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 75 08 09 51 15 01 26 ff 00 95 01 81 42 05 01 15 00 26 3c 0c 75 10 55 0e 65 11 09 30 35 12 46 2a 0c 81 02 09 31 15 00 26 f1 06 35 12 46 df 06 81 02 c0 05 0d 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 95 01 75 08 15 00 25 0f 81 02 85 08 09 55 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 85 09 09 02 15 00 26 ff 00 75 08 95 3f 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 05 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 3d 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 01 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 01 b1 02 c0", + ) + + +class Testsynaptics_06cb_ce08(BaseTest.TestPTP): + def create_device(self): + return PTP( + "uhid test synaptics_06cb_ce08", + max_contacts=5, + physical="Vendor Usage 1", + input_info=(BusType.I2C, 0x06CB, 0xCE08), + rdesc="05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 05 01 09 02 a1 01 85 18 09 01 a1 00 05 09 19 01 29 03 46 00 00 15 00 25 01 75 01 95 03 81 02 95 05 81 01 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0 06 00 ff 09 02 a1 01 85 20 09 01 a1 00 09 03 15 00 26 ff 00 35 00 46 ff 00 75 08 95 05 81 02 c0 c0 05 0d 09 05 a1 01 85 03 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 09 22 a1 02 15 00 25 01 09 47 09 42 95 02 75 01 81 02 95 01 75 03 25 05 09 51 81 02 75 01 95 03 81 03 05 01 15 00 26 f8 04 75 10 55 0e 65 11 09 30 35 00 46 24 04 95 01 81 02 46 30 02 26 a0 02 09 31 81 02 c0 05 0d 55 0c 66 01 10 47 ff ff 00 00 27 ff ff 00 00 75 10 95 01 09 56 81 02 09 54 25 7f 95 01 75 08 81 02 05 09 09 01 25 01 75 01 95 01 81 02 95 07 81 03 05 0d 85 08 09 55 09 59 75 04 95 02 25 0f b1 02 85 0d 09 60 75 01 95 01 15 00 25 01 b1 02 95 07 b1 03 85 07 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 0e a1 01 85 04 09 22 a1 02 09 52 15 00 25 0a 75 08 95 01 b1 02 c0 09 22 a1 00 85 06 09 57 09 58 75 01 95 02 25 01 b1 02 95 06 b1 03 c0 c0 06 00 ff 09 01 a 1 01 85 09 09 02 15 00 26 ff 00 75 08 95 14 91 02 85 0a 09 03 15 00 26 ff 00 75 08 95 14 91 02 85 0b 09 04 15 00 26 ff 00 75 08 95 45 81 02 85 0c 09 05 15 00 26 ff 00 75 08 95 45 81 02 85 0f 09 06 15 00 26 ff 00 75 08 95 03 b1 02 85 0e 09 07 15 00 26 ff 00 75 08 95 01 b1 02 c0", + ) diff --git a/tools/testing/selftests/hid/tests/test_tablet.py b/tools/testing/selftests/hid/tests/test_tablet.py new file mode 100644 index 000000000000..303ffff9ee8d --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_tablet.py @@ -0,0 +1,872 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021 Benjamin Tissoires +# Copyright (c) 2021 Red Hat, Inc. +# + +from . import base +import copy +from enum import Enum +from hidtools.util import BusType +import libevdev +import logging +import pytest +from typing import Dict, Tuple + +logger = logging.getLogger("hidtools.test.tablet") + + +class PenState(Enum): + """Pen states according to Microsoft reference: + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + + PEN_IS_OUT_OF_RANGE = (False, None) + PEN_IS_IN_RANGE = (False, libevdev.EV_KEY.BTN_TOOL_PEN) + PEN_IS_IN_CONTACT = (True, libevdev.EV_KEY.BTN_TOOL_PEN) + PEN_IS_IN_RANGE_WITH_ERASING_INTENT = (False, libevdev.EV_KEY.BTN_TOOL_RUBBER) + PEN_IS_ERASING = (True, libevdev.EV_KEY.BTN_TOOL_RUBBER) + + def __init__(self, touch, tool): + self.touch = touch + self.tool = tool + + @classmethod + def from_evdev(cls, evdev) -> "PenState": + touch = bool(evdev.value[libevdev.EV_KEY.BTN_TOUCH]) + tool = None + if ( + evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] + and not evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN] + ): + tool = libevdev.EV_KEY.BTN_TOOL_RUBBER + elif ( + evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN] + and not evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] + ): + tool = libevdev.EV_KEY.BTN_TOOL_PEN + elif ( + evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN] + or evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] + ): + raise ValueError("2 tools are not allowed") + + return cls((touch, tool)) + + def apply(self, events) -> "PenState": + if libevdev.EV_SYN.SYN_REPORT in events: + raise ValueError("EV_SYN is in the event sequence") + touch = self.touch + touch_found = False + tool = self.tool + tool_found = False + + for ev in events: + if ev == libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH): + if touch_found: + raise ValueError(f"duplicated BTN_TOUCH in {events}") + touch_found = True + touch = bool(ev.value) + elif ev in ( + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_RUBBER), + ): + if tool_found: + raise ValueError(f"duplicated BTN_TOOL_* in {events}") + tool_found = True + if ev.value: + tool = ev.code + else: + tool = None + + new_state = PenState((touch, tool)) + assert ( + new_state in self.valid_transitions() + ), f"moving from {self} to {new_state} is forbidden" + + return new_state + + def valid_transitions(self) -> Tuple["PenState", ...]: + """Following the state machine in the URL above, with a couple of addition + for skipping the in-range state, due to historical reasons. + + Note that those transitions are from the evdev point of view, not HID""" + if self == PenState.PEN_IS_OUT_OF_RANGE: + return ( + PenState.PEN_IS_OUT_OF_RANGE, + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + ) + + if self == PenState.PEN_IS_IN_RANGE: + return ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + PenState.PEN_IS_IN_CONTACT, + ) + + if self == PenState.PEN_IS_IN_CONTACT: + return ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ) + + if self == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT: + return ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + PenState.PEN_IS_ERASING, + ) + + if self == PenState.PEN_IS_ERASING: + return ( + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ) + + return tuple() + + +class Data(object): + pass + + +class Pen(object): + def __init__(self, x, y): + self.x = x + self.y = y + self.tipswitch = False + self.tippressure = 15 + self.azimuth = 0 + self.inrange = False + self.width = 10 + self.height = 10 + self.barrelswitch = False + self.invert = False + self.eraser = False + self.x_tilt = 0 + self.y_tilt = 0 + self.twist = 0 + self._old_values = None + self.current_state = None + + def _restore(self): + if self._old_values is not None: + for i in [ + "x", + "y", + "tippressure", + "azimuth", + "width", + "height", + "twist", + "x_tilt", + "y_tilt", + ]: + setattr(self, i, getattr(self._old_values, i)) + + def move_to(self, state): + # fill in the previous values + if self.current_state == PenState.PEN_IS_OUT_OF_RANGE: + self._restore() + + print(f"\n *** pen is moving to {state} ***") + + if state == PenState.PEN_IS_OUT_OF_RANGE: + self._old_values = copy.copy(self) + self.x = 0 + self.y = 0 + self.tipswitch = False + self.tippressure = 0 + self.azimuth = 0 + self.inrange = False + self.width = 0 + self.height = 0 + self.invert = False + self.eraser = False + self.x_tilt = 0 + self.y_tilt = 0 + self.twist = 0 + elif state == PenState.PEN_IS_IN_RANGE: + self.tipswitch = False + self.inrange = True + self.invert = False + self.eraser = False + elif state == PenState.PEN_IS_IN_CONTACT: + self.tipswitch = True + self.inrange = True + self.invert = False + self.eraser = False + elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT: + self.tipswitch = False + self.inrange = True + self.invert = True + self.eraser = False + elif state == PenState.PEN_IS_ERASING: + self.tipswitch = False + self.inrange = True + self.invert = True + self.eraser = True + + self.current_state = state + + def __assert_axis(self, evdev, axis, value): + if ( + axis == libevdev.EV_KEY.BTN_TOOL_RUBBER + and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None + ): + return + + assert ( + evdev.value[axis] == value + ), f"assert evdev.value[{axis}] ({evdev.value[axis]}) != {value}" + + def assert_expected_input_events(self, evdev): + assert evdev.value[libevdev.EV_ABS.ABS_X] == self.x + assert evdev.value[libevdev.EV_ABS.ABS_Y] == self.y + assert self.current_state == PenState.from_evdev(evdev) + + @staticmethod + def legal_transitions() -> Dict[str, Tuple[PenState, ...]]: + """This is the first half of the Windows Pen Implementation state machine: + we don't have Invert nor Erase bits, so just move in/out-of-range or proximity. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "in-range": (PenState.PEN_IS_IN_RANGE,), + "in-range -> out-of-range": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "in-range -> touch": (PenState.PEN_IS_IN_RANGE, PenState.PEN_IS_IN_CONTACT), + "in-range -> touch -> release": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "in-range -> touch -> release -> out-of-range": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + def legal_transitions_with_invert() -> Dict[str, Tuple[PenState, ...]]: + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "hover-erasing": (PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,), + "hover-erasing -> out-of-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "hover-erasing -> erase": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + ), + "hover-erasing -> erase -> release": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "hover-erasing -> erase -> release -> out-of-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "hover-erasing -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_RANGE, + ), + "in-range -> hover-erasing": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + } + + @staticmethod + def tolerated_transitions() -> Dict[str, Tuple[PenState, ...]]: + """This is not adhering to the Windows Pen Implementation state machine + but we should expect the kernel to behave properly, mostly for historical + reasons.""" + return { + "direct-in-contact": (PenState.PEN_IS_IN_CONTACT,), + "direct-in-contact -> out-of-range": ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + def tolerated_transitions_with_invert() -> Dict[str, Tuple[PenState, ...]]: + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "direct-erase": (PenState.PEN_IS_ERASING,), + "direct-erase -> out-of-range": ( + PenState.PEN_IS_ERASING, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + def broken_transitions() -> Dict[str, Tuple[PenState, ...]]: + """Those tests are definitely not part of the Windows specification. + However, a half broken device might export those transitions. + For example, a pen that has the eraser button might wobble between + touching and erasing if the tablet doesn't enforce the Windows + state machine.""" + return { + "in-range -> touch -> erase -> hover-erase": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "in-range -> erase -> hover-erase": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "hover-erase -> erase -> touch -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "hover-erase -> touch -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "touch -> erase -> touch -> erase": ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + ), + } + + +class PenDigitizer(base.UHIDTestDevice): + def __init__( + self, + name, + rdesc_str=None, + rdesc=None, + application="Pen", + physical="Stylus", + input_info=(BusType.USB, 1, 2), + evdev_name_suffix=None, + ): + super().__init__(name, application, rdesc_str, rdesc, input_info) + self.physical = physical + self.cur_application = application + if evdev_name_suffix is not None: + self.name += evdev_name_suffix + + self.fields = [] + for r in self.parsed_rdesc.input_reports.values(): + if r.application_name == self.application: + physicals = [f.physical_name for f in r] + if self.physical not in physicals and None not in physicals: + continue + self.fields = [f.usage_name for f in r] + + def event(self, pen): + rs = [] + r = self.create_report(application=self.cur_application, data=pen) + self.call_input_event(r) + rs.append(r) + return rs + + def get_report(self, req, rnum, rtype): + if rtype != self.UHID_FEATURE_REPORT: + return (1, []) + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return (1, []) + + return (1, []) + + def set_report(self, req, rnum, rtype, data): + if rtype != self.UHID_FEATURE_REPORT: + return 1 + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return 1 + + return 1 + + +class BaseTest: + class TestTablet(base.BaseTestCase.TestUhid): + def create_device(self): + raise Exception("please reimplement me in subclasses") + + def post(self, uhdev, pen): + r = uhdev.event(pen) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + return events + + def validate_transitions(self, from_state, pen, evdev, events): + # check that the final state is correct + pen.assert_expected_input_events(evdev) + + # check that the transitions are valid + sync_events = [] + while libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) in events: + # split the first EV_SYN from the list + idx = events.index(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT)) + sync_events = events[:idx] + events = events[idx + 1 :] + + # now check for a valid transition + from_state = from_state.apply(sync_events) + + if events: + from_state = from_state.apply(sync_events) + + def _test_states(self, state_list, scribble): + """Internal method to test against a list of + transition between states. + state_list is a list of PenState objects + scribble is a boolean which tells if we need + to wobble a little the X,Y coordinates of the pen + between each state transition.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + cur_state = PenState.PEN_IS_OUT_OF_RANGE + + p = Pen(50, 60) + p.move_to(PenState.PEN_IS_OUT_OF_RANGE) + events = self.post(uhdev, p) + self.validate_transitions(cur_state, p, evdev, events) + + cur_state = p.current_state + + for state in state_list: + if scribble and cur_state != PenState.PEN_IS_OUT_OF_RANGE: + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + self.validate_transitions(cur_state, p, evdev, events) + assert len(events) >= 3 # X, Y, SYN + p.move_to(state) + if scribble and state != PenState.PEN_IS_OUT_OF_RANGE: + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + self.validate_transitions(cur_state, p, evdev, events) + cur_state = p.current_state + + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [pytest.param(v, id=k) for k, v in Pen.legal_transitions().items()], + ) + def test_valid_pen_states(self, state_list, scribble): + """This is the first half of the Windows Pen Implementation state machine: + we don't have Invert nor Erase bits, so just move in/out-of-range or proximity. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + self._test_states(state_list, scribble) + + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [pytest.param(v, id=k) for k, v in Pen.tolerated_transitions().items()], + ) + def test_tolerated_pen_states(self, state_list, scribble): + """This is not adhering to the Windows Pen Implementation state machine + but we should expect the kernel to behave properly, mostly for historical + reasons.""" + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Invert" not in uhdev.fields, + "Device not compatible, missing Invert usage", + ) + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [ + pytest.param(v, id=k) + for k, v in Pen.legal_transitions_with_invert().items() + ], + ) + def test_valid_invert_pen_states(self, state_list, scribble): + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Invert" not in uhdev.fields, + "Device not compatible, missing Invert usage", + ) + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [ + pytest.param(v, id=k) + for k, v in Pen.tolerated_transitions_with_invert().items() + ], + ) + def test_tolerated_invert_pen_states(self, state_list, scribble): + """This is the second half of the Windows Pen Implementation state machine: + we now have Invert and Erase bits, so move in/out or proximity with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Invert" not in uhdev.fields, + "Device not compatible, missing Invert usage", + ) + @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) + @pytest.mark.parametrize( + "state_list", + [pytest.param(v, id=k) for k, v in Pen.broken_transitions().items()], + ) + def test_tolerated_broken_pen_states(self, state_list, scribble): + """Those tests are definitely not part of the Windows specification. + However, a half broken device might export those transitions. + For example, a pen that has the eraser button might wobble between + touching and erasing if the tablet doesn't enforce the Windows + state machine.""" + self._test_states(state_list, scribble) + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Barrel Switch" not in uhdev.fields, + "Device not compatible, missing Barrel Switch usage", + ) + def test_primary_button(self): + """Primary button (stylus) pressed, reports as pressed even while hovering. + Actual reporting from the device: hid=TIPSWITCH,BARRELSWITCH,INRANGE (code=TOUCH,STYLUS,PEN): + { 0, 0, 1 } <- hover + { 0, 1, 1 } <- primary button pressed + { 0, 1, 1 } <- liftoff + { 0, 0, 0 } <- leaves + """ + + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + p = Pen(50, 60) + p.inrange = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1) in events + assert evdev.value[libevdev.EV_ABS.ABS_X] == 50 + assert evdev.value[libevdev.EV_ABS.ABS_Y] == 60 + assert not evdev.value[libevdev.EV_KEY.BTN_STYLUS] + + p.barrelswitch = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1) in events + + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + assert len(events) == 3 # X, Y, SYN + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 51) in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 59) in events + + p.barrelswitch = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0) in events + + p.inrange = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0) in events + + @pytest.mark.skip_if_uhdev( + lambda uhdev: "Barrel Switch" not in uhdev.fields, + "Device not compatible, missing Barrel Switch usage", + ) + def test_contact_primary_button(self): + """Primary button (stylus) pressed, reports as pressed even while hovering. + Actual reporting from the device: hid=TIPSWITCH,BARRELSWITCH,INRANGE (code=TOUCH,STYLUS,PEN): + { 0, 0, 1 } <- hover + { 0, 1, 1 } <- primary button pressed + { 1, 1, 1 } <- touch-down + { 1, 1, 1 } <- still touch, scribble on the screen + { 0, 1, 1 } <- liftoff + { 0, 0, 0 } <- leaves + """ + + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + p = Pen(50, 60) + p.inrange = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1) in events + assert evdev.value[libevdev.EV_ABS.ABS_X] == 50 + assert evdev.value[libevdev.EV_ABS.ABS_Y] == 60 + assert not evdev.value[libevdev.EV_KEY.BTN_STYLUS] + + p.barrelswitch = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1) in events + + p.tipswitch = True + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_STYLUS] + + p.x += 1 + p.y -= 1 + events = self.post(uhdev, p) + assert len(events) == 3 # X, Y, SYN + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 51) in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 59) in events + + p.tipswitch = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + + p.barrelswitch = False + p.inrange = False + events = self.post(uhdev, p) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0) in events + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0) in events + + +class GXTP_pen(PenDigitizer): + def event(self, pen): + if not hasattr(self, "prev_tip_state"): + self.prev_tip_state = False + + internal_pen = copy.copy(pen) + + # bug in the controller: when the pen touches the + # surface, in-range stays to 1, but when + # the pen moves in-range gets reverted to 0 + if pen.tipswitch and self.prev_tip_state: + internal_pen.inrange = False + + self.prev_tip_state = pen.tipswitch + + # another bug in the controller: when the pen is + # inverted, invert is set to 1, but as soon as + # the pen touches the surface, eraser is correctly + # set to 1 but invert is released + if pen.eraser: + internal_pen.invert = False + + return super().event(internal_pen) + + +class USIPen(PenDigitizer): + pass + + +################################################################################ +# +# Windows 7 compatible devices +# +################################################################################ +# class TestEgalax_capacitive_0eef_7224(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7224', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7224), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_72fa(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_72fa', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 72 22 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 87 13 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 72 22 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 87 13 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x72fa), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_7336(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7336', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 c1 20 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c2 18 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 c1 20 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c2 18 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7336), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_7337(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7337', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 ae 17 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c3 0e 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 ae 17 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c3 0e 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7337), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_7349(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_7349', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x7349), +# evdev_name_suffix=' Touchscreen') +# +# +# class TestEgalax_capacitive_0eef_73f4(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test egalax-capacitive_0eef_73f4', +# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 96 4e 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 23 2c 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 96 4e 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 23 2c 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0', +# input_info=(BusType.USB, 0x0eef, 0x73f4), +# evdev_name_suffix=' Touchscreen') +# +# bogus: BTN_TOOL_PEN is not emitted +# class TestIrtouch_6615_0070(BaseTest.TestTablet): +# def create_device(self): +# return PenDigitizer('uhid test irtouch_6615_0070', +# rdesc='05 01 09 02 a1 01 85 10 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 30 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 05 0d 09 54 15 00 26 02 00 75 08 95 01 81 02 85 03 09 55 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 05 0d 09 02 a1 01 85 20 09 20 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 85 01 06 00 ff 09 01 75 08 95 01 b1 02 c0 c0', +# input_info=(BusType.USB, 0x6615, 0x0070)) + + +class TestNexio_1870_0100(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test nexio_1870_0100", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 40 81 00 19 01 29 40 91 00 c0", + input_info=(BusType.USB, 0x1870, 0x0100), + ) + + +class TestNexio_1870_010d(BaseTest.TestTablet): + def create_device(self): + return PenDigitizer( + "uhid test nexio_1870_010d", + rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 9 5 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 9 5 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 0 1 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 5 5 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", + ) + + From patchwork Fri Feb 17 16:18:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58701 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981131wrn; Fri, 17 Feb 2023 08:28:41 -0800 (PST) X-Google-Smtp-Source: AK7set8bmSzODMSscdVM8CMqG2VwIhXthL2boZAczOd8gGNWmlqK6aEUYxb/GY8U9Dl0vTPDTQRH X-Received: by 2002:a17:906:4914:b0:88f:9445:f8df with SMTP id b20-20020a170906491400b0088f9445f8dfmr1395171ejq.21.1676651321304; Fri, 17 Feb 2023 08:28:41 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651321; cv=none; d=google.com; s=arc-20160816; b=aaSpJcwp0PNc79CJEWzM6T7BVTebXpobOjWt5pDujmgwkbZr0dMBi5nHu3E+ZneOq9 u3M9QkpXAB5tBUb7PG/1Vw96qXcr7NwaJkf8dpYIa7kxsJWDf4hPvcbBi2RgoMBb+fle mzmm/BL6xCxUOfP5phk4zxRU2O5WN0V4qk2V1cFDyrhk9YqWc/ZFvAzLuhXCWer6+b9i CU/F3xjSpgoKk8G8kV0Tu4avcGMWO5KGBYHMgBlepu0xJ/ZZ8jIHOobmRbZH5kKSYE8U f3O1DJfvFShFGwk1TVWkDsIu7CkLWRGChGi0D0BFt50JW63IDnRra3Rzoqb1tnozh5AP RZtA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=hHHBVj1YcIn0KCl3Qd3GIK4smjO76vu9fFe/d1GedaA=; b=jSB1/8xslbchlhO1vqmDNzEb5kSY4XtV5bDNqo7KuMBfKqHtH/VAk1lj4DkdMtE8v/ v2/szVt7upvUHzJN65Z+XJs4YBJ9kzLqn0nA7nizvMpkA/kGEc4DG5ht7YDGLz9gOmff v8dAMpggV1AVK7xZx9ufnuBBcbB7GqLIHuyzT3k/JIeKSicofb/dJjFPiEAXxCaaYtOD 9GLQp1fTxDzcDjLod3ypcxZgS5XroCaJUdaICiHhrWomkf4YlST9s67PRpgqEnAdC+Ne YMCYb4l0MTn70dDg7MQ/p9ISI/kVmAl/+Sz25zFNiP/dedfm47hw+eRvRSkAoDGlPbPk qMzQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=PUjbEtSz; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id wi8-20020a170906fd4800b008b17aa7b33bsi3142941ejb.668.2023.02.17.08.28.17; Fri, 17 Feb 2023 08:28:41 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=PUjbEtSz; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229839AbjBQQUt (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:49 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53122 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230352AbjBQQUX (ORCPT ); Fri, 17 Feb 2023 11:20:23 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B95AA718E9 for ; Fri, 17 Feb 2023 08:19:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650740; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hHHBVj1YcIn0KCl3Qd3GIK4smjO76vu9fFe/d1GedaA=; b=PUjbEtSzz3YCn45bOw0TH5msghPGQlpHXCzDqAKLw51uJtkOSdSkTbWkFzSDQEVBuFMr0c yPqH8gdbDvAKKSCBIMQy3P8SmD8tka+jL9UokPYbantg8TJnThSL1Yx9rBzzZGRgBzyyB2 0mATJ+WuMN5A4NY1ki4tQX6KU6JihPc= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-248-MZo6XbBbPi6zY7T_YRp5aw-1; Fri, 17 Feb 2023 11:18:57 -0500 X-MC-Unique: MZo6XbBbPi6zY7T_YRp5aw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id DC3063C21C25; Fri, 17 Feb 2023 16:18:56 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 07AD8C15BA0; Fri, 17 Feb 2023 16:18:54 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:18:01 +0100 Subject: [PATCH 07/11] selftests: hid: import hid-tools wacom tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-7-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Jason Gerecke X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=126277; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=mxLx8nClhVhwvjwcL3TwU8nIm85CP+kOrIySZMR2Mus=; b=xS50VwTYuHZjG3aOkPh0KC3bWeHbKFAv5BNrTa17HMlA3OyDkhKVooWMs0OKfHtSvPjSM0PIg V7CsrrA2SABAN9ljxMaZ7bXnzgLSiv486oZ6LW7Am5xIpG2pN4FJ7dC X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096335643270386?= X-GMAIL-MSGID: =?utf-8?q?1758096335643270386?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Jason Gerecke Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 3 + tools/testing/selftests/hid/hid-wacom.sh | 7 + .../selftests/hid/tests/descriptors_wacom.py | 1360 ++++++++++++++++++++ .../selftests/hid/tests/test_wacom_generic.py | 844 ++++++++++++ 5 files changed, 2215 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index d8ad104c3863..4f11e865bbb3 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -11,6 +11,7 @@ TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh TEST_PROGS += hid-tablet.sh +TEST_PROGS += hid-wacom.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index 96ae99efcbaa..266fbd84ae9c 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -19,4 +19,7 @@ CONFIG_HIDRAW=y CONFIG_HID=y CONFIG_INPUT_EVDEV=y CONFIG_UHID=y +CONFIG_USB=y +CONFIG_USB_HID=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-wacom.sh b/tools/testing/selftests/hid/hid-wacom.sh new file mode 100755 index 000000000000..1630c22726d2 --- /dev/null +++ b/tools/testing/selftests/hid/hid-wacom.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_wacom_generic.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/descriptors_wacom.py b/tools/testing/selftests/hid/tests/descriptors_wacom.py new file mode 100644 index 000000000000..91c16e005c12 --- /dev/null +++ b/tools/testing/selftests/hid/tests/descriptors_wacom.py @@ -0,0 +1,1360 @@ +# SPDX-License-Identifier: GPL-2.0 + +# fmt: off +wacom_pth660_v145 = [ + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x02, # . Usage (Mouse), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x01, # . Report ID (1), + 0x09, 0x01, # . Usage (Pointer), + 0xA1, 0x00, # . Collection (Physical), + 0x05, 0x09, # . Usage Page (Button), + 0x19, 0x01, # . Usage Minimum (01h), + 0x29, 0x03, # . Usage Maximum (03h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x03, # . Report Count (3), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x05, # . Report Count (5), + 0x81, 0x03, # . Input (Constant, Variable), + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x30, # . Usage (X), + 0x09, 0x31, # . Usage (Y), + 0x15, 0x81, # . Logical Minimum (-127), + 0x25, 0x7F, # . Logical Maximum (127), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x06, # . Input (Variable, Relative), + 0xC0, # . End Collection, + 0xC0, # . End Collection, + 0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh), + 0x09, 0x01, # . Usage (01h), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (20h), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (42h), + 0x09, 0x44, # . Usage (44h), + 0x09, 0x5A, # . Usage (5Ah), + 0x09, 0x45, # . Usage (45h), + 0x09, 0x3C, # . Usage (3Ch), + 0x09, 0x32, # . Usage (32h), + 0x09, 0x36, # . Usage (36h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (0130h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x35, 0x00, # . Physical Minimum (0), + 0x47, 0x80, 0x57, 0x00, 0x00, # . Physical Maximum (22400), + 0x15, 0x00, # . Logical Minimum (0), + 0x27, 0x00, 0xAF, 0x00, 0x00, # . Logical Maximum (44800), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (0131h), + 0x47, 0xD0, 0x39, 0x00, 0x00, # . Physical Maximum (14800), + 0x27, 0xA0, 0x73, 0x00, 0x00, # . Logical Maximum (29600), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (30h), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x1F, # . Logical Maximum (8191), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x3D, # . Usage (3Dh), + 0x09, 0x3E, # . Usage (3Eh), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0xC0, # . Physical Minimum (-64), + 0x45, 0x3F, # . Physical Maximum (63), + 0x15, 0xC0, # . Logical Minimum (-64), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x41, # . Usage (41h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x36, 0x4C, 0xFF, # . Physical Minimum (-180), + 0x46, 0xB3, 0x00, # . Physical Maximum (179), + 0x16, 0x7C, 0xFC, # . Logical Minimum (-900), + 0x26, 0x83, 0x03, # . Logical Maximum (899), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x03, 0x0D, # . Usage (0D03h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x07, # . Logical Maximum (2047), # !!! Errata: Missing Physical Min/Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x32, 0x01, # . Usage (0132h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (5Bh), + 0x09, 0x5C, # . Usage (5Ch), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (77h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x85, 0x11, # . Report ID (17), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x1A, 0x10, 0x09, # . Usage Minimum (0910h), + 0x2A, 0x17, 0x09, # . Usage Maximum (0917h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x08, # . Report Count (8), + 0x81, 0x02, # . Input (Variable), + 0x1A, 0x40, 0x09, # . Usage Minimum (0940h), + 0x2A, 0x47, 0x09, # . Usage Maximum (0947h), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x95, 0x09, # . Usage (0995h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x38, 0x01, # . Usage (0138h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0x67, 0x01, # . Physical Maximum (359), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x47, # . Logical Maximum (71), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x39, 0x01, # . Usage (0139h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x25, 0x01, # . Logical Maximum (1), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x04, # . Report Count (4), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x85, 0x13, # . Report ID (19), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x0A, 0x3B, 0x04, # . Usage (043Bh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x64, # . Logical Maximum (100), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x04, 0x04, # . Usage (0404h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x52, 0x04, # . Usage (0452h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x54, 0x04, # . Usage (0454h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x85, 0x02, # . Report ID (2), + 0x09, 0x01, # . Usage (01h), + 0x75, 0x08, # . Report Size (8), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x03, # . Report ID (3), + 0x0A, 0x03, 0x10, # . Usage (1003h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x04, # . Report ID (4), + 0x0A, 0x04, 0x10, # . Usage (1004h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x07, # . Report ID (7), + 0x0A, 0x09, 0x10, # . Usage (1009h), + 0x25, 0x02, # . Logical Maximum (2), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x0A, 0x07, 0x10, # . Usage (1007h), + 0x09, 0x00, # . Usage (00h), + 0x0A, 0x08, 0x10, # . Usage (1008h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x06, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0C, # . Report ID (12), + 0x0A, 0x30, 0x0D, # . Usage (0D30h), + 0x0A, 0x31, 0x0D, # . Usage (0D31h), + 0x0A, 0x32, 0x0D, # . Usage (0D32h), + 0x0A, 0x33, 0x0D, # . Usage (0D33h), # !!! Errata: Missing Non-zero Physical Max + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0D, # . Report ID (13), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x0A, 0x0D, 0x10, # . Usage (100Dh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x14, # . Report ID (20), + 0x0A, 0x14, 0x10, # . Usage (1014h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x31, # . Report ID (49), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x05, # . Report Count (5), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x32, # . Report ID (50), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x0A, 0x32, 0x10, # . Usage (1032h), + 0x25, 0x03, # . Logical Maximum (3), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x34, # . Report ID (52), + 0x0A, 0x34, 0x10, # . Usage (1034h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x35, # . Report ID (53), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0A, # . Report Count (10), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x36, # . Report ID (54), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x96, 0x01, 0x01, # . Report Count (257), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCC, # . Report ID (204), + 0x0A, 0xCC, 0x10, # . Usage (10CCh), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x0A, 0xAC, 0x10, # . Usage (10ACh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x75, 0x08, # . Report Size (8), + 0x85, 0xAC, # . Report ID (172), + 0x09, 0x00, # . Usage (00h), + 0x95, 0xBF, # . Report Count (191), + 0x81, 0x02, # . Input (Variable), + 0x85, 0x33, # . Report ID (51), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x12, # . Report Count (18), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x64, # . Report ID (100), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0C, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x15, # . Report ID (21), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x12, # . Report ID (18), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x16, # . Report ID (22), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x40, # . Report ID (64), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x41, # . Report ID (65), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x42, # . Report ID (66), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x43, # . Report ID (67), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x44, # . Report ID (68), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x45, # . Report ID (69), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x20, # . Report Count (32), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x60, # . Report ID (96), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x61, # . Report ID (97), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x62, # . Report ID (98), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x85, 0xD0, # . Report ID (208), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD1, # . Report ID (209), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD2, # . Report ID (210), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD3, # . Report ID (211), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD4, # . Report ID (212), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD5, # . Report ID (213), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD6, # . Report ID (214), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD7, # . Report ID (215), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD8, # . Report ID (216), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x0C, 0x00, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD9, # . Report ID (217), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x00, 0x0A, # . Report Count (2560), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDA, # . Report ID (218), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x04, # . Report Count (1028), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDB, # . Report ID (219), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x06, 0x00, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDC, # . Report ID (220), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDD, # . Report ID (221), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDE, # . Report ID (222), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDF, # . Report ID (223), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x22, 0x00, # . Report Count (34), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE0, # . Report ID (224), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x01, 0x00, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE1, # . Report ID (225), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE2, # . Report ID (226), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE3, # . Report ID (227), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE4, # . Report ID (228), + 0x09, 0x01, # . Usage (01h), + 0x96, 0xFF, 0x01, # . Report Count (511), + 0xB1, 0x02, # . Feature (Variable), + 0xC0 # . End Collection +] +# fmt: on + +# Report ID (20), Usage (1014h), Report Count (13) -> 15 +wacom_pth660_v150 = wacom_pth660_v145.copy() +wacom_pth660_v150[0x2CB] = 0x0F + +# fmt: off +wacom_pth860_v145 = [ + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x02, # . Usage (Mouse), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x01, # . Report ID (1), + 0x09, 0x01, # . Usage (Pointer), + 0xA1, 0x00, # . Collection (Physical), + 0x05, 0x09, # . Usage Page (Button), + 0x19, 0x01, # . Usage Minimum (01h), + 0x29, 0x03, # . Usage Maximum (03h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x03, # . Report Count (3), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x05, # . Report Count (5), + 0x81, 0x03, # . Input (Constant, Variable), + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x30, # . Usage (X), + 0x09, 0x31, # . Usage (Y), + 0x15, 0x80, # . Logical Minimum (-128), + 0x25, 0x7F, # . Logical Maximum (127), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x06, # . Input (Variable, Relative), + 0xC0, # . End Collection, + 0xC0, # . End Collection, + 0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh), + 0x09, 0x01, # . Usage (01h), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (20h), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (42h), + 0x09, 0x44, # . Usage (44h), + 0x09, 0x5A, # . Usage (5Ah), + 0x09, 0x45, # . Usage (45h), + 0x09, 0x3C, # . Usage (3Ch), + 0x09, 0x32, # . Usage (32h), + 0x09, 0x36, # . Usage (36h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (0130h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x35, 0x00, # . Physical Minimum (0), + 0x47, 0x7C, 0x79, 0x00, 0x00, # . Physical Maximum (31100), + 0x15, 0x00, # . Logical Minimum (0), + 0x27, 0xF8, 0xF2, 0x00, 0x00, # . Logical Maximum (62200), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (0131h), + 0x47, 0x60, 0x54, 0x00, 0x00, # . Physical Maximum (21600), + 0x27, 0xC0, 0xA8, 0x00, 0x00, # . Logical Maximum (43200), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (30h), # !!! Errata: Missing Physical Max = 0 + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x1F, # . Logical Maximum (8191), + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x3D, # . Usage (3Dh), + 0x09, 0x3E, # . Usage (3Eh), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0xC0, # . Physical Minimum (-64), + 0x45, 0x3F, # . Physical Maximum (63), + 0x15, 0xC0, # . Logical Minimum (-64), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x41, # . Usage (41h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x36, 0x4C, 0xFF, # . Physical Minimum (-180), + 0x46, 0xB3, 0x00, # . Physical Maximum (179), + 0x16, 0x7C, 0xFC, # . Logical Minimum (-900), + 0x26, 0x83, 0x03, # . Logical Maximum (899), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x03, 0x0D, # . Usage (0D03h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x07, # . Logical Maximum (2047), # !!! Errata: Missing Physical Min/Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x32, 0x01, # . Usage (0132h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (5Bh), + 0x09, 0x5C, # . Usage (5Ch), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (77h), + 0x16, 0x00, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x85, 0x11, # . Report ID (17), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x1A, 0x10, 0x09, # . Usage Minimum (0910h), + 0x2A, 0x17, 0x09, # . Usage Maximum (0917h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x08, # . Report Count (8), + 0x81, 0x02, # . Input (Variable), + 0x1A, 0x40, 0x09, # . Usage Minimum (0940h), + 0x2A, 0x47, 0x09, # . Usage Maximum (0947h), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x95, 0x09, # . Usage (0995h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x38, 0x01, # . Usage (0138h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0x67, 0x01, # . Physical Maximum (359), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x47, # . Logical Maximum (71), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x39, 0x01, # . Usage (0139h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x25, 0x01, # . Logical Maximum (1), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x04, # . Report Count (4), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x85, 0x13, # . Report ID (19), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x0A, 0x3B, 0x04, # . Usage (043Bh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x64, # . Logical Maximum (100), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x04, 0x04, # . Usage (0404h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x52, 0x04, # . Usage (0452h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x54, 0x04, # . Usage (0454h), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x85, 0x02, # . Report ID (2), + 0x09, 0x01, # . Usage (01h), + 0x75, 0x08, # . Report Size (8), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x03, # . Report ID (3), + 0x0A, 0x03, 0x10, # . Usage (1003h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x04, # . Report ID (4), + 0x0A, 0x04, 0x10, # . Usage (1004h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x07, # . Report ID (7), + 0x0A, 0x09, 0x10, # . Usage (1009h), + 0x25, 0x02, # . Logical Maximum (2), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x0A, 0x07, 0x10, # . Usage (1007h), + 0x09, 0x00, # . Usage (00h), + 0x0A, 0x08, 0x10, # . Usage (1008h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x06, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0C, # . Report ID (12), + 0x0A, 0x30, 0x0D, # . Usage (0D30h), + 0x0A, 0x31, 0x0D, # . Usage (0D31h), + 0x0A, 0x32, 0x0D, # . Usage (0D32h), + 0x0A, 0x33, 0x0D, # . Usage (0D33h), # !!! Errata: Missing Non-zero Physical Max + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0D, # . Report ID (13), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x0A, 0x0D, 0x10, # . Usage (100Dh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x14, # . Report ID (20), + 0x0A, 0x14, 0x10, # . Usage (1014h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x31, # . Report ID (49), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x05, # . Report Count (5), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x32, # . Report ID (50), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x0A, 0x32, 0x10, # . Usage (1032h), + 0x25, 0x03, # . Logical Maximum (3), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x34, # . Report ID (52), + 0x0A, 0x34, 0x10, # . Usage (1034h), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x35, # . Report ID (53), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0A, # . Report Count (10), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x36, # . Report ID (54), + 0x0A, 0x35, 0x10, # . Usage (1035h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x96, 0x01, 0x01, # . Report Count (257), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCC, # . Report ID (204), + 0x0A, 0xCC, 0x10, # . Usage (10CCh), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x0A, 0xAC, 0x10, # . Usage (10ACh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x75, 0x08, # . Report Size (8), + 0x85, 0xAC, # . Report ID (172), + 0x09, 0x00, # . Usage (00h), + 0x95, 0xBF, # . Report Count (191), + 0x81, 0x02, # . Input (Variable), + 0x85, 0x33, # . Report ID (51), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x12, # . Report Count (18), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x64, # . Report ID (100), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0C, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x15, # . Report ID (21), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x12, # . Report ID (18), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x16, # . Report ID (22), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x40, # . Report ID (64), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x41, # . Report ID (65), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x42, # . Report ID (66), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x43, # . Report ID (67), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x44, # . Report ID (68), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x45, # . Report ID (69), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x20, # . Report Count (32), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x60, # . Report ID (96), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x61, # . Report ID (97), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x62, # . Report ID (98), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x85, 0xD0, # . Report ID (208), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD1, # . Report ID (209), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD2, # . Report ID (210), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD3, # . Report ID (211), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD4, # . Report ID (212), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD5, # . Report ID (213), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD6, # . Report ID (214), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD7, # . Report ID (215), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD8, # . Report ID (216), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x0C, 0x00, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD9, # . Report ID (217), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x00, 0x0A, # . Report Count (2560), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDA, # . Report ID (218), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x04, # . Report Count (1028), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDB, # . Report ID (219), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x06, 0x00, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDC, # . Report ID (220), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDD, # . Report ID (221), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDE, # . Report ID (222), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDF, # . Report ID (223), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x22, 0x00, # . Report Count (34), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE0, # . Report ID (224), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x01, 0x00, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE1, # . Report ID (225), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE2, # . Report ID (226), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE3, # . Report ID (227), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE4, # . Report ID (228), + 0x09, 0x01, # . Usage (01h), + 0x96, 0xFF, 0x01, # . Report Count (511), + 0xB1, 0x02, # . Feature (Variable), + 0xC0 # . End Collection +] +# fmt: on + +# Report ID (20), Usage (1014h), Report Count (13) -> 15 +wacom_pth860_v150 = wacom_pth860_v145.copy() +wacom_pth860_v150[0x2CA] = 0x0F + +# fmt: off +wacom_pth460_v105 = [ + 0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh), + 0x09, 0x01, # . Usage (01h), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (20h), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (42h), + 0x09, 0x44, # . Usage (44h), + 0x09, 0x5A, # . Usage (5Ah), + 0x09, 0x45, # . Usage (45h), + 0x09, 0x3C, # . Usage (3Ch), + 0x09, 0x32, # . Usage (32h), + 0x09, 0x36, # . Usage (36h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (0130h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x47, 0x58, 0x3E, 0x00, 0x00, # . Physical Maximum (15960), + 0x27, 0xB0, 0x7C, 0x00, 0x00, # . Logical Maximum (31920), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (0131h), + 0x47, 0xF7, 0x26, 0x00, 0x00, # . Physical Maximum (9975), + 0x27, 0xEE, 0x4D, 0x00, 0x00, # . Logical Maximum (19950), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (30h), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x26, 0xFF, 0x1F, # . Logical Maximum (8191), # !!! Errata: Missing Physical Max = 0 + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x3D, # . Usage (3Dh), + 0x09, 0x3E, # . Usage (3Eh), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0xC0, # . Physical Minimum (-64), + 0x45, 0x3F, # . Physical Maximum (63), + 0x15, 0xC0, # . Logical Minimum (-64), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x41, # . Usage (41h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x36, 0x4C, 0xFF, # . Physical Minimum (-180), + 0x46, 0xB3, 0x00, # . Physical Maximum (179), + 0x16, 0x7C, 0xFC, # . Logical Minimum (-900), + 0x26, 0x83, 0x03, # . Logical Maximum (899), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x0A, # . Input (Variable, Wrap), + 0x0A, 0x03, 0x0D, # . Usage (0D03h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x07, # . Logical Maximum (2047), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x32, 0x01, # . Usage (0132h), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (5Bh), + 0x09, 0x5C, # . Usage (5Ch), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (77h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x85, 0x11, # . Report ID (17), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x1A, 0x10, 0x09, # . Usage Minimum (0910h), + 0x2A, 0x15, 0x09, # . Usage Maximum (0915h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x0A, 0x95, 0x09, # . Usage (0995h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x39, # . Usage (39h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x38, 0x01, # . Usage (0138h), + 0x65, 0x14, # . Unit (Degrees), + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0x67, 0x01, # . Physical Maximum (359), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x47, # . Logical Maximum (71), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x4A, # . Input (Variable, Wrap, Null State), + 0x0A, 0x39, 0x01, # . Usage (0139h), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x75, 0x08, # . Report Size (8), + 0x95, 0x04, # . Report Count (4), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x85, 0x13, # . Report ID (19), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x3B, 0x04, # . Usage (043Bh), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x64, # . Logical Maximum (100), + 0x75, 0x07, # . Report Size (7), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x04, 0x04, # . Usage (0404h), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x0A, 0x52, 0x04, # . Usage (0452h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x41, 0x04, # . Usage (0441h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x07, # . Logical Maximum (7), + 0x75, 0x03, # . Report Size (3), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x54, 0x04, # . Usage (0454h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x3C, 0x04, # . Usage (043Ch), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0xFB, # . Logical Minimum (-5), + 0x25, 0x32, # . Logical Maximum (50), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x0A, 0x13, 0x10, # . Usage (1013h), + 0xA1, 0x00, # . Collection (Physical), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x0A, 0x3D, 0x04, # . Usage (043Dh), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0x75, 0x08, # . Report Size (8), + 0x95, 0x03, # . Report Count (3), + 0x81, 0x03, # . Input (Constant, Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x85, 0x02, # . Report ID (2), + 0x0A, 0x02, 0x10, # . Usage (1002h), + 0x15, 0x02, # . Logical Minimum (2), + 0x25, 0x02, # . Logical Maximum (2), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x03, # . Report ID (3), + 0x0A, 0x03, 0x10, # . Usage (1003h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x04, # . Report ID (4), + 0x0A, 0x04, 0x10, # . Usage (1004h), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x07, # . Report ID (7), + 0x0A, 0x09, 0x10, # . Usage (1009h), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x0A, 0x07, 0x10, # . Usage (1007h), + 0x09, 0x00, # . Usage (00h), + 0x0A, 0x08, 0x10, # . Usage (1008h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x09, 0x00, # . Usage (00h), + 0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x06, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x09, 0x00, # . Usage (00h), + 0x25, 0x00, # . Logical Maximum (0), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0x85, 0x0C, # . Report ID (12), + 0x0A, 0x30, 0x0D, # . Usage (0D30h), + 0x0A, 0x31, 0x0D, # . Usage (0D31h), + 0x0A, 0x32, 0x0D, # . Usage (0D32h), + 0x0A, 0x33, 0x0D, # . Usage (0D33h), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x35, 0x00, # . Physical Minimum (0), + 0x46, 0xC8, 0x00, # . Physical Maximum (200), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0x90, 0x01, # . Logical Maximum (400), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x0D, # . Report ID (13), + 0x0A, 0x0D, 0x10, # . Usage (100Dh), + 0x65, 0x00, # . Unit, + 0x55, 0x00, # . Unit Exponent (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x14, # . Report ID (20), + 0x0A, 0x14, 0x10, # . Usage (1014h), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x95, 0x0D, # . Report Count (13), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCC, # . Report ID (204), + 0x0A, 0xCC, 0x10, # . Usage (10CCh), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x09, 0x0E, # . Usage (0Eh), + 0xA1, 0x02, # . Collection (Logical), + 0x85, 0x31, # . Report ID (49), + 0x0A, 0x31, 0x10, # . Usage (1031h), + 0x25, 0x64, # . Logical Maximum (100), + 0x95, 0x03, # . Report Count (3), + 0xB1, 0x02, # . Feature (Variable), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x03, # . Feature (Constant, Variable), + 0xC0, # . End Collection, + 0x0A, 0xAC, 0x10, # . Usage (10ACh), + 0xA1, 0x02, # . Collection (Logical), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x00, # . Logical Maximum (255), + 0x75, 0x08, # . Report Size (8), + 0x85, 0xAC, # . Report ID (172), + 0x09, 0x00, # . Usage (00h), + 0x96, 0xBF, 0x00, # . Report Count (191), + 0x81, 0x02, # . Input (Variable), + 0x85, 0x15, # . Report ID (21), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x33, # . Report ID (51), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x12, # . Report Count (18), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x44, # . Report ID (68), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x45, # . Report ID (69), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x20, # . Report Count (32), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x60, # . Report ID (96), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3F, # . Report Count (63), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x61, # . Report ID (97), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x62, # . Report ID (98), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x65, # . Report ID (101), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x66, # . Report ID (102), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x67, # . Report ID (103), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x04, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x68, # . Report ID (104), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x11, # . Report Count (17), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x6F, # . Report ID (111), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x3E, # . Report Count (62), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCD, # . Report ID (205), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x02, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x16, # . Report ID (22), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0E, # . Report Count (14), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0x35, # . Report ID (53), + 0x09, 0x00, # . Usage (00h), + 0x95, 0x0A, # . Report Count (10), + 0xB1, 0x02, # . Feature (Variable), + 0xC0, # . End Collection, + 0x85, 0xD1, # . Report ID (209), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD2, # . Report ID (210), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x01, # . Report Count (260), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD3, # . Report ID (211), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD4, # . Report ID (212), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD5, # . Report ID (213), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD6, # . Report ID (214), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD7, # . Report ID (215), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x08, 0x00, # . Report Count (8), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD8, # . Report ID (216), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x0C, 0x00, # . Report Count (12), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xD9, # . Report ID (217), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x00, 0x0A, # . Report Count (2560), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDA, # . Report ID (218), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x04, # . Report Count (1028), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDB, # . Report ID (219), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x06, 0x00, # . Report Count (6), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDC, # . Report ID (220), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDD, # . Report ID (221), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDE, # . Report ID (222), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x04, 0x00, # . Report Count (4), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xDF, # . Report ID (223), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x22, 0x00, # . Report Count (34), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE0, # . Report ID (224), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x01, 0x00, # . Report Count (1), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE1, # . Report ID (225), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE2, # . Report ID (226), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE3, # . Report ID (227), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x02, 0x00, # . Report Count (2), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xE4, # . Report ID (228), + 0x09, 0x01, # . Usage (01h), + 0x96, 0xFF, 0x01, # . Report Count (511), + 0xB1, 0x02, # . Feature (Variable), + 0x85, 0xCB, # . Report ID (203), + 0x09, 0x01, # . Usage (01h), + 0x96, 0x1F, 0x00, # . Report Count (31), + 0xB1, 0x02, # . Feature (Variable), + 0xC0 # . End Collection +] +# fmt: on diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py new file mode 100644 index 000000000000..b1eb2bc787fc --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py @@ -0,0 +1,844 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Benjamin Tissoires +# Copyright (c) 2017 Red Hat, Inc. +# Copyright (c) 2020 Wacom Technology Corp. +# +# Authors: +# Jason Gerecke + +""" +Tests for the Wacom driver generic codepath. + +This module tests the function of the Wacom driver's generic codepath. +The generic codepath is used by devices which are not explicitly listed +in the driver's device table. It uses the device's HID descriptor to +decode reports sent by the device. +""" + +from .descriptors_wacom import ( + wacom_pth660_v145, + wacom_pth660_v150, + wacom_pth860_v145, + wacom_pth860_v150, + wacom_pth460_v105, +) + +import attr +from enum import Enum +from hidtools.hut import HUT +from hidtools.hid import HidUnit +from . import base +import libevdev +import pytest + +import logging + +logger = logging.getLogger("hidtools.test.wacom") + +KERNEL_MODULE = ("wacom", "wacom") + + +class ProximityState(Enum): + """ + Enumeration of allowed proximity states. + """ + + # Tool is not able to be sensed by the device + OUT = 0 + + # Tool is close enough to be sensed, but some data may be invalid + # or inaccurate + IN_PROXIMITY = 1 + + # Tool is close enough to be sensed with high accuracy. All data + # valid. + IN_RANGE = 2 + + def fill(self, reportdata): + """Fill a report with approrpiate HID properties/values.""" + reportdata.inrange = self in [ProximityState.IN_RANGE] + reportdata.wacomsense = self in [ + ProximityState.IN_PROXIMITY, + ProximityState.IN_RANGE, + ] + + +class ReportData: + """ + Placeholder for HID report values. + """ + + pass + + +@attr.s +class Buttons: + """ + Stylus button state. + + Describes the state of each of the buttons / "side switches" that + may be present on a stylus. Buttons set to 'None' indicate the + state is "unchanged" since the previous event. + """ + + primary = attr.ib(default=None) + secondary = attr.ib(default=None) + tertiary = attr.ib(default=None) + + @staticmethod + def clear(): + """Button object with all states cleared.""" + return Buttons(False, False, False) + + def fill(self, reportdata): + """Fill a report with approrpiate HID properties/values.""" + reportdata.barrelswitch = int(self.primary or 0) + reportdata.secondarybarrelswitch = int(self.secondary or 0) + reportdata.b3 = int(self.tertiary or 0) + + +@attr.s +class ToolID: + """ + Stylus tool identifiers. + + Contains values used to identify a specific stylus, e.g. its serial + number and tool-type identifier. Values of ``0`` may sometimes be + used for the out-of-range condition. + """ + + serial = attr.ib() + tooltype = attr.ib() + + @staticmethod + def clear(): + """ToolID object with all fields cleared.""" + return ToolID(0, 0) + + def fill(self, reportdata): + """Fill a report with approrpiate HID properties/values.""" + reportdata.transducerserialnumber = self.serial & 0xFFFFFFFF + reportdata.serialhi = (self.serial >> 32) & 0xFFFFFFFF + reportdata.tooltype = self.tooltype + + +@attr.s +class PhysRange: + """ + Range of HID physical values, with units. + """ + + unit = attr.ib() + min_size = attr.ib() + max_size = attr.ib() + + CENTIMETER = HidUnit.from_string("SILinear: cm") + DEGREE = HidUnit.from_string("EnglishRotation: deg") + + def contains(self, field): + """ + Check if the physical size of the provided field is in range. + + Compare the physical size described by the provided HID field + against the range of sizes described by this object. This is + an exclusive range comparison (e.g. 0 cm is not within the + range 0 cm - 5 cm) and exact unit comparison (e.g. 1 inch is + not within the range 0 cm - 5 cm). + """ + phys_size = (field.physical_max - field.physical_min) * 10 ** (field.unit_exp) + return ( + field.unit == self.unit.value + and phys_size > self.min_size + and phys_size < self.max_size + ) + + +class BaseTablet(base.UHIDTestDevice): + """ + Skeleton object for all kinds of tablet devices. + """ + + def __init__(self, rdesc, name=None, info=None): + assert rdesc is not None + super().__init__(name, "Pen", input_info=info, rdesc=rdesc) + self.buttons = Buttons.clear() + self.toolid = ToolID.clear() + self.proximity = ProximityState.OUT + self.offset = 0 + self.ring = -1 + self.ek0 = False + + def match_evdev_rule(self, application, evdev): + """ + Filter out evdev nodes based on the requested application. + + The Wacom driver may create several device nodes for each USB + interface device. It is crucial that we run tests with the + expected device node or things will obviously go off the rails. + Use the Wacom driver's usual naming conventions to apply a + sensible default filter. + """ + if application in ["Pen", "Pad"]: + return evdev.name.endswith(application) + else: + return True + + def create_report( + self, x, y, pressure, buttons=None, toolid=None, proximity=None, reportID=None + ): + """ + Return an input report for this device. + + :param x: absolute x + :param y: absolute y + :param pressure: pressure + :param buttons: stylus button state. Use ``None`` for unchanged. + :param toolid: tool identifiers. Use ``None`` for unchanged. + :param proximity: a ProximityState indicating the sensor's ability + to detect and report attributes of this tool. Use ``None`` + for unchanged. + :param reportID: the numeric report ID for this report, if needed + """ + if buttons is not None: + self.buttons = buttons + buttons = self.buttons + + if toolid is not None: + self.toolid = toolid + toolid = self.toolid + + if proximity is not None: + self.proximity = proximity + proximity = self.proximity + + reportID = reportID or self.default_reportID + + report = ReportData() + report.x = x + report.y = y + report.tippressure = pressure + report.tipswitch = pressure > 0 + buttons.fill(report) + proximity.fill(report) + toolid.fill(report) + + return super().create_report(report, reportID=reportID) + + def create_report_heartbeat(self, reportID): + """ + Return a heartbeat input report for this device. + + Heartbeat reports generally contain battery status information, + among other things. + """ + report = ReportData() + report.wacombatterycharging = 1 + return super().create_report(report, reportID=reportID) + + def create_report_pad(self, reportID, ring, ek0): + report = ReportData() + + if ring is not None: + self.ring = ring + ring = self.ring + + if ek0 is not None: + self.ek0 = ek0 + ek0 = self.ek0 + + if ring >= 0: + report.wacomtouchring = ring + report.wacomtouchringstatus = 1 + else: + report.wacomtouchring = 0x7F + report.wacomtouchringstatus = 0 + + report.wacomexpresskey00 = ek0 + return super().create_report(report, reportID=reportID) + + def event(self, x, y, pressure, buttons=None, toolid=None, proximity=None): + """ + Send an input event on the default report ID. + + :param x: absolute x + :param y: absolute y + :param buttons: stylus button state. Use ``None`` for unchanged. + :param toolid: tool identifiers. Use ``None`` for unchanged. + :param proximity: a ProximityState indicating the sensor's ability + to detect and report attributes of this tool. Use ``None`` + for unchanged. + """ + r = self.create_report(x, y, pressure, buttons, toolid, proximity) + self.call_input_event(r) + return [r] + + def event_heartbeat(self, reportID): + """ + Send a heartbeat event on the requested report ID. + """ + r = self.create_report_heartbeat(reportID) + self.call_input_event(r) + return [r] + + def event_pad(self, reportID, ring=None, ek0=None): + """ + Send a pad event on the requested report ID. + """ + r = self.create_report_pad(reportID, ring, ek0) + self.call_input_event(r) + return [r] + + def get_report(self, req, rnum, rtype): + if rtype != self.UHID_FEATURE_REPORT: + return (1, []) + + rdesc = None + for v in self.parsed_rdesc.feature_reports.values(): + if v.report_ID == rnum: + rdesc = v + + if rdesc is None: + return (1, []) + + result = (1, []) + result = self.create_report_offset(rdesc) or result + return result + + def create_report_offset(self, rdesc): + require = [ + "Wacom Offset Left", + "Wacom Offset Top", + "Wacom Offset Right", + "Wacom Offset Bottom", + ] + if not set(require).issubset(set([f.usage_name for f in rdesc])): + return None + + report = ReportData() + report.wacomoffsetleft = self.offset + report.wacomoffsettop = self.offset + report.wacomoffsetright = self.offset + report.wacomoffsetbottom = self.offset + r = rdesc.create_report([report], None) + return (0, r) + + +class OpaqueTablet(BaseTablet): + """ + Bare-bones opaque tablet with a minimum of features. + + A tablet stripped down to its absolute core. It is capable of + reporting X/Y position and if the pen is in contact. No pressure, + no barrel switches, no eraser. Notably it *does* report an "In + Range" flag, but this is only because the Wacom driver expects + one to function properly. The device uses only standard HID usages, + not any of Wacom's vendor-defined pages. + """ + + # fmt: off + report_descriptor = [ + 0x05, 0x0D, # . Usage Page (Digitizer), + 0x09, 0x01, # . Usage (Digitizer), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x01, # . Report ID (1), + 0x09, 0x20, # . Usage (Stylus), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (Tip Switch), + 0x09, 0x32, # . Usage (In Range), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x05, 0x01, # . Usage Page (Desktop), + 0x09, 0x30, # . Usage (X), + 0x27, 0x80, 0x3E, 0x00, 0x00, # . Logical Maximum (16000), + 0x47, 0x80, 0x3E, 0x00, 0x00, # . Physical Maximum (16000), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x31, # . Usage (Y), + 0x27, 0x28, 0x23, 0x00, 0x00, # . Logical Maximum (9000), + 0x47, 0x28, 0x23, 0x00, 0x00, # . Physical Maximum (9000), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0xC0, # . End Collection, + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)): + super().__init__(rdesc, name, info) + self.default_reportID = 1 + + +class OpaqueCTLTablet(BaseTablet): + """ + Opaque tablet similar to something in the CTL product line. + + A pen-only tablet with most basic features you would expect from + an actual device. Position, eraser, pressure, barrel buttons. + Uses the Wacom vendor-defined usage page. + """ + + # fmt: off + report_descriptor = [ + 0x06, 0x0D, 0xFF, # . Usage Page (Vnd Wacom Emr), + 0x09, 0x01, # . Usage (Digitizer), + 0xA1, 0x01, # . Collection (Application), + 0x85, 0x10, # . Report ID (16), + 0x09, 0x20, # . Usage (Stylus), + 0x35, 0x00, # . Physical Minimum (0), + 0x45, 0x00, # . Physical Maximum (0), + 0x15, 0x00, # . Logical Minimum (0), + 0x25, 0x01, # . Logical Maximum (1), + 0xA1, 0x00, # . Collection (Physical), + 0x09, 0x42, # . Usage (Tip Switch), + 0x09, 0x44, # . Usage (Barrel Switch), + 0x09, 0x5A, # . Usage (Secondary Barrel Switch), + 0x09, 0x45, # . Usage (Eraser), + 0x09, 0x3C, # . Usage (Invert), + 0x09, 0x32, # . Usage (In Range), + 0x09, 0x36, # . Usage (In Proximity), + 0x25, 0x01, # . Logical Maximum (1), + 0x75, 0x01, # . Report Size (1), + 0x95, 0x07, # . Report Count (7), + 0x81, 0x02, # . Input (Variable), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x30, 0x01, # . Usage (X), + 0x65, 0x11, # . Unit (Centimeter), + 0x55, 0x0D, # . Unit Exponent (13), + 0x47, 0x80, 0x3E, 0x00, 0x00, # . Physical Maximum (16000), + 0x27, 0x80, 0x3E, 0x00, 0x00, # . Logical Maximum (16000), + 0x75, 0x18, # . Report Size (24), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x0A, 0x31, 0x01, # . Usage (Y), + 0x47, 0x28, 0x23, 0x00, 0x00, # . Physical Maximum (9000), + 0x27, 0x28, 0x23, 0x00, 0x00, # . Logical Maximum (9000), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x30, # . Usage (Tip Pressure), + 0x55, 0x00, # . Unit Exponent (0), + 0x65, 0x00, # . Unit, + 0x47, 0x00, 0x00, 0x00, 0x00, # . Physical Maximum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x81, 0x02, # . Input (Variable), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x06, # . Report Count (6), + 0x81, 0x03, # . Input (Constant, Variable), + 0x0A, 0x32, 0x01, # . Usage (Z), + 0x25, 0x3F, # . Logical Maximum (63), + 0x75, 0x08, # . Report Size (8), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x5B, # . Usage (Transducer Serial Number), + 0x09, 0x5C, # . Usage (Transducer Serial Number Hi), + 0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648), + 0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647), + 0x75, 0x20, # . Report Size (32), + 0x95, 0x02, # . Report Count (2), + 0x81, 0x02, # . Input (Variable), + 0x09, 0x77, # . Usage (Tool Type), + 0x15, 0x00, # . Logical Minimum (0), + 0x26, 0xFF, 0x0F, # . Logical Maximum (4095), + 0x75, 0x10, # . Report Size (16), + 0x95, 0x01, # . Report Count (1), + 0x81, 0x02, # . Input (Variable), + 0xC0, # . End Collection, + 0xC0 # . End Collection + ] + # fmt: on + + def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)): + super().__init__(rdesc, name, info) + self.default_reportID = 16 + + +class PTHX60_Pen(BaseTablet): + """ + Pen interface of a PTH-660 / PTH-860 / PTH-460 tablet. + + This generation of devices are nearly identical to each other, though + the PTH-460 uses a slightly different descriptor construction (splits + the pad among several physical collections) + """ + + def __init__(self, rdesc=None, name=None, info=None): + super().__init__(rdesc, name, info) + self.default_reportID = 16 + + +class BaseTest: + class TestTablet(base.BaseTestCase.TestUhid): + kernel_modules = [KERNEL_MODULE] + + def sync_and_assert_events( + self, report, expected_events, auto_syn=True, strict=False + ): + """ + Assert we see the expected events in response to a report. + """ + uhdev = self.uhdev + syn_event = self.syn_event + if auto_syn: + expected_events.append(syn_event) + actual_events = uhdev.next_sync_events() + self.debug_reports(report, uhdev, actual_events) + if strict: + self.assertInputEvents(expected_events, actual_events) + else: + self.assertInputEventsIn(expected_events, actual_events) + + def get_usages(self, uhdev): + def get_report_usages(report): + application = report.application + for field in report.fields: + if field.usages is not None: + for usage in field.usages: + yield (field, usage, application) + else: + yield (field, field.usage, application) + + desc = uhdev.parsed_rdesc + reports = [ + *desc.input_reports.values(), + *desc.feature_reports.values(), + *desc.output_reports.values(), + ] + for report in reports: + for usage in get_report_usages(report): + yield usage + + def assertName(self, uhdev): + """ + Assert that the name is as we expect. + + The Wacom driver applies a number of decorations to the name + provided by the hardware. We cannot rely on the definition of + this assertion from the base class to work properly. + """ + evdev = uhdev.get_evdev() + expected_name = uhdev.name + " Pen" + if "wacom" not in expected_name.lower(): + expected_name = "Wacom " + expected_name + assert evdev.name == expected_name + + def test_descriptor_physicals(self): + """ + Verify that all HID usages which should have a physical range + actually do, and those which shouldn't don't. Also verify that + the associated unit is correct and within a sensible range. + """ + + def usage_id(page_name, usage_name): + page = HUT.usage_page_from_name(page_name) + return (page.page_id << 16) | page[usage_name].usage + + required = { + usage_id("Generic Desktop", "X"): PhysRange( + PhysRange.CENTIMETER, 5, 150 + ), + usage_id("Generic Desktop", "Y"): PhysRange( + PhysRange.CENTIMETER, 5, 150 + ), + usage_id("Digitizers", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Digitizers", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Digitizers", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360), + usage_id("Wacom", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Wacom", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), + usage_id("Wacom", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360), + usage_id("Wacom", "X"): PhysRange(PhysRange.CENTIMETER, 5, 150), + usage_id("Wacom", "Y"): PhysRange(PhysRange.CENTIMETER, 5, 150), + usage_id("Wacom", "Wacom TouchRing"): PhysRange( + PhysRange.DEGREE, 358, 360 + ), + usage_id("Wacom", "Wacom Offset Left"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + usage_id("Wacom", "Wacom Offset Top"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + usage_id("Wacom", "Wacom Offset Right"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + usage_id("Wacom", "Wacom Offset Bottom"): PhysRange( + PhysRange.CENTIMETER, 0, 0.5 + ), + } + for field, usage, application in self.get_usages(self.uhdev): + if application == usage_id("Generic Desktop", "Mouse"): + # Ignore the vestigial Mouse collection which exists + # on Wacom tablets only for backwards compatibility. + continue + + expect_physical = usage in required + + phys_set = field.physical_min != 0 or field.physical_max != 0 + assert phys_set == expect_physical + + unit_set = field.unit != 0 + assert unit_set == expect_physical + + if unit_set: + assert required[usage].contains(field) + + def test_prop_direct(self): + """ + Todo: Verify that INPUT_PROP_DIRECT is set on display devices. + """ + pass + + def test_prop_pointer(self): + """ + Todo: Verify that INPUT_PROP_POINTER is set on opaque devices. + """ + pass + + +class TestOpaqueTablet(BaseTest.TestTablet): + def create_device(self): + return OpaqueTablet() + + def test_sanity(self): + """ + Bring a pen into contact with the tablet, then remove it. + + Ensure that we get the basic tool/touch/motion events that should + be sent by the driver. + """ + uhdev = self.uhdev + + self.sync_and_assert_events( + uhdev.event( + 100, + 200, + pressure=300, + buttons=Buttons.clear(), + toolid=ToolID(serial=1, tooltype=1), + proximity=ProximityState.IN_RANGE, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(110, 220, pressure=0), + [ + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 220), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0), + ], + ) + + self.sync_and_assert_events( + uhdev.event( + 120, + 230, + pressure=0, + toolid=ToolID.clear(), + proximity=ProximityState.OUT, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0), + ], + ) + + self.sync_and_assert_events( + uhdev.event(130, 240, pressure=0), [], auto_syn=False, strict=True + ) + + +class TestOpaqueCTLTablet(TestOpaqueTablet): + def create_device(self): + return OpaqueCTLTablet() + + def test_buttons(self): + """ + Test that the barrel buttons (side switches) work as expected. + + Press and release each button individually to verify that we get + the expected events. + """ + uhdev = self.uhdev + + self.sync_and_assert_events( + uhdev.event( + 100, + 200, + pressure=0, + buttons=Buttons.clear(), + toolid=ToolID(serial=1, tooltype=1), + proximity=ProximityState.IN_RANGE, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=True)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=False)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=True)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 1), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + self.sync_and_assert_events( + uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=False)), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 0), + libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1), + ], + ) + + +PTHX60_Devices = [ + {"rdesc": wacom_pth660_v145, "info": (0x3, 0x056A, 0x0357)}, + {"rdesc": wacom_pth660_v150, "info": (0x3, 0x056A, 0x0357)}, + {"rdesc": wacom_pth860_v145, "info": (0x3, 0x056A, 0x0358)}, + {"rdesc": wacom_pth860_v150, "info": (0x3, 0x056A, 0x0358)}, + {"rdesc": wacom_pth460_v105, "info": (0x3, 0x056A, 0x0392)}, +] + +PTHX60_Names = [ + "PTH-660/v145", + "PTH-660/v150", + "PTH-860/v145", + "PTH-860/v150", + "PTH-460/v105", +] + + +class TestPTHX60_Pen(TestOpaqueCTLTablet): + @pytest.fixture( + autouse=True, scope="class", params=PTHX60_Devices, ids=PTHX60_Names + ) + def set_device_params(self, request): + request.cls.device_params = request.param + + def create_device(self): + return PTHX60_Pen(**self.device_params) + + @pytest.mark.xfail + def test_descriptor_physicals(self): + # XFAIL: Various documented errata + super().test_descriptor_physicals() + + def test_heartbeat_spurious(self): + """ + Test that the heartbeat report does not send spurious events. + """ + uhdev = self.uhdev + + self.sync_and_assert_events( + uhdev.event( + 100, + 200, + pressure=300, + buttons=Buttons.clear(), + toolid=ToolID(serial=1, tooltype=0x822), + proximity=ProximityState.IN_RANGE, + ), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100), + libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200), + libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1), + ], + ) + + # Exactly zero events: not even a SYN + self.sync_and_assert_events( + uhdev.event_heartbeat(19), [], auto_syn=False, strict=True + ) + + self.sync_and_assert_events( + uhdev.event(110, 200, pressure=300), + [ + libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110), + ], + ) + + def test_empty_pad_sync(self): + self.empty_pad_sync(num=3, denom=16, reverse=True) + + def empty_pad_sync(self, num, denom, reverse): + """ + Test that multiple pad collections do not trigger empty syncs. + """ + + def offset_rotation(value): + """ + Offset touchring rotation values by the same factor as the + Linux kernel. Tablets historically don't use the same origin + as HID, and it sometimes changes from tablet to tablet... + """ + evdev = self.uhdev.get_evdev() + info = evdev.absinfo[libevdev.EV_ABS.ABS_WHEEL] + delta = info.maximum - info.minimum + 1 + if reverse: + value = info.maximum - value + value += num * delta // denom + if value > info.maximum: + value -= delta + elif value < info.minimum: + value += delta + return value + + uhdev = self.uhdev + uhdev.application = "Pad" + evdev = uhdev.get_evdev() + + print(evdev.name) + self.sync_and_assert_events( + uhdev.event_pad(reportID=17, ring=0, ek0=1), + [ + libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 1), + libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(0)), + libevdev.InputEvent(libevdev.EV_ABS.ABS_MISC, 15), + ], + ) + + self.sync_and_assert_events( + uhdev.event_pad(reportID=17, ring=1, ek0=1), + [libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(1))], + ) + + self.sync_and_assert_events( + uhdev.event_pad(reportID=17, ring=2, ek0=0), + [ + libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(2)), + libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 0), + ], + ) From patchwork Fri Feb 17 16:18:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58700 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981128wrn; Fri, 17 Feb 2023 08:28:41 -0800 (PST) X-Google-Smtp-Source: AK7set/Wx8WhX4h4f1mqshWdjxUIAdOV9bQ6eHc8QTueA4H2vJWz9myc/wQ6ZwOsiuy3ENallBaY X-Received: by 2002:a17:906:2659:b0:886:221b:44e5 with SMTP id i25-20020a170906265900b00886221b44e5mr9352683ejc.62.1676651321064; Fri, 17 Feb 2023 08:28:41 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651321; cv=none; d=google.com; s=arc-20160816; b=DqHA3oBTk9NkC1HrLGwTOz6okL3OSurdmpiz/bxabKlPw0x4MTEwxxnhNHB2LktI1/ sdjVZ7A/g5fz5KYxRrXUhs91a0moD+tSZL4iRRg1Fku3ffjohML0nLdxUMee0drka4Pk BoLE505e8hWyCVf6BlAk+xoWz53JdWz2ovsP7POIw+LpDS+Vgn9/xnv1ooX+Z3Bd6GlW x7lwEDQemWRzQggoTRdhRXdmWv3/g8sA4z19WOlgZFuRrcmBthaWcmt12Gf8jiAReX5B 7TNwtYcbibV7+rJ0NzMMxDLVXZlhnQ5MgDG3H9bJPT6eiRQso5+113RKLRtSR55UprPx eQLA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=5rVpYXrca9+Iqt65tdSPetVuX0vl1nKf85t3g3FCKQw=; b=RSSAgPuADfOgS8GPFjIdg2aeWeOdSwGTwDSUVYnltYtCV8/2cd+/jfjg0dXR6tXtbZ ETox2WPpadANXb65pBcaL1qbF9HLjUqHkU6Qoq8A8XD9BmozezSjvK9QOzmKpYxnKkZU eDmfov4UmFmzQnhrT2Z2h+1ONKOJvZiYgNx2GcGSJDHHiGYCVhDHyB9CWbE8w00cy5xY 3vNu6ARCA/WK+ZtR1RRWhlDVFCBmAOlMOLXIEwjt0mNCn3Gfn7Y5pKv42CsT0THMes2J viYcyxXTKQXLFHoy1q72CdM0L0tvV/C+GaNImWSMHjTiWZ/83QNSrctH41uZtdSggi82 IfpQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=YPryQJGw; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id 21-20020a170906001500b008b14b9286aesi5504315eja.750.2023.02.17.08.28.18; Fri, 17 Feb 2023 08:28:41 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=YPryQJGw; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231196AbjBQQUw (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52322 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231164AbjBQQUZ (ORCPT ); Fri, 17 Feb 2023 11:20:25 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 77C2872916 for ; Fri, 17 Feb 2023 08:19:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650742; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5rVpYXrca9+Iqt65tdSPetVuX0vl1nKf85t3g3FCKQw=; b=YPryQJGwxRjIbI9hL2yxKLruTOBb4vqR5cXQpjWVMJz0zHWVTlHisaGwICnmPuhSZbTvF5 czM5oI3M7/waC5JXx2YcFgZ1sYzIqzBDlGNsTudd91TLcg9CaPnuFiOUmRwXnu5Nd6oOfU hpFfRFt77/1dHvAPy3Iz6L+fbQWDq1Y= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-594-bcfot1hVOBCMNrA3rrj2Zw-1; Fri, 17 Feb 2023 11:18:59 -0500 X-MC-Unique: bcfot1hVOBCMNrA3rrj2Zw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D1D201C05AD6; Fri, 17 Feb 2023 16:18:58 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2BE8BC15BAD; Fri, 17 Feb 2023 16:18:57 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:18:02 +0100 Subject: [PATCH 08/11] selftests: hid: import hid-tools hid-apple tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-8-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Roderick Colenbrander X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=22118; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=5ZVcKqFaYfb3/FDPoi1Mg/fv8p5nkBwksf68dW3d4Ek=; b=WW9mqLRSg4J6HCZkfDLHcEK+VH+QIttfqvwRTKkVYpOOBT4zb0zDr0m+hX0FN12WylcEq2hmQ 3d0DtMfh3W4CmJWOjzId27WNzZ8IiarkeJLczryhdJYo8kgBb3MDZWh X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096335622809119?= X-GMAIL-MSGID: =?utf-8?q?1758096335622809119?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 1 + tools/testing/selftests/hid/hid-apple.sh | 7 + .../selftests/hid/tests/test_apple_keyboard.py | 440 +++++++++++++++++++++ 4 files changed, 449 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 4f11e865bbb3..ce03c65bfba0 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -6,6 +6,7 @@ include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh +TEST_PROGS += hid-apple.sh TEST_PROGS += hid-gamepad.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index 266fbd84ae9c..52b527cc2260 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -21,5 +21,6 @@ CONFIG_INPUT_EVDEV=y CONFIG_UHID=y CONFIG_USB=y CONFIG_USB_HID=y +CONFIG_HID_APPLE=y CONFIG_HID_MULTITOUCH=y CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-apple.sh b/tools/testing/selftests/hid/hid-apple.sh new file mode 100755 index 000000000000..656f2d5ae5a9 --- /dev/null +++ b/tools/testing/selftests/hid/hid-apple.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_apple_keyboard.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_apple_keyboard.py b/tools/testing/selftests/hid/tests/test_apple_keyboard.py new file mode 100644 index 000000000000..f81071d46166 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_apple_keyboard.py @@ -0,0 +1,440 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019 Benjamin Tissoires +# Copyright (c) 2019 Red Hat, Inc. +# + +from .test_keyboard import ArrayKeyboard, TestArrayKeyboard +from hidtools.util import BusType + +import libevdev +import logging + +logger = logging.getLogger("hidtools.test.apple-keyboard") + +KERNEL_MODULE = ("apple", "hid-apple") + + +class KbdData(object): + pass + + +class AppleKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x01, # .Report ID (1) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0xe0, # .Usage Minimum (224) + 0x29, 0xe7, # .Usage Maximum (231) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x08, # .Report Count (8) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x01, # .Report Count (1) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x05, # .Report Count (5) + 0x05, 0x08, # .Usage Page (LEDs) + 0x19, 0x01, # .Usage Minimum (1) + 0x29, 0x05, # .Usage Maximum (5) + 0x91, 0x02, # .Output (Data,Var,Abs) + 0x75, 0x03, # .Report Size (3) + 0x95, 0x01, # .Report Count (1) + 0x91, 0x01, # .Output (Cnst,Arr,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x06, # .Report Count (6) + 0x15, 0x00, # .Logical Minimum (0) + 0x26, 0xff, 0x00, # .Logical Maximum (255) + 0x05, 0x07, # .Usage Page (Keyboard) + 0x19, 0x00, # .Usage Minimum (0) + 0x2a, 0xff, 0x00, # .Usage Maximum (255) + 0x81, 0x00, # .Input (Data,Arr,Abs) + 0xc0, # End Collection + 0x05, 0x0c, # Usage Page (Consumer Devices) + 0x09, 0x01, # Usage (Consumer Control) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x47, # .Report ID (71) + 0x05, 0x01, # .Usage Page (Generic Desktop) + 0x09, 0x06, # .Usage (Keyboard) + 0xa1, 0x02, # .Collection (Logical) + 0x05, 0x06, # ..Usage Page (Generic Device Controls) + 0x09, 0x20, # ..Usage (Battery Strength) + 0x15, 0x00, # ..Logical Minimum (0) + 0x26, 0xff, 0x00, # ..Logical Maximum (255) + 0x75, 0x08, # ..Report Size (8) + 0x95, 0x01, # ..Report Count (1) + 0x81, 0x02, # ..Input (Data,Var,Abs) + 0xc0, # .End Collection + 0xc0, # End Collection + 0x05, 0x0c, # Usage Page (Consumer Devices) + 0x09, 0x01, # Usage (Consumer Control) + 0xa1, 0x01, # Collection (Application) + 0x85, 0x11, # .Report ID (17) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x03, # .Report Count (3) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x01, # .Report Count (1) + 0x05, 0x0c, # .Usage Page (Consumer Devices) + 0x09, 0xb8, # .Usage (Eject) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x06, 0xff, 0x00, # .Usage Page (Vendor Usage Page 0xff) + 0x09, 0x03, # .Usage (Vendor Usage 0x03) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x03, # .Report Count (3) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x05, 0x0c, # .Usage Page (Consumer Devices) + 0x85, 0x12, # .Report ID (18) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x01, # .Report Count (1) + 0x09, 0xcd, # .Usage (Play/Pause) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb3, # .Usage (Fast Forward) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb4, # .Usage (Rewind) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb5, # .Usage (Scan Next Track) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x09, 0xb6, # .Usage (Scan Previous Track) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x85, 0x13, # .Report ID (19) + 0x15, 0x00, # .Logical Minimum (0) + 0x25, 0x01, # .Logical Maximum (1) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x01, # .Report Count (1) + 0x06, 0x01, 0xff, # .Usage Page (Vendor Usage Page 0xff01) + 0x09, 0x0a, # .Usage (Vendor Usage 0x0a) + 0x81, 0x02, # .Input (Data,Var,Abs) + 0x06, 0x01, 0xff, # .Usage Page (Vendor Usage Page 0xff01) + 0x09, 0x0c, # .Usage (Vendor Usage 0x0c) + 0x81, 0x22, # .Input (Data,Var,Abs,NoPref) + 0x75, 0x01, # .Report Size (1) + 0x95, 0x06, # .Report Count (6) + 0x81, 0x01, # .Input (Cnst,Arr,Abs) + 0x85, 0x09, # .Report ID (9) + 0x09, 0x0b, # .Usage (Vendor Usage 0x0b) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x01, # .Report Count (1) + 0xb1, 0x02, # .Feature (Data,Var,Abs) + 0x75, 0x08, # .Report Size (8) + 0x95, 0x02, # .Report Count (2) + 0xb1, 0x01, # .Feature (Cnst,Arr,Abs) + 0xc0, # End Collection + ] + # fmt: on + + def __init__( + self, + rdesc=report_descriptor, + name="Apple Wireless Keyboard", + input_info=(BusType.BLUETOOTH, 0x05AC, 0x0256), + ): + super().__init__(rdesc, name, input_info) + self.default_reportID = 1 + + def send_fn_state(self, state): + data = KbdData() + setattr(data, "0xff0003", state) + r = self.create_report(data, reportID=17) + self.call_input_event(r) + return [r] + + +class TestAppleKeyboard(TestArrayKeyboard): + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + return AppleKeyboard() + + def test_single_function_key(self): + """check for function key reliability.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["F4"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0 + + def test_single_fn_function_key(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["F4"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + def test_single_fn_function_key_release_first(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["F4"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + + def test_single_fn_function_key_inverted(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.event(["F4"]) + r.extend(uhdev.send_fn_state(1)) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + + def test_multiple_fn_function_key_release_first(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["F4"])) + r.extend(uhdev.event(["F4", "F6"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.event(["F6"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + def test_multiple_fn_function_key_release_between(self): + """check for function key reliability with the fn key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + # press F4 + r = uhdev.event(["F4"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + # press Fn key + r = uhdev.send_fn_state(1) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + # keep F4 and press F6 + r = uhdev.event(["F4", "F6"]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + # keep F4 and F6 + r = uhdev.event(["F4", "F6"]) + expected = [] + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + # release Fn key and all keys + r = uhdev.send_fn_state(0) + r.extend(uhdev.event([])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + def test_single_pageup_key_release_first(self): + """check for function key reliability with the [page] up key.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev() + syn_event = self.syn_event + + r = uhdev.send_fn_state(1) + r.extend(uhdev.event(["UpArrow"])) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 1)) + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1 + + r = uhdev.send_fn_state(0) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1 + assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 + + r = uhdev.event([]) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 0)) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + self.assertInputEventsIn(expected, events) + assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0 + assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0 From patchwork Fri Feb 17 16:18:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58702 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981186wrn; Fri, 17 Feb 2023 08:28:47 -0800 (PST) X-Google-Smtp-Source: AK7set+PaVWRMiwBljFIa0RX8w2nVNvmWLLSIdozA2xz2PHn16HU0oUfvEzZ1abWR90HijvLIDPU X-Received: by 2002:a05:6402:1484:b0:4ad:7055:3908 with SMTP id e4-20020a056402148400b004ad70553908mr4613937edv.21.1676651327546; Fri, 17 Feb 2023 08:28:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651327; cv=none; d=google.com; s=arc-20160816; b=SS585t8uxt6pyEbg72kgBDellWVrk2XUE7wl7ReAhfHyASgI4IKcAiRvm9DRe6MCQL tovUXHuqqlcvbyIPWsrQ86TSsuROy01MfQ4fT4Hd5kVDSojHbXGN5gTlg7W2kQRH3bvs Sp5W4vHBW9MOi/nxa5weaS1yjjStewoIrGkj7doH1wpmPIR1akaZA55NoSEApEOtNwL8 A4erioKhe0HJO2JvSKrEGffWTyp7GJgtvg0YlUHRYRhpU56CpdaoWFxDQxyiDienux4r JNx3jKPBjcp7XyPgP3D3D/ABxJ03+XTDJ6Qym3NxP6Ebox1dKRCjUCPkv6h7U4OoZDPa DS0Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=R9HEkc68kBrPCHNCKk7IRwO1iDwaVhXDPVzUp6kzZu0=; b=wVxRH8bW+DuHR5yIFyepdIzocyHzvA1Uq9BEBF99y7zOoYS8j+VSvryiI/FOagUeEP ypgwUs0MnIyyd38SXdsB1iLk3m4n/habFhF4T5O63ohcz6r9SCVdMIkU2JYPD3p/8D/a tckkQSpFh6QdOmVP8zHjZnTMrNbJGqzvNCSPTackmyyjlsFSk1I6M0b8y642laP3GP+0 ZjskWR3hMIiRHRRilnkF2gUhDv0b6P+9wRpiz3M5NSi+4DvO6bPTvrPHo6iPEgb4spMU tfftffAgQP2hxiUXg8D7n2+2XMWIBxGD+MiGoklPhIAyeapk/cxzumigcQp9ibQuGAPT Lw6w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b="MKNzA/1+"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id k13-20020aa7d8cd000000b004acc28218e7si3965823eds.581.2023.02.17.08.28.24; Fri, 17 Feb 2023 08:28:47 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b="MKNzA/1+"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231207AbjBQQUz (ORCPT + 99 others); Fri, 17 Feb 2023 11:20:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53074 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230246AbjBQQU3 (ORCPT ); Fri, 17 Feb 2023 11:20:29 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0239271C98 for ; Fri, 17 Feb 2023 08:19:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650744; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=R9HEkc68kBrPCHNCKk7IRwO1iDwaVhXDPVzUp6kzZu0=; b=MKNzA/1+vPDekOhi/Rx4J+lEuhjD/9kvDQW8Pezn5WdAJj24BlX9+pem50az9crwud4nPJ GzS9a9WhhUW744p628mnl/5KdXDpJcou12QMTmTIGeFyJRJaXAezJdgJINLOsC7LamFAmZ yijavPIpXB3qECb44z1zIuEwrZ0OnEg= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-377-vmaAm1xuMLSzPit785927A-1; Fri, 17 Feb 2023 11:19:01 -0500 X-MC-Unique: vmaAm1xuMLSzPit785927A-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7431580280C; Fri, 17 Feb 2023 16:19:00 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 17354C15BA0; Fri, 17 Feb 2023 16:18:58 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:18:03 +0100 Subject: [PATCH 09/11] selftests: hid: import hid-tools hid-ite tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-9-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Peter Hutterer , Roderick Colenbrander X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=12211; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=PfonMQY364bbGXn/80KOa2Wxjaq21ABYhUoRdpo2/JA=; b=2rp2ooqHnDsINSwedJWhRUKJfhA3kF/BnVTEYxabbYujgxC6hc0QUsmuzqGGeXgjOEQhGoEtu V9hX9FHEix7Bm+9NQkimCmDUv8WosYzRY+IBwog/rBqPfvisAKDS0me X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096342898638497?= X-GMAIL-MSGID: =?utf-8?q?1758096342898638497?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Peter Hutterer Cc: Roderick Colenbrander Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 1 + tools/testing/selftests/hid/hid-ite.sh | 7 + .../selftests/hid/tests/test_ite_keyboard.py | 166 +++++++++++++++++++++ 4 files changed, 175 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index ce03c65bfba0..3ca696c44aab 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -8,6 +8,7 @@ include ../../../scripts/Makefile.include TEST_PROGS := hid-core.sh TEST_PROGS += hid-apple.sh TEST_PROGS += hid-gamepad.sh +TEST_PROGS += hid-ite.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index 52b527cc2260..f400b8d94e3c 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -22,5 +22,6 @@ CONFIG_UHID=y CONFIG_USB=y CONFIG_USB_HID=y CONFIG_HID_APPLE=y +CONFIG_HID_ITE=y CONFIG_HID_MULTITOUCH=y CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-ite.sh b/tools/testing/selftests/hid/hid-ite.sh new file mode 100755 index 000000000000..52c5ccf42292 --- /dev/null +++ b/tools/testing/selftests/hid/hid-ite.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_ite_keyboard.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_ite_keyboard.py b/tools/testing/selftests/hid/tests/test_ite_keyboard.py new file mode 100644 index 000000000000..38550c167bae --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_ite_keyboard.py @@ -0,0 +1,166 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020 Benjamin Tissoires +# Copyright (c) 2020 Red Hat, Inc. +# + +from .test_keyboard import ArrayKeyboard, TestArrayKeyboard +from hidtools.util import BusType + +import libevdev +import logging + +logger = logging.getLogger("hidtools.test.ite-keyboard") + +KERNEL_MODULE = ("itetech", "hid_ite") + + +class KbdData(object): + pass + + +# The ITE keyboards have an issue regarding the Wifi key: +# nothing comes in when pressing the key, but we get a null +# event on the key release. +# This test covers this case. +class ITEKeyboard(ArrayKeyboard): + # fmt: off + report_descriptor = [ + 0x06, 0x85, 0xff, # Usage Page (Vendor Usage Page 0xff85) + 0x09, 0x95, # Usage (Vendor Usage 0x95) 3 + 0xa1, 0x01, # Collection (Application) 5 + 0x85, 0x5a, # .Report ID (90) 7 + 0x09, 0x01, # .Usage (Vendor Usage 0x01) 9 + 0x15, 0x00, # .Logical Minimum (0) 11 + 0x26, 0xff, 0x00, # .Logical Maximum (255) 13 + 0x75, 0x08, # .Report Size (8) 16 + 0x95, 0x10, # .Report Count (16) 18 + 0xb1, 0x00, # .Feature (Data,Arr,Abs) 20 + 0xc0, # End Collection 22 + 0x05, 0x01, # Usage Page (Generic Desktop) 23 + 0x09, 0x06, # Usage (Keyboard) 25 + 0xa1, 0x01, # Collection (Application) 27 + 0x85, 0x01, # .Report ID (1) 29 + 0x75, 0x01, # .Report Size (1) 31 + 0x95, 0x08, # .Report Count (8) 33 + 0x05, 0x07, # .Usage Page (Keyboard) 35 + 0x19, 0xe0, # .Usage Minimum (224) 37 + 0x29, 0xe7, # .Usage Maximum (231) 39 + 0x15, 0x00, # .Logical Minimum (0) 41 + 0x25, 0x01, # .Logical Maximum (1) 43 + 0x81, 0x02, # .Input (Data,Var,Abs) 45 + 0x95, 0x01, # .Report Count (1) 47 + 0x75, 0x08, # .Report Size (8) 49 + 0x81, 0x03, # .Input (Cnst,Var,Abs) 51 + 0x95, 0x05, # .Report Count (5) 53 + 0x75, 0x01, # .Report Size (1) 55 + 0x05, 0x08, # .Usage Page (LEDs) 57 + 0x19, 0x01, # .Usage Minimum (1) 59 + 0x29, 0x05, # .Usage Maximum (5) 61 + 0x91, 0x02, # .Output (Data,Var,Abs) 63 + 0x95, 0x01, # .Report Count (1) 65 + 0x75, 0x03, # .Report Size (3) 67 + 0x91, 0x03, # .Output (Cnst,Var,Abs) 69 + 0x95, 0x06, # .Report Count (6) 71 + 0x75, 0x08, # .Report Size (8) 73 + 0x15, 0x00, # .Logical Minimum (0) 75 + 0x26, 0xff, 0x00, # .Logical Maximum (255) 77 + 0x05, 0x07, # .Usage Page (Keyboard) 80 + 0x19, 0x00, # .Usage Minimum (0) 82 + 0x2a, 0xff, 0x00, # .Usage Maximum (255) 84 + 0x81, 0x00, # .Input (Data,Arr,Abs) 87 + 0xc0, # End Collection 89 + 0x05, 0x0c, # Usage Page (Consumer Devices) 90 + 0x09, 0x01, # Usage (Consumer Control) 92 + 0xa1, 0x01, # Collection (Application) 94 + 0x85, 0x02, # .Report ID (2) 96 + 0x19, 0x00, # .Usage Minimum (0) 98 + 0x2a, 0x3c, 0x02, # .Usage Maximum (572) 100 + 0x15, 0x00, # .Logical Minimum (0) 103 + 0x26, 0x3c, 0x02, # .Logical Maximum (572) 105 + 0x75, 0x10, # .Report Size (16) 108 + 0x95, 0x01, # .Report Count (1) 110 + 0x81, 0x00, # .Input (Data,Arr,Abs) 112 + 0xc0, # End Collection 114 + 0x05, 0x01, # Usage Page (Generic Desktop) 115 + 0x09, 0x0c, # Usage (Wireless Radio Controls) 117 + 0xa1, 0x01, # Collection (Application) 119 + 0x85, 0x03, # .Report ID (3) 121 + 0x15, 0x00, # .Logical Minimum (0) 123 + 0x25, 0x01, # .Logical Maximum (1) 125 + 0x09, 0xc6, # .Usage (Wireless Radio Button) 127 + 0x95, 0x01, # .Report Count (1) 129 + 0x75, 0x01, # .Report Size (1) 131 + 0x81, 0x06, # .Input (Data,Var,Rel) 133 + 0x75, 0x07, # .Report Size (7) 135 + 0x81, 0x03, # .Input (Cnst,Var,Abs) 137 + 0xc0, # End Collection 139 + 0x05, 0x88, # Usage Page (Vendor Usage Page 0x88) 140 + 0x09, 0x01, # Usage (Vendor Usage 0x01) 142 + 0xa1, 0x01, # Collection (Application) 144 + 0x85, 0x04, # .Report ID (4) 146 + 0x19, 0x00, # .Usage Minimum (0) 148 + 0x2a, 0xff, 0xff, # .Usage Maximum (65535) 150 + 0x15, 0x00, # .Logical Minimum (0) 153 + 0x26, 0xff, 0xff, # .Logical Maximum (65535) 155 + 0x75, 0x08, # .Report Size (8) 158 + 0x95, 0x02, # .Report Count (2) 160 + 0x81, 0x02, # .Input (Data,Var,Abs) 162 + 0xc0, # End Collection 164 + 0x05, 0x01, # Usage Page (Generic Desktop) 165 + 0x09, 0x80, # Usage (System Control) 167 + 0xa1, 0x01, # Collection (Application) 169 + 0x85, 0x05, # .Report ID (5) 171 + 0x19, 0x81, # .Usage Minimum (129) 173 + 0x29, 0x83, # .Usage Maximum (131) 175 + 0x15, 0x00, # .Logical Minimum (0) 177 + 0x25, 0x01, # .Logical Maximum (1) 179 + 0x95, 0x08, # .Report Count (8) 181 + 0x75, 0x01, # .Report Size (1) 183 + 0x81, 0x02, # .Input (Data,Var,Abs) 185 + 0xc0, # End Collection 187 + ] + # fmt: on + + def __init__( + self, + rdesc=report_descriptor, + name=None, + input_info=(BusType.USB, 0x06CB, 0x2968), + ): + super().__init__(rdesc, name, input_info) + + def event(self, keys, reportID=None, application=None): + application = application or "Keyboard" + return super().event(keys, reportID, application) + + +class TestITEKeyboard(TestArrayKeyboard): + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + return ITEKeyboard() + + def test_wifi_key(self): + uhdev = self.uhdev + syn_event = self.syn_event + + # the following sends a 'release' event on the Wifi key. + # the kernel is supposed to translate this into Wifi key + # down and up + r = [0x03, 0x00] + uhdev.call_input_event(r) + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 1)) + events = uhdev.next_sync_events() + self.debug_reports([r], uhdev, events) + self.assertInputEventsIn(expected, events) + + expected = [syn_event] + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 0)) + # the kernel sends the two down/up key events in a batch, no need to + # call events = uhdev.next_sync_events() + self.debug_reports([], uhdev, events) + self.assertInputEventsIn(expected, events) From patchwork Fri Feb 17 16:18:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58707 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981926wrn; Fri, 17 Feb 2023 08:30:09 -0800 (PST) X-Google-Smtp-Source: AK7set9OWz8JZghqQHt/AraxXBTQQ3grgBiYxFbiOgU6PpfjKo+8/lILqPNOVxZawKdQ2Mo+hLOc X-Received: by 2002:a17:906:3615:b0:8ae:f19f:609e with SMTP id q21-20020a170906361500b008aef19f609emr774403ejb.66.1676651409237; Fri, 17 Feb 2023 08:30:09 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651409; cv=none; d=google.com; s=arc-20160816; b=CU/oUknOQ0yBX+0UIMr16k6ki3UOBL/oxiWehwvxSMpYLLG4pTrTakbRx7ycwm1Jz8 vLrpGDfv0u5yLcmxND/tKLLLI9qurmVd+YeDRO8MyR8G9n64eswoWLHJcmAYAe9U4nia aUCG79j2QeTWArOQKJdmwcUZMYV58aDYrH+PLZIYxg/WiqTnQq+q3auZH/BWXX13qBgR RmlN9pL2tXNV/jgmsrycWa+ASv12GSnuL/FZgnW5OsIzI3ZgUd8UOjgJsWv32LAXWUj9 UKT3LhQ4yuLEG0pJyGJXWrEi99nhWn68PaZ9EBTKbmxoF0wwJHGYE4W2VDKCNWUgLZvl Jicw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=HVsOUldtjpJvVyw0Csw/egkP/31uwH/LRh0j7R/R6EY=; b=ba+FFK1DjjcLhaM5tCUTsxgKGCCBVVPkF1751fot5Uers0uRI0yaS5G3SD3S7Ihpb4 RYgv+qQNHwovVfH+6upoJBzg1+657IzzCDeUbpMWuDDjJxJQWUDE9kj9fWqhDlPaE6yZ LKaIXzMoQhcxl8UkyUdI1lAydI4cexcenB3wCBPqWAzJldrXaSrsS/jcOmT8GPGRbeL3 sHgQQueBbyUUYAeyF0L0uSzpPTOmRwDrLvKbhR8NMPUwBfUfCl+SyP9hWMTbWuFv2NXt dlTLOWuuCyJYpyd8F8qnZgviO7g/+8dcGU5x+9AA9xSP+x0sHQkiwH7xYRlxspg0a50J DVcg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=GyC5jLwP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id mf21-20020a1709071a5500b008b1318e9b71si5442321ejc.519.2023.02.17.08.29.46; Fri, 17 Feb 2023 08:30:09 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=GyC5jLwP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231261AbjBQQVK (ORCPT + 99 others); Fri, 17 Feb 2023 11:21:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53076 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230017AbjBQQUi (ORCPT ); Fri, 17 Feb 2023 11:20:38 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4C7F3711AD for ; Fri, 17 Feb 2023 08:19:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650746; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HVsOUldtjpJvVyw0Csw/egkP/31uwH/LRh0j7R/R6EY=; b=GyC5jLwPrURwAS6beN5gHLpxFYlKerNABydxcJsLbxzkhEFC3KR0+rZ6BsP1LXqAm0UAyQ uinRksjaUzTojIcQxUUrHr+DAtLvxwAOttT76hI8JU+vEa3mEONcZO9ti3XHCA1JXbBgh7 jAKCpN6tKtSwBqIQxcBPNA2AXQJrimM= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-668-qDSt2WXuOr6KDHyTqBu6FQ-1; Fri, 17 Feb 2023 11:19:03 -0500 X-MC-Unique: qDSt2WXuOr6KDHyTqBu6FQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8F0793C025C4; Fri, 17 Feb 2023 16:19:02 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id ECDADC15BA0; Fri, 17 Feb 2023 16:19:00 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:18:04 +0100 Subject: [PATCH 10/11] selftests: hid: import hid-tools hid-sony and hid-playstation tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-10-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Roderick Colenbrander , Jose Torreguitar X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=13986; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=tFgoHCJsykqT5eoFuiHk5bbXNKEXLpiSTqTgdIQzyV4=; b=izyDrJ6JP9r/c463+7YgXbEpicHNskk+j3rxG5oqOfOajMSSLXPLcSacwx/CM9uj7BLMVyJ0A gYGA1TCbrXcCVY2crK7SGzaINTZfX/yBva+vkdJZizs62Knc5KE6g4u X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096428249383325?= X-GMAIL-MSGID: =?utf-8?q?1758096428249383325?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Cc: Roderick Colenbrander Cc: Jose Torreguitar Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/config | 5 + tools/testing/selftests/hid/hid-sony.sh | 7 + tools/testing/selftests/hid/tests/test_sony.py | 282 +++++++++++++++++++++++++ 4 files changed, 295 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 3ca696c44aab..dcea4f1e9369 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -12,6 +12,7 @@ TEST_PROGS += hid-ite.sh TEST_PROGS += hid-keyboard.sh TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh +TEST_PROGS += hid-sony.sh TEST_PROGS += hid-tablet.sh TEST_PROGS += hid-wacom.sh diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config index f400b8d94e3c..442a5ea16325 100644 --- a/tools/testing/selftests/hid/config +++ b/tools/testing/selftests/hid/config @@ -19,9 +19,14 @@ CONFIG_HIDRAW=y CONFIG_HID=y CONFIG_INPUT_EVDEV=y CONFIG_UHID=y +CONFIG_LEDS_CLASS_MULTICOLOR=y CONFIG_USB=y CONFIG_USB_HID=y CONFIG_HID_APPLE=y CONFIG_HID_ITE=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLAYSTATION=y +CONFIG_PLAYSTATION_FF=y +CONFIG_HID_SONY=y +CONFIG_SONY_FF=y CONFIG_HID_WACOM=y diff --git a/tools/testing/selftests/hid/hid-sony.sh b/tools/testing/selftests/hid/hid-sony.sh new file mode 100755 index 000000000000..c863c442686e --- /dev/null +++ b/tools/testing/selftests/hid/hid-sony.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_sony.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_sony.py b/tools/testing/selftests/hid/tests/test_sony.py new file mode 100644 index 000000000000..c80f50ed29d3 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_sony.py @@ -0,0 +1,282 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020 Benjamin Tissoires +# Copyright (c) 2020 Red Hat, Inc. +# + +from .base import application_matches +from .test_gamepad import BaseTest +from hidtools.device.sony_gamepad import ( + PS3Controller, + PS4ControllerBluetooth, + PS4ControllerUSB, + PS5ControllerBluetooth, + PS5ControllerUSB, + PSTouchPoint, +) +from hidtools.util import BusType + +import libevdev +import logging +import pytest + +logger = logging.getLogger("hidtools.test.sony") + +PS3_MODULE = ("sony", "hid_sony") +PS4_MODULE = ("playstation", "hid_playstation") +PS5_MODULE = ("playstation", "hid_playstation") + + +class SonyBaseTest: + class SonyTest(BaseTest.TestGamepad): + pass + + class SonyPS4ControllerTest(SonyTest): + kernel_modules = [PS4_MODULE] + + def test_accelerometer(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev("Accelerometer") + + for x in range(-32000, 32000, 4000): + r = uhdev.event(accel=(x, None, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X) in events + value = evdev.value[libevdev.EV_ABS.ABS_X] + # Check against range due to small loss in precision due + # to inverse calibration, followed by calibration by hid-sony. + assert x - 1 <= value <= x + 1 + + for y in range(-32000, 32000, 4000): + r = uhdev.event(accel=(None, y, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y) in events + value = evdev.value[libevdev.EV_ABS.ABS_Y] + assert y - 1 <= value <= y + 1 + + for z in range(-32000, 32000, 4000): + r = uhdev.event(accel=(None, None, z)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Z) in events + value = evdev.value[libevdev.EV_ABS.ABS_Z] + assert z - 1 <= value <= z + 1 + + def test_gyroscope(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev("Accelerometer") + + for rx in range(-2000000, 2000000, 200000): + r = uhdev.event(gyro=(rx, None, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RX) in events + value = evdev.value[libevdev.EV_ABS.ABS_RX] + # Sensor internal value is 16-bit, but calibrated is 22-bit, so + # 6-bit (64) difference, so allow a range of +/- 64. + assert rx - 64 <= value <= rx + 64 + + for ry in range(-2000000, 2000000, 200000): + r = uhdev.event(gyro=(None, ry, None)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RY) in events + value = evdev.value[libevdev.EV_ABS.ABS_RY] + assert ry - 64 <= value <= ry + 64 + + for rz in range(-2000000, 2000000, 200000): + r = uhdev.event(gyro=(None, None, rz)) + events = uhdev.next_sync_events("Accelerometer") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RZ) in events + value = evdev.value[libevdev.EV_ABS.ABS_RZ] + assert rz - 64 <= value <= rz + 64 + + def test_battery(self): + uhdev = self.uhdev + + assert uhdev.power_supply_class is not None + + # DS4 capacity levels are in increments of 10. + # Battery is never below 5%. + for i in range(5, 105, 10): + uhdev.battery.capacity = i + uhdev.event() + assert uhdev.power_supply_class.capacity == i + + # Discharging tests only make sense for BlueTooth. + if uhdev.bus == BusType.BLUETOOTH: + uhdev.battery.cable_connected = False + uhdev.battery.capacity = 45 + uhdev.event() + assert uhdev.power_supply_class.status == "Discharging" + + uhdev.battery.cable_connected = True + uhdev.battery.capacity = 5 + uhdev.event() + assert uhdev.power_supply_class.status == "Charging" + + uhdev.battery.capacity = 100 + uhdev.event() + assert uhdev.power_supply_class.status == "Charging" + + uhdev.battery.full = True + uhdev.event() + assert uhdev.power_supply_class.status == "Full" + + def test_mt_single_touch(self): + """send a single touch in the first slot of the device, + and release it.""" + uhdev = self.uhdev + evdev = uhdev.get_evdev("Touch Pad") + + t0 = PSTouchPoint(1, 50, 100) + r = uhdev.event(touch=[t0]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + + t0.tipswitch = False + r = uhdev.event(touch=[t0]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_mt_dual_touch(self): + """Send 2 touches in the first 2 slots. + Make sure the kernel sees this as a dual touch. + Release and check + + Note: PTP will send here BTN_DOUBLETAP emulation""" + uhdev = self.uhdev + evdev = uhdev.get_evdev("Touch Pad") + + t0 = PSTouchPoint(1, 50, 100) + t1 = PSTouchPoint(2, 150, 200) + + r = uhdev.event(touch=[t0]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + r = uhdev.event(touch=[t0, t1]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events + assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1 + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events + ) + assert ( + libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events + ) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200 + + t0.tipswitch = False + r = uhdev.event(touch=[t0, t1]) + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1 + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events + assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events + + t1.tipswitch = False + r = uhdev.event(touch=[t1]) + + events = uhdev.next_sync_events("Touch Pad") + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + +class TestPS3Controller(SonyBaseTest.SonyTest): + kernel_modules = [PS3_MODULE] + + def create_device(self): + controller = PS3Controller() + controller.application_matches = application_matches + return controller + + @pytest.fixture(autouse=True) + def start_controller(self): + # emulate a 'PS' button press to tell the kernel we are ready to accept events + self.assert_button(17) + + # drain any remaining udev events + while self.uhdev.dispatch(10): + pass + + def test_led(self): + for k, v in self.uhdev.led_classes.items(): + # the kernel might have set a LED for us + logger.info(f"{k}: {v.brightness}") + + idx = int(k[-1]) - 1 + assert self.uhdev.hw_leds.get_led(idx)[0] == bool(v.brightness) + + v.brightness = 0 + self.uhdev.dispatch(10) + assert self.uhdev.hw_leds.get_led(idx)[0] is False + + v.brightness = v.max_brightness + self.uhdev.dispatch(10) + assert self.uhdev.hw_leds.get_led(idx)[0] + + +class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest): + def create_device(self): + controller = PS4ControllerBluetooth() + controller.application_matches = application_matches + return controller + + +class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest): + def create_device(self): + controller = PS4ControllerUSB() + controller.application_matches = application_matches + return controller + + +class TestPS5ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest): + kernel_modules = [PS5_MODULE] + + def create_device(self): + controller = PS5ControllerBluetooth() + controller.application_matches = application_matches + return controller + + +class TestPS5ControllerUSB(SonyBaseTest.SonyPS4ControllerTest): + kernel_modules = [PS5_MODULE] + + def create_device(self): + controller = PS5ControllerUSB() + controller.application_matches = application_matches + return controller From patchwork Fri Feb 17 16:18:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 58703 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp981349wrn; Fri, 17 Feb 2023 08:29:08 -0800 (PST) X-Google-Smtp-Source: AK7set/6d+EnSf7Ov2xVNnmKMX/yRwILBtiHUVcVqXQ8i+fG0VrhbTn6mYs0pvI7GN+Ie8AXYdva X-Received: by 2002:a05:6402:3216:b0:4ad:f1db:18bf with SMTP id g22-20020a056402321600b004adf1db18bfmr2061231eda.6.1676651348081; Fri, 17 Feb 2023 08:29:08 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676651348; cv=none; d=google.com; s=arc-20160816; b=GDKx251aVC1BIoAWuTP3x30LiajzV/DvUxecwL6qUaesbMcrRUFfroxbt7s9hDXENh fjZ0h36N3oKomrUWBAY0Ke8P0i4Xtknw9k4lFCLbpUXmXgfWn0xi5UjarchwIfc8jyqu olCFwybXL/f37cSqTRbTYocGlwGIFI1d+yLIqhMTrd6NA0S7N6N0rjBzxxGc5AKPYBkw TShajko1oNjb4erq5M7PH3qB8aXjLnBzo6psV6zdvGGsAwMGu3yD6r8odvBpYEXa4tOe I0teZCdyXUFQfaws+z20V+VcNorPWC3TrllqTPK2l6KNEY/CKZgaBgyoL/1jPMrxXTzs klhg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=cuB7x4yeuY8QBYR6htxKdH+Dn4QUn1FKGu0L8DYt9Fg=; b=HgoYnSRzAbkuSn4jI/jjDPgm1Sa7E6JYEwL3YTZDS660DbMJq8CeG546TUeXA/pY6g 7EowQpzDTqOhZbMaVCAU+CkizTA9FDL4aTRfki1JBEtpzMyGMGvie4xMueR/d+i5n4aJ 1MS60EqakAmYbIY/ZWP7AaaETtaVVveLuVeU3JW8y9H8XS8kyqIHYMGE07wRkKlcjduM OjEjHii7ugoiG0RqmJ9/KpNWHmu+vOpWcZpJIVE+rIUlf+8p3+IKD1l0OJjkXtUzvzs7 oLwWo1UoUvlEcvMe5CQrK/MG53tRhgtkUqV848d4dEfS/nfNg7QHzTaj6TBsw2Llt4hg rQ6Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=WsSDqUb2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id c8-20020a056402120800b004accb738c57si5996945edw.130.2023.02.17.08.28.45; Fri, 17 Feb 2023 08:29:08 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=WsSDqUb2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231233AbjBQQV0 (ORCPT + 99 others); Fri, 17 Feb 2023 11:21:26 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53906 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231232AbjBQQU5 (ORCPT ); Fri, 17 Feb 2023 11:20:57 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8B63B6CA21 for ; Fri, 17 Feb 2023 08:19:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676650750; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cuB7x4yeuY8QBYR6htxKdH+Dn4QUn1FKGu0L8DYt9Fg=; b=WsSDqUb2QmKTnDFcW/7g143ZfSuvWxJXG92pWCACLSWDsDEoZQsV2vupu28h9R4S5iNorz HGOfaPWMPPufpIg01Nj4ieyeU8PQLK9yTH5ar9OB+AZrR0fF0lqBD0Kk1Og0/BuvJj17IA hTx2TTtmQoSd9/W7Xpdn3nc/pX4j7bE= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-572-ZhJleZk_PJagTQhi8A-apQ-1; Fri, 17 Feb 2023 11:19:05 -0500 X-MC-Unique: ZhJleZk_PJagTQhi8A-apQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id B43023814581; Fri, 17 Feb 2023 16:19:03 +0000 (UTC) Received: from xps-13.local (unknown [10.39.193.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id CC98DC15BA0; Fri, 17 Feb 2023 16:19:02 +0000 (UTC) From: Benjamin Tissoires Date: Fri, 17 Feb 2023 17:18:05 +0100 Subject: [PATCH 11/11] selftests: hid: import hid-tools usb-crash tests MIME-Version: 1.0 Message-Id: <20230217-import-hid-tools-tests-v1-11-d1c48590d0ee@redhat.com> References: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> In-Reply-To: <20230217-import-hid-tools-tests-v1-0-d1c48590d0ee@redhat.com> To: Jiri Kosina , Shuah Khan Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires X-Developer-Signature: v=1; a=ed25519-sha256; t=1676650715; l=6172; i=benjamin.tissoires@redhat.com; s=20230215; h=from:subject:message-id; bh=9UDXPt2tM21JKTbYVa/JCBbuyLMC72VLNcGOr7GD/fU=; b=FtnlzDnQvd1h1EqJEOMQoin+hx/F4RYlmdGncS9cJPzE7rWxQZPzoV0yj2044vaIw2S1RqA/s Y2msgJf6yzYCaLSQ1mK/f+zb799px6BKJIFJ8RzoPUc15zt9ccEhiXl X-Developer-Key: i=benjamin.tissoires@redhat.com; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758096363975492377?= X-GMAIL-MSGID: =?utf-8?q?1758096363975492377?= These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. This one gets skipped when run by vmtest.sh as we currently need to test against actual kernel modules (.ko), not built-in to fetch the list of supported devices. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 1 + tools/testing/selftests/hid/hid-usb_crash.sh | 7 ++ .../testing/selftests/hid/tests/test_usb_crash.py | 103 +++++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index dcea4f1e9369..01c0491d64da 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -14,6 +14,7 @@ TEST_PROGS += hid-mouse.sh TEST_PROGS += hid-multitouch.sh TEST_PROGS += hid-sony.sh TEST_PROGS += hid-tablet.sh +TEST_PROGS += hid-usb_crash.sh TEST_PROGS += hid-wacom.sh CXX ?= $(CROSS_COMPILE)g++ diff --git a/tools/testing/selftests/hid/hid-usb_crash.sh b/tools/testing/selftests/hid/hid-usb_crash.sh new file mode 100755 index 000000000000..3f0debe7e8fd --- /dev/null +++ b/tools/testing/selftests/hid/hid-usb_crash.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Runs tests for the HID subsystem + +export TARGET=test_usb_crash.py + +bash ./run-hid-tools-tests.sh diff --git a/tools/testing/selftests/hid/tests/test_usb_crash.py b/tools/testing/selftests/hid/tests/test_usb_crash.py new file mode 100644 index 000000000000..e98bff9197c7 --- /dev/null +++ b/tools/testing/selftests/hid/tests/test_usb_crash.py @@ -0,0 +1,103 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021 Benjamin Tissoires +# Copyright (c) 2021 Red Hat, Inc. +# + +# This is to ensure we don't crash when emulating USB devices + +from . import base +import pytest +import logging + +logger = logging.getLogger("hidtools.test.usb") + + +class USBDev(base.UHIDTestDevice): + # fmt: off + report_descriptor = [ + 0x05, 0x01, # .Usage Page (Generic Desktop) 0 + 0x09, 0x02, # .Usage (Mouse) 2 + 0xa1, 0x01, # .Collection (Application) 4 + 0x09, 0x02, # ..Usage (Mouse) 6 + 0xa1, 0x02, # ..Collection (Logical) 8 + 0x09, 0x01, # ...Usage (Pointer) 10 + 0xa1, 0x00, # ...Collection (Physical) 12 + 0x05, 0x09, # ....Usage Page (Button) 14 + 0x19, 0x01, # ....Usage Minimum (1) 16 + 0x29, 0x03, # ....Usage Maximum (3) 18 + 0x15, 0x00, # ....Logical Minimum (0) 20 + 0x25, 0x01, # ....Logical Maximum (1) 22 + 0x75, 0x01, # ....Report Size (1) 24 + 0x95, 0x03, # ....Report Count (3) 26 + 0x81, 0x02, # ....Input (Data,Var,Abs) 28 + 0x75, 0x05, # ....Report Size (5) 30 + 0x95, 0x01, # ....Report Count (1) 32 + 0x81, 0x03, # ....Input (Cnst,Var,Abs) 34 + 0x05, 0x01, # ....Usage Page (Generic Desktop) 36 + 0x09, 0x30, # ....Usage (X) 38 + 0x09, 0x31, # ....Usage (Y) 40 + 0x15, 0x81, # ....Logical Minimum (-127) 42 + 0x25, 0x7f, # ....Logical Maximum (127) 44 + 0x75, 0x08, # ....Report Size (8) 46 + 0x95, 0x02, # ....Report Count (2) 48 + 0x81, 0x06, # ....Input (Data,Var,Rel) 50 + 0xc0, # ...End Collection 52 + 0xc0, # ..End Collection 53 + 0xc0, # .End Collection 54 + ] + # fmt: on + + def __init__(self, name=None, input_info=None): + super().__init__( + name, "Mouse", input_info=input_info, rdesc=USBDev.report_descriptor + ) + + # skip witing for udev events, it's likely that the report + # descriptor is wrong + def is_ready(self): + return True + + # we don't have an evdev node here, so paper over + # the checks + def get_evdev(self, application=None): + return "OK" + + +class TestUSBDevice(base.BaseTestCase.TestUhid): + """ + Test class to test if an emulated USB device crashes + the kernel. + """ + + # conftest.py is generating the following fixture: + # + # @pytest.fixture(params=[('modulename', 1, 2)]) + # def usbVidPid(self, request): + # return request.param + + @pytest.fixture() + def new_uhdev(self, usbVidPid, request): + self.module, self.vid, self.pid = usbVidPid + self._load_kernel_module(None, self.module) + return USBDev(input_info=(3, self.vid, self.pid)) + + def test_creation(self): + """ + inject the USB dev through uhid and immediately see if there is a crash: + + uhid can create a USB device with the BUS_USB bus, and some + drivers assume that they can then access USB related structures + when they are actually provided a uhid device. This leads to + a crash because those access result in a segmentation fault. + + The kernel should not crash on any (random) user space correct + use of its API. So run through all available modules and declared + devices to see if we can generate a uhid device without a crash. + + The test is empty as the fixture `check_taint` is doing the job (and + honestly, when the kernel crashes, the whole machine freezes). + """ + assert True