[systemd-devel] [PATCH udev] udev: Add program/rule to export touchscreen/tablet size as udev properties
David Herrmann
dh.herrmann at gmail.com
Thu Dec 18 07:15:05 PST 2014
Hi
On Thu, Dec 18, 2014 at 3:58 PM, Carlos Garnacho <carlosg at gnome.org> wrote:
> This rule is only run on tablet/touchscreen devices, and extracts their size
> in millimeters, as it can be found out through their struct input_absinfo.
>
> Conceivably, that information can be changed through EVIOCSABS anywhere
> else, but we're only interested in values prior to any calibration, this
> rule is thus only run on "add", and no tracking of changes is performed.
> This should only remain a problem if calibration were automatically applied
> by an earlier udev rule (read: don't).
> ---
> Makefile.am | 14 +++
> rules/60-input_abs_size.rules | 8 ++
> src/udev/input_abs_size/input_abs_size.c | 177 +++++++++++++++++++++++++++++++
> 3 files changed, 199 insertions(+)
> create mode 100644 rules/60-input_abs_size.rules
> create mode 100644 src/udev/input_abs_size/input_abs_size.c
>
> diff --git a/Makefile.am b/Makefile.am
> index ab07d3b..a23705a 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -3741,6 +3741,20 @@ dist_udevrules_DATA += \
> rules/61-accelerometer.rules
>
> # ------------------------------------------------------------------------------
> +input_abs_size_SOURCES = \
> + src/udev/input_abs_size/input_abs_size.c
> +
> +input_abs_size_LDADD = \
> + libudev-internal.la -lm \
> + libsystemd-shared.la
> +
> +udevlibexec_PROGRAMS += \
> + input_abs_size_
> +
> +dist_udevrules_DATA += \
> + rules/60-input_abs_size.rules
> +
> +# ------------------------------------------------------------------------------
> if ENABLE_GUDEV
> if ENABLE_GTK_DOC
> SUBDIRS += \
> diff --git a/rules/60-input_abs_size.rules b/rules/60-input_abs_size.rules
> new file mode 100644
> index 0000000..5f613ac
> --- /dev/null
> +++ b/rules/60-input_abs_size.rules
> @@ -0,0 +1,8 @@
> +# do not edit this file, it will be overwritten on update
> +
> +ACTION!="add", GOTO="input_abs_size_end"
> +
> +ENV{ID_INPUT_TOUCHSCREEN}=="1", IMPORT{program}="input_abs_size %p"
> +ENV{ID_INPUT_TABLET}=="1", IMPORT{program}="input_abs_size %p"
> +
> +LABEL="input_abs_size_end"
> diff --git a/src/udev/input_abs_size/input_abs_size.c b/src/udev/input_abs_size/input_abs_size.c
> new file mode 100644
> index 0000000..34a6bf3
> --- /dev/null
> +++ b/src/udev/input_abs_size/input_abs_size.c
> @@ -0,0 +1,177 @@
> +/*
> + * input_abs_size - extracts abs X/Y size in millimeters from input devices
> + *
> + * Copyright (C) 2014 Red Hat
> + * Author:
> + * Carlos Garnacho <carlosg at gnome.org>
> + *
> + * 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 keymap; if not, write to the Free Software Foundation,
> + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <math.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <getopt.h>
> +#include <limits.h>
> +#include <linux/limits.h>
> +#include <linux/input.h>
> +
> +#include "libudev.h"
> +#include "libudev-private.h"
> +
> +/* Resolution is defined to be in units/mm for ABS_X/Y */
> +#define ABS_SIZE_MM(absinfo) ((absinfo.maximum - absinfo.minimum) / absinfo.resolution)
> +
> +static void extract_info(struct udev *udev,
> + struct udev_device *dev,
> + const char *devpath)
> +{
> + struct input_absinfo xabsinfo = { 0 }, yabsinfo = { 0 };
> + _cleanup_close_ int fd = -1;
> + char text[1024];
> +
> + if ((fd = open(devpath, O_RDONLY)) < 0)
> + return;
> +
> + if (ioctl(fd, EVIOCGABS(ABS_X), &xabsinfo) < 0 ||
> + ioctl(fd, EVIOCGABS(ABS_Y), &yabsinfo) < 0)
> + return;
> +
> + if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0)
> + return;
> +
> + snprintf (text, sizeof(text),
> + "ID_INPUT_WIDTH_MM=%d\n"
> + "ID_INPUT_HEIGHT_MM=%d",
> + ABS_SIZE_MM (xabsinfo),
> + ABS_SIZE_MM (yabsinfo));
> + puts (text);
> +}
> +
> +static void help(void)
> +{
> + printf("Usage: input_abs_size [options] <device path>\n"
> + " --debug debug to stderr\n"
> + " --help print this help text\n\n");
> +}
> +
> +int main(int argc, char** argv)
> +{
> + struct udev *udev;
> + struct udev_device *dev;
> +
> + static const struct option options[] = {
> + { "debug", no_argument, NULL, 'd' },
> + { "help", no_argument, NULL, 'h' },
> + {}
> + };
> +
> + char devpath[PATH_MAX];
> + char *devnode;
> + struct udev_enumerate *enumerate;
> + struct udev_list_entry *list_entry;
> +
> + udev = udev_new();
> + if (udev == NULL)
> + return 1;
> +
> + log_parse_environment();
> + log_open();
> +
> + /* CLI argument parsing */
> + while (1) {
> + int option;
> +
> + option = getopt_long(argc, argv, "dxh", options, NULL);
> + if (option == -1)
> + break;
> +
> + switch (option) {
> + case 'd':
> + log_set_target(LOG_TARGET_CONSOLE);
> + log_set_max_level(LOG_DEBUG);
> + log_open();
> + break;
> + case 'h':
> + help();
> + exit(0);
> + default:
> + exit(1);
> + }
> + }
> +
> + if (argv[optind] == NULL) {
> + help();
> + exit(1);
> + }
> +
> + /* get the device */
> + snprintf(devpath, sizeof(devpath), "/sys/%s", argv[optind]);
> + dev = udev_device_new_from_syspath(udev, devpath);
> + if (dev == NULL) {
> + fprintf(stderr, "unable to access '%s'\n", devpath);
> + return 1;
> + }
> +
> + /* Get the children devices and find the devnode */
> + devnode = NULL;
> + enumerate = udev_enumerate_new(udev);
> + udev_enumerate_add_match_parent(enumerate, dev);
> + udev_enumerate_scan_devices(enumerate);
> + udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
Who guarantees that there are any children at the time you run this
program? In fact, you run this program immediately once the input
device is found, but the evdev device might get registered later. On
modern machines, the kernel is fast enough to register both before any
uevent can get scheduled. However, there is no such guarantee! I
dislike relying on such races to not hit us..
Regardless of that race: What is the point of exposing that
information? Why would any process but a reader of the evdev device be
interested in this? Your commit message lacks any explanation why this
interface is useful. Please elaborate!
Thanks
David
> + struct udev_device *device;
> + const char *node;
> +
> + device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate),
> + udev_list_entry_get_name(list_entry));
> + if (device == NULL)
> + continue;
> + /* Already found it */
> + if (devnode != NULL) {
> + udev_device_unref(device);
> + continue;
> + }
> +
> + node = udev_device_get_devnode(device);
> + if (node == NULL) {
> + udev_device_unref(device);
> + continue;
> + }
> + /* Use the event sub-device */
> + if (strstr(node, "/event") == NULL) {
> + udev_device_unref(device);
> + continue;
> + }
> +
> + devnode = strdup(node);
> + udev_device_unref(device);
> + }
> +
> + if (devnode == NULL) {
> + fprintf(stderr, "unable to get device node for '%s'\n", devpath);
> + return 0;
> + }
> +
> + log_debug("Opening input device %s\n", devnode);
> + extract_info(udev, dev, devnode);
> + free(devnode);
> +
> + return 0;
> +}
> --
> 2.1.0
>
> _______________________________________________
> systemd-devel mailing list
> systemd-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/systemd-devel
More information about the systemd-devel
mailing list