[PATCH libevdev 1/6] Add support for uinput device creation

David Herrmann dh.herrmann at gmail.com
Wed Aug 14 06:05:33 PDT 2013


Hi

On Wed, Aug 14, 2013 at 2:45 PM, Benjamin Tissoires
<benjamin.tissoires at gmail.com> wrote:
> On Tue, Aug 13, 2013 at 12:39 PM, Peter Hutterer
> <peter.hutterer at who-t.net> wrote:
>> This lets libevdev provide a relatively generic interface for the
>> creation of uinput devices so we don't need to duplicate this across
>> multiple projects.
>>
>> Most of this is lifted from the current test implementation, with a
>> couple of minor changes.
>>
>> EV_REP needs special handling:
>> Kernel allows to set the EV_REP bit, it doesn't set REP_* bits (which we
>> wrap anyway) but it will also set the default values (500, 33).
>>
>> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
>> ---
>>  doc/libevdev.doxygen.in        |   3 +-
>>  libevdev/Makefile.am           |   5 +-
>>  libevdev/libevdev-uinput-int.h |  29 +++++
>>  libevdev/libevdev-uinput.c     | 285 +++++++++++++++++++++++++++++++++++++++++
>>  libevdev/libevdev-uinput.h     | 232 +++++++++++++++++++++++++++++++++
>>  libevdev/libevdev.c            |   1 -
>>  test/Makefile.am               |   3 +
>>  7 files changed, 555 insertions(+), 3 deletions(-)
>>  create mode 100644 libevdev/libevdev-uinput-int.h
>>  create mode 100644 libevdev/libevdev-uinput.c
>>  create mode 100644 libevdev/libevdev-uinput.h
>>
>> diff --git a/doc/libevdev.doxygen.in b/doc/libevdev.doxygen.in
>> index 4511ae8..855b317 100644
>> --- a/doc/libevdev.doxygen.in
>> +++ b/doc/libevdev.doxygen.in
>> @@ -668,7 +668,8 @@ WARN_LOGFILE           =
>>  # directories like "/usr/src/myproject". Separate the files or directories
>>  # with spaces.
>>
>> -INPUT                  = @top_srcdir@/libevdev/libevdev.h
>> +INPUT                  = @top_srcdir@/libevdev/libevdev.h \
>> +                        @top_srcdir@/libevdev/libevdev-uinput.h
>>
>>  # This tag can be used to specify the character encoding of the source files
>>  # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
>> diff --git a/libevdev/Makefile.am b/libevdev/Makefile.am
>> index 9879c0e..2e99c2a 100644
>> --- a/libevdev/Makefile.am
>> +++ b/libevdev/Makefile.am
>> @@ -6,12 +6,15 @@ libevdev_la_SOURCES = \
>>                     libevdev.h \
>>                     libevdev-int.h \
>>                     libevdev-util.h \
>> +                   libevdev-uinput.c \
>> +                   libevdev-uinput.h \
>> +                   libevdev-uinput-int.h \
>>                     libevdev.c
>>
>>  libevdev_la_LDFLAGS = -version-info $(LIBEVDEV_LT_VERSION) -export-symbols-regex '^libevdev_' $(GCOV_LDFLAGS)
>>
>>  libevdevincludedir = $(includedir)/libevdev-1.0/libevdev
>> -libevdevinclude_HEADERS = libevdev.h
>> +libevdevinclude_HEADERS = libevdev.h libevdev-uinput.h
>>
>>  event-names.h: Makefile make-event-names.py
>>                 $(srcdir)/make-event-names.py --output=c > $@
>> diff --git a/libevdev/libevdev-uinput-int.h b/libevdev/libevdev-uinput-int.h
>> new file mode 100644
>> index 0000000..70fa1e1
>> --- /dev/null
>> +++ b/libevdev/libevdev-uinput-int.h
>> @@ -0,0 +1,29 @@
>> +/*
>> + * Copyright © 2013 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.
>> + */
>> +
>> +
>> +struct libevdev_uinput {
>> +       int fd; /**< file descriptor to uinput */
>> +       char *name; /**< device name */
>> +       char *syspath; /**< /sys path */
>> +       time_t ctime[2]; /**< before/after UI_DEV_CREATE */
>> +};
>> diff --git a/libevdev/libevdev-uinput.c b/libevdev/libevdev-uinput.c
>> new file mode 100644
>> index 0000000..3009fba
>> --- /dev/null
>> +++ b/libevdev/libevdev-uinput.c
>> @@ -0,0 +1,285 @@
>> +/*
>> + * Copyright © 2013 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 <fcntl.h>
>> +#include <poll.h>
>> +#include <errno.h>
>> +#include <unistd.h>
>> +#include <string.h>
>> +#include <stdio.h>
>> +#include <dirent.h>
>> +#include <sys/stat.h>
>> +#include <time.h>
>> +#include <linux/uinput.h>
>> +
>> +#include "libevdev.h"
>> +#include "libevdev-int.h"
>> +#include "libevdev-uinput.h"
>> +#include "libevdev-uinput-int.h"
>> +#include "libevdev-util.h"
>> +
>> +#define SYS_INPUT_DIR "/sys/devices/virtual/input/"
>> +
>> +static struct libevdev_uinput *
>> +alloc_uinput_device(const char *name)
>> +{
>> +       struct libevdev_uinput *uinput_dev;
>> +
>> +       uinput_dev = calloc(1, sizeof(struct libevdev_uinput));
>> +       if (uinput_dev)
>> +               uinput_dev->name = strdup(name);
>> +
>> +       return uinput_dev;
>> +}
>> +
>> +int set_evbits(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev)
>
> hmm, it looks like either this function should be prefixed with
> libevdev_uinput, or it should be static.
>
>> +{
>> +       int rc = 0;
>> +       unsigned int type;
>> +
>> +       for (type = 0; type < EV_MAX; type++) {
>> +               unsigned int code;
>> +               int max;
>> +               int uinput_bit;
>> +               const unsigned long *mask;
>> +
>> +               if (!libevdev_has_event_type(dev, type))
>> +                       continue;
>> +
>> +               rc = ioctl(fd, UI_SET_EVBIT, type);
>> +               if (rc == -1)
>> +                       break;
>> +
>> +               /* uinput can't set EV_REP */
>> +               if (type == EV_REP)
>> +                       continue;
>> +
>> +               max = type_to_mask_const(dev, type, &mask);
>> +               if (max == -1)
>> +                       continue;
>> +
>> +               switch(type) {
>> +                       case EV_KEY: uinput_bit = UI_SET_KEYBIT; break;
>> +                       case EV_REL: uinput_bit = UI_SET_RELBIT; break;
>> +                       case EV_ABS: uinput_bit = UI_SET_ABSBIT; break;
>> +                       case EV_MSC: uinput_bit = UI_SET_MSCBIT; break;
>> +                       case EV_LED: uinput_bit = UI_SET_LEDBIT; break;
>> +                       case EV_SND: uinput_bit = UI_SET_SNDBIT; break;
>> +                       case EV_FF: uinput_bit = UI_SET_FFBIT; break;
>> +                       case EV_SW: uinput_bit = UI_SET_SWBIT; break;
>> +                       default:
>> +                                   rc = -1;
>> +                                   errno = EINVAL;
>> +                                   goto out;
>> +               }
>> +
>> +               for (code = 0; code < max; code++) {
>> +                       if (!libevdev_has_event_code(dev, type, code))
>> +                               continue;
>> +
>> +                       rc = ioctl(fd, uinput_bit, code);
>> +                       if (rc == -1)
>> +                               goto out;
>> +
>> +                       if (type == EV_ABS) {
>> +                               const struct input_absinfo *abs = libevdev_get_abs_info(dev, code);
>> +                               uidev->absmin[code] = abs->minimum;
>> +                               uidev->absmax[code] = abs->maximum;
>> +                               uidev->absfuzz[code] = abs->fuzz;
>> +                               uidev->absflat[code] = abs->flat;
>> +                               /* uinput has no resolution in the device struct, this needs
>> +                                * to be fixed in the kernel */
>> +                       }
>> +               }
>> +
>> +       }
>> +
>> +out:
>> +       return rc;
>> +}
>> +
>> +int set_props(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev)
>
> ditto
>
>> +{
>> +       unsigned int prop;
>> +       int rc = 0;
>> +
>> +       for (prop = 0; prop < INPUT_PROP_MAX; prop++) {
>> +               if (!libevdev_has_property(dev, prop))
>> +                       continue;
>> +
>> +               rc = ioctl(fd, UI_SET_PROPBIT, prop);
>> +               if (rc == -1)
>> +                       break;
>> +       }
>> +       return rc;
>> +}
>> +
>> +int
>> +libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct libevdev_uinput** uinput_dev)
>
> The code looks good, but I don't really like the parameter "fd". To
> me, it looks like this parameter can only be a fresh open of
> /dev/uinput node, with the right permissions and parameters. Besides,
> having this as a parameter, would allow a user to use it twice for two
> different uinput devices.
>
> To my mind, the parameter should be omitted and the open called in
> libevdev_uinput_create_from_device(). If the user wants to have a
> handle to it, it can call libevdev_uinput_get_fd(), and it will also
> allows us to close the fd in libevdev_uinput_destroy().

You might not have permissions to access /dev/uinput and get the fd
via a unix socket. That's quite common practice. But we could allow
"-1" and then open the fd inside of the constructor.

But I agree that libevdev should own the fd. uinput doesn't allow
multiplexing on a single fd so I cannot see any reason why a user
would want access to it after calling create_from_device().

Regards
David


More information about the Input-tools mailing list