Received: by 2002:a05:6358:3188:b0:123:57c1:9b43 with SMTP id q8csp982276rwd; Wed, 7 Jun 2023 09:19:56 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7zJtL6leuP5KtlfV5eTvfJ32mjmN9vSYLTDB6YfSoLtK8cPs8dQjSg/h/VhqUXh53jqggd X-Received: by 2002:a05:6a00:179d:b0:64d:b0d8:a396 with SMTP id s29-20020a056a00179d00b0064db0d8a396mr2911075pfg.7.1686154796424; Wed, 07 Jun 2023 09:19:56 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686154796; cv=none; d=google.com; s=arc-20160816; b=s32rsdHExQFd8dNXkzV+Umm7HgFgr0lU5H7uo7M2Zy7OV9hxN/vadsQ2K817/EOKEz 0ecZQfKL5r+YPwmA2X0uSQFC0dKjuMo+8OF3GDG2oVFMjsSILgsABJxZZATqqVnUmM2C cHhgleyfVL8W4UzpMIb5qKzaaSiNtTyFOymIZy6wRPNCsg8ofmmM6OYQb94bko7xVz7u ySuuZZQFjjaH0doF5KKtG8UFGo5g/E4SEe82NU8IVQ7d5waxzjpFXK2X9nwCYlNZLuEh PCbhCEF0nZKWM4rwHVaFGPkXLuDRoFQ7+Ur8ejLUKynUEHK0HQTF38M/sDy6MLTyk67u tFTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=9TGoSQ+F4iWdsnxfTzcIIh2K7H1QkAxRMMhVlF/TgVQ=; b=W7SH5gop/RbWrUnR7JiPYd8waaLMIBTUxGxMtJ6S7jeWHTny0Z1/haSl0BSEX1pwU7 h3sVqg7SeBYcXboDu7x4fPaDcGy0zw/IQv9NEGU3SC9dwQjSbJt2W6Hvcb3ssJIchnM/ QAHVBKSc62gfDJD2qnvpqBSTUlaKjVphq09Ewu9Q1bf4L8AyfO+ba1Ep0LpWWv3K9oUM MnDGEGdIquVsVJZRKi1uhwi/yIqIFxFe5h6u+eUU20Vhz+eR82MdpKxVQvQ5VnB0MN0w vSLSswVZy9lkxwiMJsZBLmueJXj6Tn/FrBPUS708OER1L5Q/QCwKFUL+NqqWoaTBiUZh HWKw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=r5cyogrj; 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=kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id a18-20020aa78e92000000b0063b68fa0807si9086087pfr.263.2023.06.07.09.19.42; Wed, 07 Jun 2023 09:19:56 -0700 (PDT) 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=@kernel.org header.s=k20201202 header.b=r5cyogrj; 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=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241079AbjFGPcb (ORCPT + 99 others); Wed, 7 Jun 2023 11:32:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44572 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241052AbjFGPc2 (ORCPT ); Wed, 7 Jun 2023 11:32:28 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7899F188; Wed, 7 Jun 2023 08:32:25 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 062B961790; Wed, 7 Jun 2023 15:32:25 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 39A36C433D2; Wed, 7 Jun 2023 15:32:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1686151944; bh=5hhZRxX2WWgW0sPLPamoyhkZr9xNbcuFiVquE58MnA4=; h=From:To:Cc:Subject:Date:From; b=r5cyogrjCKLhqXI1ii7LXr6kUxd70C1Souo5RNpulj79nqY/szsqwu/hmhlkRJtLP p1PYr5K4SQCY2C3RpCf2rVbMpxEScHPTRkJTU5iRRJB1JnDapPmTB3db60HSEQmFnR SQGQTGPXu7aJaYaCXBAlCvm4Zc/lQCgxuhivyAPtfy9U2vyCE7R6+ALFgEBdI5oO2m dVWXdgbiJtIjfOALESgsxfPADGBLRylbPGBxe0hEaaH1U2UGnumSyxsaKbw2MDCxdC 4LUuI5x5qJHo6uQ5o9Q/sdkyaAb0ZMFCUjhhZU7I4woVquAzJNKwt2mvCR56wF4qXX 75kGjRN+qPsAA== From: Enric Balletbo i Serra To: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Shuah Khan , Dmitry Torokhov , Javier Martinez Canillas , Dana Elfassy , linux-input@vger.kernel.org, phuttere@redhat.com, Benjamin Tissoires , Enric Balletbo i Serra , Muhammad Usama Anjum Subject: [PATCH v3] selftests/input: Introduce basic tests for evdev ioctls Date: Wed, 7 Jun 2023 17:32:14 +0200 Message-Id: <20230607153214.15933-1-eballetbo@kernel.org> X-Mailer: git-send-email 2.40.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED 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 This provides a basic infrastructure for the creation of tests for the evdev interface. Most of this code is adapted from the libevdev wrapper library. While most of evdev ioctls are covered and tested using libevdev tests there are some evdev ioctls that aren't because are not supported (and will not be supported) by libevdev [1]. So, adding, at least those tests, would make sense. The test creates an uinput device (and an evdev device) so you can call the wanted ioctl from userspace. So, to run those tests you need to have support for uinput and evdev as well. [1] For example, libevdev doesn't support setting EV_REP because it's inherently racy - one libevdev context to set those values via the ioctl would cause all other libevdev contexts on the same device to be out of sync. Since we do not get notifications when the values changed, libevdev's buffered values for EV_REP will remain whatever they were initially. Signed-off-by: Enric Balletbo i Serra Acked-by: Muhammad Usama Anjum --- Test output: TAP version 13 1..3 # Starting 3 tests from 1 test cases. # RUN global.eviocgname_get_device_name ... # OK global.eviocgname_get_device_name ok 1 global.eviocgname_get_device_name # RUN global.eviocgrep_get_repeat_settings ... # OK global.eviocgrep_get_repeat_settings ok 2 global.eviocgrep_get_repeat_settings # RUN global.eviocsrep_set_repeat_settings ... # OK global.eviocsrep_set_repeat_settings ok 3 global.eviocsrep_set_repeat_settings # PASSED: 3 / 3 tests passed. # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 Changes since v1: - As UI_GET_SYSNAME has been around 3.15 remove the fallback code that pre-dates that ioctl. - Fix a bug in the va-args handling in selftest_uinput_create_device calls. - Fix typo s/an/and - Implement a test case for EVIOCSREP. Changes since v2: - Fix a buf left in the va-args handling in selftest_uinput_create_device calls. - Replace fetch_syspath_and_devnode() function to open_devnode() function that return the openend file descriptor, so we can reduce the code. tools/testing/selftests/Makefile | 1 + tools/testing/selftests/input/.gitignore | 2 + tools/testing/selftests/input/Makefile | 5 + tools/testing/selftests/input/config | 3 + tools/testing/selftests/input/evioc-test.c | 237 +++++++++++++++++++++ 5 files changed, 248 insertions(+) create mode 100644 tools/testing/selftests/input/.gitignore create mode 100644 tools/testing/selftests/input/Makefile create mode 100644 tools/testing/selftests/input/config create mode 100644 tools/testing/selftests/input/evioc-test.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 90a62cf75008..29fc77168aa7 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -28,6 +28,7 @@ TARGETS += futex TARGETS += gpio TARGETS += hid TARGETS += intel_pstate +TARGETS += input TARGETS += iommu TARGETS += ipc TARGETS += ir diff --git a/tools/testing/selftests/input/.gitignore b/tools/testing/selftests/input/.gitignore new file mode 100644 index 000000000000..37f5dff3255b --- /dev/null +++ b/tools/testing/selftests/input/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +evioc-test diff --git a/tools/testing/selftests/input/Makefile b/tools/testing/selftests/input/Makefile new file mode 100644 index 000000000000..031729be0628 --- /dev/null +++ b/tools/testing/selftests/input/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +CFLAGS += -D_GNU_SOURCE -std=gnu99 -Wl,-no-as-needed -Wall $(KHDR_INCLUDES) + +TEST_GEN_PROGS := evioc-test +include ../lib.mk diff --git a/tools/testing/selftests/input/config b/tools/testing/selftests/input/config new file mode 100644 index 000000000000..b7512f3e6d8d --- /dev/null +++ b/tools/testing/selftests/input/config @@ -0,0 +1,3 @@ +CONFIG_INPUT=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_UINPUT=m diff --git a/tools/testing/selftests/input/evioc-test.c b/tools/testing/selftests/input/evioc-test.c new file mode 100644 index 000000000000..ad7b93fe39cf --- /dev/null +++ b/tools/testing/selftests/input/evioc-test.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Red Hat, Inc. + * + * Part of the code in this file is inspired and copied from the libevdev wrapper library + * for evdev devices written by Peter Hutterer. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest_harness.h" + +#define TEST_DEVICE_NAME "selftest input device" + +struct selftest_uinput { + int uinput_fd; /** file descriptor to uinput */ + int evdev_fd; /** file descriptor to evdev */ +}; + +static int is_event_device(const struct dirent *dent) +{ + return strncmp("event", dent->d_name, 5) == 0; +} + +static int open_devnode(struct selftest_uinput *uidev) +{ +#define SYS_INPUT_DIR "/sys/devices/virtual/input/" + char buf[sizeof(SYS_INPUT_DIR) + 64] = SYS_INPUT_DIR; + struct dirent **namelist; + char *devnode = NULL; + int ndev, i; + int rc; + + rc = ioctl(uidev->uinput_fd, + UI_GET_SYSNAME(sizeof(buf) - strlen(SYS_INPUT_DIR)), + &buf[strlen(SYS_INPUT_DIR)]); + if (rc == -1) { + fprintf(stderr, "cannot get the sysfs name of the uinput device (%d)\n", rc); + return rc; + } + + ndev = scandir(buf, &namelist, is_event_device, alphasort); + if (ndev <= 0) + return -1; + + /* ndev should only ever be 1 */ + + for (i = 0; i < ndev; i++) { + if (!devnode && asprintf(&devnode, "/dev/input/%s", + namelist[i]->d_name) == -1) + devnode = NULL; + free(namelist[i]); + } + + free(namelist); + + return open(devnode, O_RDONLY); +#undef SYS_INPUT_DIR +} + +static void selftest_uinput_destroy(struct selftest_uinput *uidev) +{ + if (!uidev) + return; + + if (uidev->uinput_fd >= 0) + ioctl(uidev->uinput_fd, UI_DEV_DESTROY, NULL); + + close(uidev->evdev_fd); + close(uidev->uinput_fd); + + free(uidev); +} + +static int selftest_uinput_create_device(struct selftest_uinput **uidev, ...) +{ + struct selftest_uinput *new_device; + struct uinput_setup setup; + va_list args; + int rc, fd; + int type; + + new_device = calloc(1, sizeof(struct selftest_uinput)); + if (!new_device) + return -ENOMEM; + + memset(&setup, 0, sizeof(setup)); + strncpy(setup.name, TEST_DEVICE_NAME, UINPUT_MAX_NAME_SIZE - 1); + setup.id.vendor = 0x1234; /* sample vendor */ + setup.id.product = 0x5678; /* sample product */ + setup.id.bustype = BUS_USB; + + fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "cannot open uinput (%d): %m\n", errno); + goto error; + } + + va_start(args, uidev); + rc = 0; + do { + type = va_arg(args, int); + if (type == -1) + break; + rc = ioctl(fd, UI_SET_EVBIT, type); + } while (rc == 0); + va_end(args); + + rc = ioctl(fd, UI_DEV_SETUP, &setup); + if (rc == -1) + goto error; + + rc = ioctl(fd, UI_DEV_CREATE, NULL); + if (rc == -1) + goto error; + + new_device->uinput_fd = fd; + + fd = open_devnode(new_device); + if (fd < 0) { + fprintf(stderr, "cannot open event device node (%d): %m\n", errno); + goto error; + } + + new_device->evdev_fd = fd; + *uidev = new_device; + + return 0; + +error: + rc = -errno; + selftest_uinput_destroy(new_device); + return rc; +} + +TEST(eviocgname_get_device_name) +{ + struct selftest_uinput *uidev; + char buf[256]; + int rc; + + rc = selftest_uinput_create_device(&uidev); + ASSERT_EQ(0, rc); + ASSERT_NE(NULL, uidev); + + memset(buf, 0, sizeof(buf)); + /* ioctl to get the name */ + rc = ioctl(uidev->evdev_fd, EVIOCGNAME(sizeof(buf) - 1), buf); + ASSERT_GE(rc, 0); + ASSERT_STREQ(TEST_DEVICE_NAME, buf); + + selftest_uinput_destroy(uidev); +} + +TEST(eviocgrep_get_repeat_settings) +{ + struct selftest_uinput *uidev; + int rep_values[2]; + int rc; + + memset(rep_values, 0, sizeof(rep_values)); + + rc = selftest_uinput_create_device(&uidev, -1); + ASSERT_EQ(0, rc); + ASSERT_NE(NULL, uidev); + + /* ioctl to get the repeat rates values */ + rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values); + /* should fail because EV_REP is not set */ + ASSERT_EQ(-1, rc); + + selftest_uinput_destroy(uidev); + + rc = selftest_uinput_create_device(&uidev, EV_REP, -1); + ASSERT_EQ(0, rc); + ASSERT_NE(NULL, uidev); + + /* ioctl to get the repeat rates values */ + rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values); + ASSERT_EQ(0, rc); + /* should get the default delay and period values set by the kernel */ + ASSERT_EQ(rep_values[0], 250); + ASSERT_EQ(rep_values[1], 33); + + selftest_uinput_destroy(uidev); +} + +TEST(eviocsrep_set_repeat_settings) +{ + struct selftest_uinput *uidev; + int rep_values[2]; + int rc; + + memset(rep_values, 0, sizeof(rep_values)); + + rc = selftest_uinput_create_device(&uidev, -1); + ASSERT_EQ(0, rc); + ASSERT_NE(NULL, uidev); + + /* ioctl to set the repeat rates values */ + rc = ioctl(uidev->evdev_fd, EVIOCSREP, rep_values); + /* should fail because EV_REP is not set */ + ASSERT_EQ(-1, rc); + + selftest_uinput_destroy(uidev); + + rc = selftest_uinput_create_device(&uidev, EV_REP, -1); + ASSERT_EQ(0, rc); + ASSERT_NE(NULL, uidev); + + /* ioctl to set the repeat rates values */ + rep_values[0] = 500; + rep_values[1] = 200; + rc = ioctl(uidev->evdev_fd, EVIOCSREP, rep_values); + ASSERT_EQ(0, rc); + + /* ioctl to get the repeat rates values */ + rc = ioctl(uidev->evdev_fd, EVIOCGREP, rep_values); + ASSERT_EQ(0, rc); + /* should get the delay and period values set previously */ + ASSERT_EQ(rep_values[0], 500); + ASSERT_EQ(rep_values[1], 200); + + selftest_uinput_destroy(uidev); +} + +TEST_HARNESS_MAIN -- 2.40.1