[PATCH libevdev] tools: add a tool to change kernel devices

Peter Hutterer peter.hutterer at who-t.net
Tue Jan 6 14:19:56 PST 2015


Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
I needed this to manually change the resolution on uinput devices for tests, so I
figured let's make it a bit more generic. Not a lot of error checking is
performed, we can do this later if needed. For now it's pretty much assumed
that you know what you're doing when you're running this.

 tools/.gitignore              |   1 +
 tools/Makefile.am             |   7 +-
 tools/libevdev-tweak-device.1 |  59 ++++++++++
 tools/libevdev-tweak-device.c | 255 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 321 insertions(+), 1 deletion(-)
 create mode 100644 tools/libevdev-tweak-device.1
 create mode 100644 tools/libevdev-tweak-device.c

diff --git a/tools/.gitignore b/tools/.gitignore
index 292c08e..c2a2645 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -1,3 +1,4 @@
 libevdev-events
 touchpad-edge-detector
 mouse-dpi-tool
+libevdev-tweak-device
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 8e3950d..d699c5c 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,7 +1,8 @@
 noinst_PROGRAMS = libevdev-events
 bin_PROGRAMS = \
 	       touchpad-edge-detector \
-	       mouse-dpi-tool
+	       mouse-dpi-tool \
+	       libevdev-tweak-device
 
 AM_CPPFLAGS = $(GCC_CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/libevdev
 libevdev_ldadd = $(top_builddir)/libevdev/libevdev.la
@@ -14,3 +15,7 @@ touchpad_edge_detector_LDADD = $(libevdev_ldadd)
 
 mouse_dpi_tool_SOURCES = mouse-dpi-tool.c
 mouse_dpi_tool_LDADD = $(libevdev_ldadd)
+
+libevdev_tweak_device_SOURCES = libevdev-tweak-device.c
+libevdev_tweak_device_LDADD = $(libevdev_ldadd)
+libevdev_tweak_device_MANS = libevdev-tweak-device.1
diff --git a/tools/libevdev-tweak-device.1 b/tools/libevdev-tweak-device.1
new file mode 100644
index 0000000..0d2c1ac
--- /dev/null
+++ b/tools/libevdev-tweak-device.1
@@ -0,0 +1,59 @@
+.TH LIBEVDEV-TWEAK-DEVICE "1" 
+.SH NAME
+libevdev-tweak-device \- modify an evdev kernel device
+.SH SYNOPSIS
+.B libevdev-tweak-device
+--abs ABS_X [--min a] [--max b] [--res c] [--fuzz d] [--flat e]
+/dev/input/eventX
+.PP
+.B libevdev-tweak-device
+--led LED_NUML --on|--off /dev/input/eventX
+.SH DESCRIPTION
+.PP
+The
+.I libevdev-tweak-device
+tool changes the properties of the evdev kernel device at
+.I /dev/input/eventX.
+Currently this may be used to force an LED on or off, or to change the
+properties of an absolute axis (e.g. its minimum/maximum range or
+resolution). Changes are permanent until the device is removed.
+.SH OPTIONS
+.SS Changing absolute axes
+.TP 8
+.B --abs axis
+Change the given named ABS_ kernel axis, e.g. ABS_X. For a full list, see linux/input.h.
+Each of the options
+.B min, max, res, fuzz, flat
+may be given.
+.TP 8
+.B --min v
+Set the absinfo minimum to the value v
+.TP 8
+.B --max v
+Set the absinfo maximum to the value v
+.TP 8
+.B --res v
+Set the absinfo resolution to the value v
+.TP 8
+.B --fuzz v
+Set the absinfo fuzz to the value v
+.TP 8
+.B --flat v
+Set the absinfo flat to the value v
+.PP
+.SS Toggling LEDs
+.TP 8
+.B --led led
+Change the given LED, e.g. LED_NUML. For a full list, see linux/input.h.
+.TP 8
+.B --on
+Change the LED state to on
+.TP 8
+.B --off
+Change the LED state to off
+.SH NOTES
+.PP
+The kernel does not notify processes about absinfo property changes. Any
+process that has previously obtained the absinfo from the device will remain
+on the old information. This makes using this tool potentially racy, use
+with caution.
diff --git a/tools/libevdev-tweak-device.c b/tools/libevdev-tweak-device.c
new file mode 100644
index 0000000..a482d12
--- /dev/null
+++ b/tools/libevdev-tweak-device.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright © 2014 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include <config.h>
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#include "libevdev.h"
+
+static unsigned int changes; /* bitmask of changes */
+static struct input_absinfo absinfo;
+static int axis;
+static int led;
+static int led_state = -1;
+const char *path;
+
+static void
+usage(void)
+{
+	printf("%s --abs <axis> [--min min] [--max max] [--res res] [--fuzz fuzz] [--flat flat] /dev/input/eventXYZ\n"
+	       "\tChange the absinfo struct for the named axis\n"
+	       "%s --led <led> --on|--off /dev/input/eventXYZ\n"
+	       "\tEnable or disable the named LED\n",
+	       program_invocation_short_name, program_invocation_short_name);
+}
+
+enum opts {
+	OPT_ABS = 1 << 0,
+	OPT_MIN = 1 << 1,
+	OPT_MAX = 1 << 2,
+	OPT_FUZZ = 1 << 3,
+	OPT_FLAT = 1 << 4,
+	OPT_RES = 1 << 5,
+	OPT_LED = 1 << 6,
+	OPT_ON = 1 << 7,
+	OPT_OFF = 1 << 8,
+	OPT_HELP = 1 << 9,
+};
+
+static int
+parse_options(int argc, char **argv)
+{
+	int rc = 1;
+	int c;
+	int option_index = 0;
+	static struct option opts[] = {
+		{ "abs", 1, 0, OPT_ABS },
+		{ "min", 1, 0, OPT_MIN },
+		{ "max", 1, 0, OPT_MAX },
+		{ "fuzz", 1, 0, OPT_FUZZ },
+		{ "flat", 1, 0, OPT_FLAT },
+		{ "res", 1, 0, OPT_RES },
+		{ "led", 1, 0, OPT_LED },
+		{ "on", 0, 0, OPT_ON },
+		{ "off", 0, 0, OPT_OFF },
+		{ "help", 0, 0, OPT_HELP },
+		{ NULL, 0, 0, 0 },
+	};
+
+	if (argc < 2)
+		goto error;
+
+	while (1) {
+		c = getopt_long(argc, argv, "h", opts, &option_index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+			case 'h':
+			case OPT_HELP:
+				goto error;
+			case OPT_ABS:
+				if (changes & OPT_LED)
+					goto error;
+
+				axis = libevdev_event_code_from_name(EV_ABS,
+								     optarg);
+				if (axis == -1)
+					goto error;
+				break;
+			case OPT_LED:
+				if (changes & OPT_ABS)
+					goto error;
+
+				led = libevdev_event_code_from_name(EV_LED,
+								    optarg);
+				if (led == -1)
+					goto error;
+				break;
+			case OPT_MIN:
+				absinfo.minimum = atoi(optarg);
+				break;
+			case OPT_MAX:
+				absinfo.maximum = atoi(optarg);
+				break;
+			case OPT_FUZZ:
+				absinfo.fuzz = atoi(optarg);
+				break;
+			case OPT_FLAT:
+				absinfo.flat = atoi(optarg);
+				break;
+			case OPT_RES:
+				absinfo.resolution = atoi(optarg);
+				break;
+			case OPT_ON:
+				if (led_state != -1)
+					goto error;
+				led_state = 1;
+				break;
+			case OPT_OFF:
+				if (led_state != -1)
+					goto error;
+				led_state = 0;
+				break;
+			default:
+				goto error;
+		}
+		changes |= c;
+	}
+
+	if (optind >= argc)
+		goto error;
+	path = argv[optind];
+
+	rc = 0;
+error:
+	return rc;
+}
+
+static void
+set_abs(struct libevdev *dev)
+{
+	int rc;
+	struct input_absinfo abs;
+	const struct input_absinfo *a;
+
+	if ((a = libevdev_get_abs_info(dev, axis)) == NULL) {
+		fprintf(stderr,
+			"Device '%s' doesn't have axis %s\n",
+			libevdev_get_name(dev),
+			libevdev_event_code_get_name(EV_ABS, axis));
+		return;
+	}
+
+	abs = *a;
+	if (changes & OPT_MIN)
+		abs.minimum = absinfo.minimum;
+	if (changes & OPT_MAX)
+		abs.maximum = absinfo.maximum;
+	if (changes & OPT_FUZZ)
+		abs.fuzz = absinfo.fuzz;
+	if (changes & OPT_FLAT)
+		abs.flat = absinfo.flat;
+	if (changes & OPT_RES)
+		abs.resolution = absinfo.resolution;
+
+	rc = libevdev_kernel_set_abs_info(dev, axis, &abs);
+	if (rc != 0)
+		fprintf(stderr,
+			"Failed to set absinfo %s: %s",
+			libevdev_event_code_get_name(EV_ABS, axis),
+			strerror(-rc));
+}
+
+static void
+set_led(struct libevdev *dev)
+{
+	int rc;
+	enum libevdev_led_value state =
+		led_state ? LIBEVDEV_LED_ON : LIBEVDEV_LED_OFF;
+
+	if (!libevdev_has_event_code(dev, EV_LED, led)) {
+		fprintf(stderr,
+			"Device '%s' doesn't have %s\n",
+			libevdev_get_name(dev),
+			libevdev_event_code_get_name(EV_LED, axis));
+		return;
+	}
+
+
+	rc = libevdev_kernel_set_led_value(dev, led, state);
+	if (rc != 0)
+		fprintf(stderr,
+			"Failed to set LED %s: %s",
+			libevdev_event_code_get_name(EV_LED, led),
+			strerror(-rc));
+}
+
+int
+main(int argc, char **argv)
+{
+	struct libevdev *dev = NULL;
+	int fd;
+	int rc = 1;
+
+	rc = parse_options(argc, argv);
+	if (rc != 0 || !path) {
+		usage();
+		goto out;
+	}
+
+	fd = open(path, O_RDWR);
+	if (fd < 0) {
+		perror("Failed to open device");
+		goto out;
+	}
+
+	rc = libevdev_new_from_fd(fd, &dev);
+	if (rc < 0) {
+		fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc));
+		goto out;
+	}
+
+	if (changes & OPT_ABS)
+		set_abs(dev);
+	else if (changes & OPT_LED)
+		set_led(dev);
+	else
+		fprintf(stderr,
+			"++?????++ Out of Cheese Error. Redo From Start.\n");
+
+out:
+	libevdev_free(dev);
+
+	return rc;
+}
-- 
2.1.0



More information about the Input-tools mailing list