kernel - Bring in evdev from FreeBSD
authorPeeter Must <karu.pruun@gmail.com>
Fri, 8 Dec 2017 10:43:27 +0000 (12:43 +0200)
committerPeeter Must <karu.pruun@gmail.com>
Fri, 8 Dec 2017 10:43:27 +0000 (12:43 +0200)
* This is a port of evdev, the generic input event interface, from
  FreeBSD. Wikipedia: "Evdev generalizes raw input events from device
  drivers and makes them available through character devices in the
  /dev/input/ directory."

* It is currently considered experimental since in rare cases it may cause
  kernel crash when a device, e.g. usb mouse or keyboard, is detached
  while a userland program reading from the corresponding input/eventX
  device.

* In order to enable evdev, kernel needs to be rebuilt with 'device evdev'
  and 'options EVDEV_SUPPORT'. For debugging, add 'options EVDEV_DEBUG'.

* At present, only ums, kbd and kbdmux can send events to their respective
  input/eventX devices. More drivers will be added in due course. The sysctl
  kern.evdev.rcpt_mask determines which drivers send events to evdev.

13 files changed:
sys/conf/files
sys/conf/options
sys/config/LINT64
sys/dev/misc/evdev/Makefile [new file with mode: 0755]
sys/dev/misc/evdev/cdev.c [new file with mode: 0644]
sys/dev/misc/evdev/evdev.c [new file with mode: 0644]
sys/dev/misc/evdev/evdev.h [new file with mode: 0644]
sys/dev/misc/evdev/evdev_mt.c [new file with mode: 0644]
sys/dev/misc/evdev/evdev_private.h [new file with mode: 0644]
sys/dev/misc/evdev/evdev_utils.c [new file with mode: 0644]
sys/dev/misc/evdev/freebsd-bitstring.h [new file with mode: 0644]
sys/dev/misc/evdev/input-event-codes.h [new file with mode: 0644]
sys/dev/misc/evdev/input.h [new file with mode: 0644]

index 57feace..3dbfc9a 100644 (file)
@@ -2095,6 +2095,10 @@ dev/misc/ecc/ecc_e3.c            optional ecc
 dev/misc/ecc/ecc_e5.c          optional ecc
 # not ready yet
 #dev/misc/ecc/ecc_x3400.c      optional ecc
+dev/misc/evdev/cdev.c          optional evdev
+dev/misc/evdev/evdev.c         optional evdev
+dev/misc/evdev/evdev_mt.c      optional evdev
+dev/misc/evdev/evdev_utils.c   optional evdev
 dev/misc/joy/joy.c             optional joy
 dev/misc/kbdmux/kbdmux.c       optional kbdmux
 dev/misc/orm/orm.c             optional isa
index c353027..f5ebb52 100644 (file)
@@ -654,6 +654,10 @@ SND_OLDSTEREO              opt_snd.h
 
 PANIC_REBOOT_WAIT_TIME opt_panic.h
 
+# Evdev protocol support
+EVDEV_SUPPORT          opt_evdev.h
+EVDEV_DEBUG            opt_evdev.h
+
 # Obsolete options (will be removed in the future)
 #
 FFS_ROOT               opt_dontuse.h
index cf059d1..b142627 100644 (file)
@@ -2156,6 +2156,11 @@ options          RSS_DEBUG       # enable RSS (Receive Side Scaling) debugging
 # clock interrupt.  Use pctrack(8) to dump this information.
 options                DEBUG_PCTRACK
 
+# evdev interface
+device         evdev           # input event device support
+options                EVDEV_SUPPORT   # evdev support in legacy drivers
+options                EVDEV_DEBUG     # enable event debug messages
+
 # More undocumented options for linting.
 # Note that documenting these are not considered an affront.
 
diff --git a/sys/dev/misc/evdev/Makefile b/sys/dev/misc/evdev/Makefile
new file mode 100755 (executable)
index 0000000..26795c4
--- /dev/null
@@ -0,0 +1,6 @@
+KMOD=  evdev
+SRCS=  cdev.c evdev.c evdev_mt.c evdev_utils.c
+SRCS+= opt_evdev.h
+SRCS+= device_if.h bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/misc/evdev/cdev.c b/sys/dev/misc/evdev/cdev.c
new file mode 100644 (file)
index 0000000..e8904ef
--- /dev/null
@@ -0,0 +1,887 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_evdev.h"
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/devfs.h>
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h> /* IO_NDELAY in read() */
+#include <sys/uio.h>
+
+#include <sys/errno.h>
+
+#include <sys/device.h>
+#include <sys/bus.h>
+#include "bus_if.h"
+#include "device_if.h"
+
+/* Use FreeBSD bitstring locally. */
+#include "freebsd-bitstring.h"
+
+#include <dev/misc/evdev/evdev.h>
+#include <dev/misc/evdev/evdev_private.h>
+#include <dev/misc/evdev/input.h>
+
+#ifdef EVDEV_DEBUG
+#define        debugf(client, fmt, args...)    kprintf("evdev cdev: "fmt"\n", ##args)
+#else
+#define        debugf(client, fmt, args...)
+#endif
+
+#define        DEF_RING_REPORTS        8
+#define GID_INPUT              107     /* input group */
+
+static d_open_t                evdev_open;
+static d_read_t                evdev_read;
+static d_write_t       evdev_write;
+static d_ioctl_t       evdev_ioctl;
+static d_kqfilter_t    evdev_kqfilter;
+
+static int evdev_kqread(struct knote *kn, long hint);
+static void evdev_kqdetach(struct knote *kn);
+static void evdev_dtor(void *);
+static int evdev_ioctl_eviocgbit(struct evdev_dev *, int, int, caddr_t);
+static void evdev_client_filter_queue(struct evdev_client *, uint16_t);
+
+static struct dev_ops evdev_cdevsw = {
+       { "evdev", 0, 0 },
+       .d_open = evdev_open,
+       .d_read = evdev_read,
+       .d_write = evdev_write,
+       .d_ioctl = evdev_ioctl,
+       .d_kqfilter = evdev_kqfilter,
+};
+
+static struct filterops evdev_cdev_filterops = {
+       .f_flags = FILTEROP_ISFD,
+       .f_attach = NULL,
+       .f_detach = evdev_kqdetach,
+       .f_event = evdev_kqread,
+};
+
+static int
+evdev_open(struct dev_open_args *ap)
+{
+       cdev_t dev = ap->a_head.a_dev;
+       struct evdev_dev *evdev = dev->si_drv1;
+       struct evdev_client *client;
+       size_t buffer_size;
+       int ret;
+
+       if (evdev == NULL)
+               return (ENODEV);
+
+       /* Initialize client structure */
+       buffer_size = evdev->ev_report_size * DEF_RING_REPORTS;
+       client = kmalloc(offsetof(struct evdev_client, ec_buffer) +
+           sizeof(struct input_event) * buffer_size,
+           M_EVDEV, M_WAITOK | M_ZERO);
+
+       /* Initialize ring buffer */
+       client->ec_buffer_size = buffer_size;
+       client->ec_buffer_head = 0;
+       client->ec_buffer_tail = 0;
+       client->ec_buffer_ready = 0;
+
+       client->ec_evdev = evdev;
+       lockinit(&client->ec_buffer_mtx, "evclient", 0, LK_CANRECURSE);
+
+       /* Avoid race with evdev_unregister */
+       EVDEV_LOCK(evdev);
+       if (dev->si_drv1 == NULL)
+               ret = ENODEV;
+       else
+               ret = evdev_register_client(evdev, client);
+
+       if (ret != 0)
+               evdev_revoke_client(client);
+       /*
+        * Unlock evdev here because non-sleepable lock held
+        * while calling devfs_set_cdevpriv upsets WITNESS
+        */
+       EVDEV_UNLOCK(evdev);
+
+       if (!ret)
+               ret = devfs_set_cdevpriv(ap->a_fp, client, &evdev_dtor);
+
+       if (ret != 0) {
+               debugf(client, "cannot register evdev client");
+       }
+
+       return (ret);
+}
+
+static void
+evdev_dtor(void *data)
+{
+       struct evdev_client *client = (struct evdev_client *)data;
+
+       EVDEV_LOCK(client->ec_evdev);
+       if (!client->ec_revoked)
+               evdev_dispose_client(client->ec_evdev, client);
+       EVDEV_UNLOCK(client->ec_evdev);
+
+       funsetown(&client->ec_sigio);
+       lockuninit(&client->ec_buffer_mtx);
+       kfree(client, M_EVDEV);
+}
+
+static int
+evdev_read(struct dev_read_args *ap)
+{
+       struct uio *uio = ap->a_uio;
+       int ioflag = ap->a_ioflag;
+       struct evdev_client *client;
+       struct input_event event;
+       int ret = 0;
+       int remaining;
+
+       ret = devfs_get_cdevpriv(ap->a_fp, (void **)&client);
+       if (ret != 0)
+               return (ret);
+
+       debugf(client, "read %zd bytes by thread %d", uio->uio_resid, 0);
+
+       if (client->ec_revoked)
+               return (ENODEV);
+
+       /* Zero-sized reads are allowed for error checking */
+       if (uio->uio_resid != 0 && uio->uio_resid < sizeof(struct input_event))
+               return (EINVAL);
+
+       remaining = uio->uio_resid / sizeof(struct input_event);
+
+       EVDEV_CLIENT_LOCKQ(client);
+
+       if (EVDEV_CLIENT_EMPTYQ(client)) {
+               if (ioflag & IO_NDELAY) {
+                       ret = EWOULDBLOCK;
+               } else {
+                       if (remaining != 0) {
+                               client->ec_blocked = true;
+                               ret = lksleep(client, &client->ec_buffer_mtx,
+                                   PCATCH, "evread", 0);
+                       }
+               }
+       }
+
+       while (ret == 0 && !EVDEV_CLIENT_EMPTYQ(client) && remaining > 0) {
+               memcpy(&event, &client->ec_buffer[client->ec_buffer_head],
+                   sizeof(struct input_event));
+               client->ec_buffer_head =
+                   (client->ec_buffer_head + 1) % client->ec_buffer_size;
+               remaining--;
+
+               EVDEV_CLIENT_UNLOCKQ(client);
+               ret = uiomove((void *)&event, sizeof(struct input_event), uio);
+               EVDEV_CLIENT_LOCKQ(client);
+       }
+
+       EVDEV_CLIENT_UNLOCKQ(client);
+
+       return (ret);
+}
+
+static int
+evdev_write(struct dev_write_args *ap)
+{
+       cdev_t dev = ap->a_head.a_dev;
+       struct uio *uio = ap->a_uio;
+       struct evdev_dev *evdev = dev->si_drv1;
+       struct evdev_client *client;
+       struct input_event event;
+       int ret = 0;
+
+       ret = devfs_get_cdevpriv(ap->a_fp, (void **)&client);
+       if (ret != 0)
+               return (ret);
+
+       debugf(client, "write %zd bytes by thread %d", uio->uio_resid, 0);
+
+       if (client->ec_revoked || evdev == NULL)
+               return (ENODEV);
+
+       if (uio->uio_resid % sizeof(struct input_event) != 0) {
+               debugf(client, "write size not multiple of input_event size");
+               return (EINVAL);
+       }
+
+       while (uio->uio_resid > 0 && ret == 0) {
+               ret = uiomove((void *)&event, sizeof(struct input_event), uio);
+               if (ret == 0)
+                       ret = evdev_inject_event(evdev, event.type, event.code,
+                           event.value);
+       }
+
+       return (ret);
+}
+
+static int
+evdev_kqfilter(struct dev_kqfilter_args *ap)
+{
+       struct knote *kn = ap->a_kn;
+       struct klist *klist;
+       struct evdev_client *client;
+       int ret;
+
+       ret = devfs_get_cdevpriv(ap->a_fp, (void **)&client);
+       if (ret != 0)
+               return (ret);
+
+       if (client->ec_revoked)
+               return (ENODEV);
+
+       switch(kn->kn_filter) {
+       case EVFILT_READ:
+               kn->kn_fop = &evdev_cdev_filterops;
+               break;
+       default:
+               return(EINVAL);
+       }
+       kn->kn_hook = (caddr_t)client;
+
+       klist = &client->kqinfo.ki_note;
+       knote_insert(klist, kn);
+       return (0);
+}
+
+static int
+evdev_kqread(struct knote *kn, long hint)
+{
+       struct evdev_client *client;
+       int ret;
+       int locked = 0;
+
+       client = (struct evdev_client *)kn->kn_hook;
+
+       /* NOTE on DragonFly v FreeBSD.
+        * FreeBSD locks the klist when calling f_event, i.e. evdev_kqread().
+        * That's why the plain assertion EVDEV_CLIENT_LOCKQ_ASSERT(client)
+        * fails on DragonFly: DragonFly does not ensure the lock associated
+        * with the klist is locked.
+        * To mimic FreeBSD's behavior, we will lock ec_buffer_mtx if
+        * it was not locked, and unlock when leaving.
+        */
+       locked = lockowned(&(client)->ec_buffer_mtx);
+       if (!locked)
+               EVDEV_CLIENT_LOCKQ(client);
+
+       EVDEV_CLIENT_LOCKQ_ASSERT(client);
+
+       if (client->ec_revoked) {
+               kn->kn_flags |= EV_EOF;
+               ret = 1;
+       } else {
+               kn->kn_data = EVDEV_CLIENT_SIZEQ(client) *
+                   sizeof(struct input_event);
+               ret = !EVDEV_CLIENT_EMPTYQ(client);
+       }
+
+       /* Unlock if ec_buffer_mtx was not locked. */
+       if (!locked) {
+               EVDEV_CLIENT_UNLOCKQ(client);
+       }
+
+       return (ret);
+}
+
+static void
+evdev_kqdetach(struct knote *kn)
+{
+       struct evdev_client *client;
+
+       client = (struct evdev_client *)kn->kn_hook;
+       knote_remove(&client->kqinfo.ki_note, kn);
+}
+
+static int
+evdev_ioctl(struct dev_ioctl_args *ap)
+{
+       cdev_t dev = ap->a_head.a_dev;
+       u_long cmd = ap->a_cmd;
+       caddr_t data = ap->a_data;
+       struct evdev_dev *evdev = dev->si_drv1;
+       struct evdev_client *client;
+       struct input_keymap_entry *ke;
+       int ret, len, limit, type_num;
+       uint32_t code;
+       size_t nvalues;
+
+       ret = devfs_get_cdevpriv(ap->a_fp, (void **)&client);
+       if (ret != 0)
+               return (ret);
+
+       if (client->ec_revoked || evdev == NULL)
+               return (ENODEV);
+
+       /* file I/O ioctl handling */
+       switch (cmd) {
+       case FIOSETOWN:
+               return (fsetown(*(int *)data, &client->ec_sigio));
+
+       case FIOGETOWN:
+               *(int *)data = fgetown(&client->ec_sigio);
+               return (0);
+
+       case FIONBIO:
+               return (0);
+
+       case FIOASYNC:
+               if (*(int *)data)
+                       client->ec_async = true;
+               else
+                       client->ec_async = false;
+
+               return (0);
+
+       case FIONREAD:
+               EVDEV_CLIENT_LOCKQ(client);
+               *(int *)data =
+                   EVDEV_CLIENT_SIZEQ(client) * sizeof(struct input_event);
+               EVDEV_CLIENT_UNLOCKQ(client);
+               return (0);
+       }
+
+       len = IOCPARM_LEN(cmd);
+       debugf(client, "ioctl called: cmd=0x%08lx, data=%p", cmd, data);
+
+       /* evdev fixed-length ioctls handling */
+       switch (cmd) {
+       case EVIOCGVERSION:
+               *(int *)data = EV_VERSION;
+               return (0);
+
+       case EVIOCGID:
+               debugf(client, "EVIOCGID: bus=%d vendor=0x%04x product=0x%04x",
+                   evdev->ev_id.bustype, evdev->ev_id.vendor,
+                   evdev->ev_id.product);
+               memcpy(data, &evdev->ev_id, sizeof(struct input_id));
+               return (0);
+
+       case EVIOCGREP:
+               if (!evdev_event_supported(evdev, EV_REP))
+                       return (ENOTSUP);
+
+               memcpy(data, evdev->ev_rep, sizeof(evdev->ev_rep));
+               return (0);
+
+       case EVIOCSREP:
+               if (!evdev_event_supported(evdev, EV_REP))
+                       return (ENOTSUP);
+
+               evdev_inject_event(evdev, EV_REP, REP_DELAY, ((int *)data)[0]);
+               evdev_inject_event(evdev, EV_REP, REP_PERIOD,
+                   ((int *)data)[1]);
+               return (0);
+
+       case EVIOCGKEYCODE:
+               /* Fake unsupported ioctl */
+               return (0);
+
+       case EVIOCGKEYCODE_V2:
+               if (evdev->ev_methods == NULL ||
+                   evdev->ev_methods->ev_get_keycode == NULL)
+                       return (ENOTSUP);
+
+               ke = (struct input_keymap_entry *)data;
+               evdev->ev_methods->ev_get_keycode(evdev, evdev->ev_softc, ke);
+               return (0);
+
+       case EVIOCSKEYCODE:
+               /* Fake unsupported ioctl */
+               return (0);
+
+       case EVIOCSKEYCODE_V2:
+               if (evdev->ev_methods == NULL ||
+                   evdev->ev_methods->ev_set_keycode == NULL)
+                       return (ENOTSUP);
+
+               ke = (struct input_keymap_entry *)data;
+               evdev->ev_methods->ev_set_keycode(evdev, evdev->ev_softc, ke);
+               return (0);
+
+       case EVIOCGABS(0) ... EVIOCGABS(ABS_MAX):
+               if (evdev->ev_absinfo == NULL)
+                       return (EINVAL);
+
+               memcpy(data, &evdev->ev_absinfo[cmd - EVIOCGABS(0)],
+                   sizeof(struct input_absinfo));
+               return (0);
+
+       case EVIOCSABS(0) ... EVIOCSABS(ABS_MAX):
+               if (evdev->ev_absinfo == NULL)
+                       return (EINVAL);
+
+               code = cmd - EVIOCSABS(0);
+               /* mt-slot number can not be changed */
+               if (code == ABS_MT_SLOT)
+                       return (EINVAL);
+
+               EVDEV_LOCK(evdev);
+               evdev_set_absinfo(evdev, code, (struct input_absinfo *)data);
+               EVDEV_UNLOCK(evdev);
+               return (0);
+
+       case EVIOCSFF:
+       case EVIOCRMFF:
+       case EVIOCGEFFECTS:
+               /* Fake unsupported ioctls */
+               return (0);
+
+       case EVIOCGRAB:
+               EVDEV_LOCK(evdev);
+               if (*(int *)data)
+                       ret = evdev_grab_client(evdev, client);
+               else
+                       ret = evdev_release_client(evdev, client);
+               EVDEV_UNLOCK(evdev);
+               return (ret);
+
+       case EVIOCREVOKE:
+               if (*(int *)data != 0)
+                       return (EINVAL);
+
+               EVDEV_LOCK(evdev);
+               if (dev->si_drv1 != NULL && !client->ec_revoked) {
+                       evdev_dispose_client(evdev, client);
+                       evdev_revoke_client(client);
+               }
+               EVDEV_UNLOCK(evdev);
+               return (0);
+
+       case EVIOCSCLOCKID:
+               switch (*(int *)data) {
+               case CLOCK_REALTIME:
+                       client->ec_clock_id = EV_CLOCK_REALTIME;
+                       return (0);
+               case CLOCK_MONOTONIC:
+                       client->ec_clock_id = EV_CLOCK_MONOTONIC;
+                       return (0);
+               default:
+                       return (EINVAL);
+               }
+       }
+
+       /* evdev variable-length ioctls handling */
+       switch (IOCBASECMD(cmd)) {
+       case EVIOCGNAME(0):
+               strlcpy(data, evdev->ev_name, len);
+               return (0);
+
+       case EVIOCGPHYS(0):
+               if (evdev->ev_shortname[0] == 0)
+                       return (ENOENT);
+
+               strlcpy(data, evdev->ev_shortname, len);
+               return (0);
+
+       case EVIOCGUNIQ(0):
+               if (evdev->ev_serial[0] == 0)
+                       return (ENOENT);
+
+               strlcpy(data, evdev->ev_serial, len);
+               return (0);
+
+       case EVIOCGPROP(0):
+               limit = MIN(len, bitstr_size(INPUT_PROP_CNT));
+               memcpy(data, evdev->ev_prop_flags, limit);
+               return (0);
+
+       case EVIOCGMTSLOTS(0):
+               if (evdev->ev_mt == NULL)
+                       return (EINVAL);
+               if (len < sizeof(uint32_t))
+                       return (EINVAL);
+               code = *(uint32_t *)data;
+               if (!ABS_IS_MT(code))
+                       return (EINVAL);
+
+               nvalues =
+                   MIN(len / sizeof(int32_t) - 1, MAXIMAL_MT_SLOT(evdev) + 1);
+               for (int i = 0; i < nvalues; i++)
+                       ((int32_t *)data)[i + 1] =
+                           evdev_get_mt_value(evdev, i, code);
+               return (0);
+
+       case EVIOCGKEY(0):
+               limit = MIN(len, bitstr_size(KEY_CNT));
+               EVDEV_LOCK(evdev);
+               evdev_client_filter_queue(client, EV_KEY);
+               memcpy(data, evdev->ev_key_states, limit);
+               EVDEV_UNLOCK(evdev);
+               return (0);
+
+       case EVIOCGLED(0):
+               limit = MIN(len, bitstr_size(LED_CNT));
+               EVDEV_LOCK(evdev);
+               evdev_client_filter_queue(client, EV_LED);
+               memcpy(data, evdev->ev_led_states, limit);
+               EVDEV_UNLOCK(evdev);
+               return (0);
+
+       case EVIOCGSND(0):
+               limit = MIN(len, bitstr_size(SND_CNT));
+               EVDEV_LOCK(evdev);
+               evdev_client_filter_queue(client, EV_SND);
+               memcpy(data, evdev->ev_snd_states, limit);
+               EVDEV_UNLOCK(evdev);
+               return (0);
+
+       case EVIOCGSW(0):
+               limit = MIN(len, bitstr_size(SW_CNT));
+               EVDEV_LOCK(evdev);
+               evdev_client_filter_queue(client, EV_SW);
+               memcpy(data, evdev->ev_sw_states, limit);
+               EVDEV_UNLOCK(evdev);
+               return (0);
+
+       case EVIOCGBIT(0, 0) ... EVIOCGBIT(EV_MAX, 0):
+               type_num = IOCBASECMD(cmd) - EVIOCGBIT(0, 0);
+               debugf(client, "EVIOCGBIT(%d): data=%p, len=%d", type_num,
+                   data, len);
+               return (evdev_ioctl_eviocgbit(evdev, type_num, len, data));
+       }
+
+       return (EINVAL);
+}
+
+static int
+evdev_ioctl_eviocgbit(struct evdev_dev *evdev, int type, int len, caddr_t data)
+{
+       /*
+        * We will use freebsd-bitstring.h locally. This ensures bitmap
+        * is of type (unsigned long *). DragonFly's original bitmap
+        * is (unsigned char *).
+        */
+       unsigned long *bitmap;
+       int limit;
+
+       switch (type) {
+       case 0:
+               bitmap = evdev->ev_type_flags;
+               limit = EV_CNT;
+               break;
+       case EV_KEY:
+               bitmap = evdev->ev_key_flags;
+               limit = KEY_CNT;
+               break;
+       case EV_REL:
+               bitmap = evdev->ev_rel_flags;
+               limit = REL_CNT;
+               break;
+       case EV_ABS:
+               bitmap = evdev->ev_abs_flags;
+               limit = ABS_CNT;
+               break;
+       case EV_MSC:
+               bitmap = evdev->ev_msc_flags;
+               limit = MSC_CNT;
+               break;
+       case EV_LED:
+               bitmap = evdev->ev_led_flags;
+               limit = LED_CNT;
+               break;
+       case EV_SND:
+               bitmap = evdev->ev_snd_flags;
+               limit = SND_CNT;
+               break;
+       case EV_SW:
+               bitmap = evdev->ev_sw_flags;
+               limit = SW_CNT;
+               break;
+       case EV_FF:
+               /*
+                * We don't support EV_FF now, so let's
+                * just fake it returning only zeros.
+                */
+               bzero(data, len);
+               return (0);
+       default:
+               return (ENOTTY);
+       }
+
+       /*
+        * Clear ioctl data buffer in case it's bigger than
+        * bitmap size
+        */
+       bzero(data, len);
+
+       limit = bitstr_size(limit);
+       len = MIN(limit, len);
+       memcpy(data, bitmap, len);
+       return (0);
+}
+
+void
+evdev_revoke_client(struct evdev_client *client)
+{
+
+       EVDEV_LOCK_ASSERT(client->ec_evdev);
+
+       client->ec_revoked = true;
+}
+
+void
+evdev_notify_event(struct evdev_client *client)
+{
+
+       EVDEV_CLIENT_LOCKQ_ASSERT(client);
+
+       if (client->ec_blocked) {
+               client->ec_blocked = false;
+               wakeup(client);
+       }
+       if (client->ec_selected) {
+               client->ec_selected = false;
+               wakeup(&client->kqinfo);
+       }
+
+       KNOTE(&client->kqinfo.ki_note, 0);
+
+       if (client->ec_async && client->ec_sigio != NULL)
+               pgsigio(client->ec_sigio, SIGIO, 0);
+}
+
+int
+evdev_cdev_create(struct evdev_dev *evdev)
+{
+       cdev_t dev;
+       int ret, unit;
+
+       /*
+        * Iterate over devices input/eventX until we find a non-existing
+        * one and record its number in unit.
+        */
+       unit = 0;
+       while (devfs_find_device_by_name("input/event%d", unit) != NULL) {
+           unit++;
+       }
+
+       /*
+        * Put unit as minor. Minor and major will determine st_rdev of
+        * eventX. Ensuring that all eventX have different major and minor
+        * will make st_rdev unique. This is needed by libinput, which
+        * determines eventX from st_rdev.
+        */
+       dev = make_dev(&evdev_cdevsw, unit, UID_ROOT, GID_INPUT,
+                                 0660, "input/event%d", unit);
+
+       if (dev != NULL) {
+               dev->si_drv1 = evdev;
+               evdev->ev_cdev = dev;
+               evdev->ev_unit = unit;
+               ret = 0;
+       } else {
+               ret = ENODEV;
+               goto err;
+       }
+
+       reference_dev(evdev->ev_cdev);
+
+err:
+       return (ret);
+}
+
+int
+evdev_cdev_destroy(struct evdev_dev *evdev)
+{
+
+       if (evdev->ev_cdev) {
+               dev_ops_remove_minor(&evdev_cdevsw, evdev->ev_unit);
+       }
+
+       return (0);
+}
+
+static void
+evdev_client_gettime(struct evdev_client *client, struct timeval *tv)
+{
+
+       switch (client->ec_clock_id) {
+       case EV_CLOCK_BOOTTIME:
+               /*
+                * XXX: FreeBSD does not support true POSIX monotonic clock.
+                *      So aliase EV_CLOCK_BOOTTIME to EV_CLOCK_MONOTONIC.
+                */
+       case EV_CLOCK_MONOTONIC:
+               microuptime(tv);
+               break;
+
+       case EV_CLOCK_REALTIME:
+       default:
+               microtime(tv);
+               break;
+       }
+}
+
+void
+evdev_client_push(struct evdev_client *client, uint16_t type, uint16_t code,
+    int32_t value)
+{
+       struct timeval time;
+       size_t count, head, tail, ready;
+
+       EVDEV_CLIENT_LOCKQ_ASSERT(client);
+       head = client->ec_buffer_head;
+       tail = client->ec_buffer_tail;
+       ready = client->ec_buffer_ready;
+       count = client->ec_buffer_size;
+
+       /* If queue is full drop its content and place SYN_DROPPED event */
+       if ((tail + 1) % count == head) {
+               debugf(client, "client %p: buffer overflow", client);
+
+               head = (tail + count - 1) % count;
+               client->ec_buffer[head] = (struct input_event) {
+                       .type = EV_SYN,
+                       .code = SYN_DROPPED,
+                       .value = 0
+               };
+               /*
+                * XXX: Here is a small race window from now till the end of
+                *      report. The queue is empty but client has been already
+                *      notified of data readyness. Can be fixed in two ways:
+                * 1. Implement bulk insert so queue lock would not be dropped
+                *    till the SYN_REPORT event.
+                * 2. Insert SYN_REPORT just now and skip remaining events
+                */
+               client->ec_buffer_head = head;
+               client->ec_buffer_ready = head;
+       }
+
+       client->ec_buffer[tail].type = type;
+       client->ec_buffer[tail].code = code;
+       client->ec_buffer[tail].value = value;
+       client->ec_buffer_tail = (tail + 1) % count;
+
+       /* Allow users to read events only after report has been completed */
+       if (type == EV_SYN && code == SYN_REPORT) {
+               evdev_client_gettime(client, &time);
+               for (; ready != client->ec_buffer_tail;
+                   ready = (ready + 1) % count)
+                       client->ec_buffer[ready].time = time;
+               client->ec_buffer_ready = client->ec_buffer_tail;
+       }
+}
+
+void
+evdev_client_dumpqueue(struct evdev_client *client)
+{
+       struct input_event *event;
+       size_t i, head, tail, ready, size;
+
+       head = client->ec_buffer_head;
+       tail = client->ec_buffer_tail;
+       ready = client->ec_buffer_ready;
+       size = client->ec_buffer_size;
+
+       kprintf("evdev client: %p\n", client);
+       kprintf("event queue: head=%zu ready=%zu tail=%zu size=%zu\n",
+           head, ready, tail, size);
+
+       kprintf("queue contents:\n");
+
+       for (i = 0; i < size; i++) {
+               event = &client->ec_buffer[i];
+               kprintf("%zu: ", i);
+
+               if (i < head || i > tail)
+                       kprintf("unused\n");
+               else
+                       kprintf("type=%d code=%d value=%d ", event->type,
+                           event->code, event->value);
+
+               if (i == head)
+                       kprintf("<- head\n");
+               else if (i == tail)
+                       kprintf("<- tail\n");
+               else if (i == ready)
+                       kprintf("<- ready\n");
+               else
+                       kprintf("\n");
+       }
+}
+
+static void
+evdev_client_filter_queue(struct evdev_client *client, uint16_t type)
+{
+       struct input_event *event;
+       size_t head, tail, count, i;
+       bool last_was_syn = false;
+
+       EVDEV_CLIENT_LOCKQ(client);
+
+       i = head = client->ec_buffer_head;
+       tail = client->ec_buffer_tail;
+       count = client->ec_buffer_size;
+       client->ec_buffer_ready = client->ec_buffer_tail;
+
+       while (i != client->ec_buffer_tail) {
+               event = &client->ec_buffer[i];
+               i = (i + 1) % count;
+
+               /* Skip event of given type */
+               if (event->type == type)
+                       continue;
+
+               /* Remove empty SYN_REPORT events */
+               if (event->type == EV_SYN && event->code == SYN_REPORT) {
+                       if (last_was_syn)
+                               continue;
+                       else
+                               client->ec_buffer_ready = (tail + 1) % count;
+               }
+
+               /* Rewrite entry */
+               memcpy(&client->ec_buffer[tail], event,
+                   sizeof(struct input_event));
+
+               last_was_syn = (event->type == EV_SYN &&
+                   event->code == SYN_REPORT);
+
+               tail = (tail + 1) % count;
+       }
+
+       client->ec_buffer_head = i;
+       client->ec_buffer_tail = tail;
+
+       EVDEV_CLIENT_UNLOCKQ(client);
+}
diff --git a/sys/dev/misc/evdev/evdev.c b/sys/dev/misc/evdev/evdev.c
new file mode 100644 (file)
index 0000000..186828d
--- /dev/null
@@ -0,0 +1,978 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_evdev.h"
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <sys/devfs.h>  /* XXX detach driver when client is reading */
+
+/* Use FreeBSD's bitstring.h locally. */
+#include "freebsd-bitstring.h"
+
+#include <dev/misc/evdev/evdev.h>
+#include <dev/misc/evdev/evdev_private.h>
+#include <dev/misc/evdev/input.h>
+
+#ifdef EVDEV_DEBUG
+#define        debugf(evdev, fmt, args...)     kprintf("evdev: " fmt "\n", ##args)
+#else
+#define        debugf(evdev, fmt, args...)
+#endif
+
+#ifdef FEATURE
+FEATURE(evdev, "Input event devices support");
+#endif
+
+enum evdev_sparse_result
+{
+       EV_SKIP_EVENT,          /* Event value not changed */
+       EV_REPORT_EVENT,        /* Event value changed */
+       EV_REPORT_MT_SLOT,      /* Event value and MT slot number changed */
+};
+
+MALLOC_DEFINE(M_EVDEV, "evdev", "evdev memory");
+
+int evdev_rcpt_mask = EVDEV_RCPT_SYSMOUSE | EVDEV_RCPT_KBDMUX;
+int evdev_sysmouse_t_axis = 0;
+
+/*
+ * For the sake of clarity, evdev_rcpt_mask should be properly called
+ * evdev_sender_mask since the drivers (sysmouse, kbdmux, mouse and keyboard
+ * hardware drivers) send events *to* evdev, they do not receive events
+ * *from* evdev. Accordingly, we have changed the description of the sysctl
+ * to "Who sends events", but kept all the variable names so importing
+ * future changes is easier.
+ */
+
+SYSCTL_NODE(_kern, OID_AUTO, evdev, CTLFLAG_RW, 0, "Evdev args");
+SYSCTL_INT(_kern_evdev, OID_AUTO, rcpt_mask, CTLFLAG_RW, &evdev_rcpt_mask, 0,
+    "Who sends events: bit0 - sysmouse, bit1 - kbdmux, "
+    "bit2 - mouse hardware, bit3 - keyboard hardware");
+SYSCTL_INT(_kern_evdev, OID_AUTO, sysmouse_t_axis, CTLFLAG_RW,
+    &evdev_sysmouse_t_axis, 0, "Extract T-axis from 0-none, 1-ums, 2-psm");
+
+static void evdev_start_repeat(struct evdev_dev *, uint16_t);
+static void evdev_stop_repeat(struct evdev_dev *);
+static int evdev_check_event(struct evdev_dev *, uint16_t, uint16_t, int32_t);
+
+static inline void
+bit_change(bitstr_t *bitstr, int bit, int value)
+{
+       if (value)
+               bit_set(bitstr, bit);
+       else
+               bit_clear(bitstr, bit);
+}
+
+struct evdev_dev *
+evdev_alloc(void)
+{
+
+       return kmalloc(sizeof(struct evdev_dev), M_EVDEV, M_WAITOK | M_ZERO);
+}
+
+void
+evdev_free(struct evdev_dev *evdev)
+{
+
+       if (evdev != NULL && evdev->ev_cdev != NULL &&
+           evdev->ev_cdev->si_drv1 != NULL) {
+               evdev_unregister(evdev);
+       }
+
+       kfree(evdev, M_EVDEV);
+}
+
+static struct input_absinfo *
+evdev_alloc_absinfo(void)
+{
+
+       return (kmalloc(sizeof(struct input_absinfo) * ABS_CNT, M_EVDEV,
+           M_WAITOK | M_ZERO));
+}
+
+static void
+evdev_free_absinfo(struct input_absinfo *absinfo)
+{
+
+       kfree(absinfo, M_EVDEV);
+}
+
+int
+evdev_set_report_size(struct evdev_dev *evdev, size_t report_size)
+{
+       if (report_size > KEY_CNT + REL_CNT + ABS_CNT + MAX_MT_SLOTS * MT_CNT +
+           MSC_CNT + LED_CNT + SND_CNT + SW_CNT + FF_CNT)
+               return (EINVAL);
+
+       evdev->ev_report_size = report_size;
+       return (0);
+}
+
+static size_t
+evdev_estimate_report_size(struct evdev_dev *evdev)
+{
+       size_t size = 0;
+       int res;
+
+       /*
+        * Keyboards generate one event per report but other devices with
+        * buttons like mouses can report events simultaneously
+        */
+       bit_ffs_at(evdev->ev_key_flags, KEY_OK, KEY_CNT - KEY_OK, &res);
+       if (res == -1)
+               bit_ffs(evdev->ev_key_flags, BTN_MISC, &res);
+       size += (res != -1);
+       bit_count(evdev->ev_key_flags, BTN_MISC, KEY_OK - BTN_MISC, &res);
+       size += res;
+
+       /* All relative axes can be reported simultaneously */
+       bit_count(evdev->ev_rel_flags, 0, REL_CNT, &res);
+       size += res;
+
+       /*
+        * All absolute axes can be reported simultaneously.
+        * Multitouch axes can be reported ABS_MT_SLOT times
+        */
+       if (evdev->ev_absinfo != NULL) {
+               bit_count(evdev->ev_abs_flags, 0, ABS_CNT, &res);
+               size += res;
+               bit_count(evdev->ev_abs_flags, ABS_MT_FIRST, MT_CNT, &res);
+               if (res > 0) {
+                       res++;  /* ABS_MT_SLOT or SYN_MT_REPORT */
+                       if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT))
+                               /* MT type B */
+                               size += res * MAXIMAL_MT_SLOT(evdev);
+                       else
+                               /* MT type A */
+                               size += res * (MAX_MT_REPORTS - 1);
+               }
+       }
+
+       /* All misc events can be reported simultaneously */
+       bit_count(evdev->ev_msc_flags, 0, MSC_CNT, &res);
+       size += res;
+
+       /* All leds can be reported simultaneously */
+       bit_count(evdev->ev_led_flags, 0, LED_CNT, &res);
+       size += res;
+
+       /* Assume other events are generated once per report */
+       bit_ffs(evdev->ev_snd_flags, SND_CNT, &res);
+       size += (res != -1);
+
+       bit_ffs(evdev->ev_sw_flags, SW_CNT, &res);
+       size += (res != -1);
+
+       /* XXX: FF part is not implemented yet */
+
+       size++;         /* SYN_REPORT */
+       return (size);
+}
+
+static int
+evdev_register_common(struct evdev_dev *evdev)
+{
+       int ret;
+
+       debugf(evdev, "%s: registered evdev provider: %s <%s>\n",
+           evdev->ev_shortname, evdev->ev_name, evdev->ev_serial);
+
+       /* Initialize internal structures */
+       LIST_INIT(&evdev->ev_clients);
+
+       if (evdev_event_supported(evdev, EV_REP) &&
+           bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) {
+               /* Initialize callout */
+               callout_init_lk(&evdev->ev_rep_callout, &evdev->ev_mtx);
+
+               if (evdev->ev_rep[REP_DELAY] == 0 &&
+                   evdev->ev_rep[REP_PERIOD] == 0) {
+                       /* Supply default values */
+                       evdev->ev_rep[REP_DELAY] = 250;
+                       evdev->ev_rep[REP_PERIOD] = 33;
+               }
+       }
+
+       /* Initialize multitouch protocol type B states */
+       if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT) &&
+           evdev->ev_absinfo != NULL && MAXIMAL_MT_SLOT(evdev) > 0)
+               evdev_mt_init(evdev);
+
+       /* Estimate maximum report size */
+       if (evdev->ev_report_size == 0) {
+               ret = evdev_set_report_size(evdev,
+                   evdev_estimate_report_size(evdev));
+               if (ret != 0)
+                       goto bail_out;
+       }
+
+       /* Create char device node */
+       ret = evdev_cdev_create(evdev);
+bail_out:
+       return (ret);
+}
+
+int
+evdev_register(struct evdev_dev *evdev)
+{
+       int ret;
+
+       evdev->ev_lock_type = EV_LOCK_INTERNAL;
+       evdev->ev_lock = &evdev->ev_mtx;
+       lockinit(&evdev->ev_mtx, "evmtx", 0, 0);
+
+       ret = evdev_register_common(evdev);
+       if (ret != 0)
+               lockuninit(&evdev->ev_mtx);
+
+       return (ret);
+}
+
+int
+evdev_register_mtx(struct evdev_dev *evdev, struct lock *mtx)
+{
+
+       evdev->ev_lock_type = EV_LOCK_MTX;
+       evdev->ev_lock = mtx;
+       return (evdev_register_common(evdev));
+}
+
+int
+evdev_unregister(struct evdev_dev *evdev)
+{
+       struct evdev_client *client;
+       int ret;
+       debugf(evdev, "%s: unregistered evdev provider: %s\n",
+           evdev->ev_shortname, evdev->ev_name);
+
+       EVDEV_LOCK(evdev);
+       evdev->ev_cdev->si_drv1 = NULL;
+       /* Wake up sleepers */
+       LIST_FOREACH(client, &evdev->ev_clients, ec_link) {
+               evdev_revoke_client(client);
+               evdev_dispose_client(evdev, client);
+               EVDEV_CLIENT_LOCKQ(client);
+               evdev_notify_event(client);
+               EVDEV_CLIENT_UNLOCKQ(client);
+               if (evdev->ev_cdev) {
+                       devfs_assume_knotes(evdev->ev_cdev, &client->kqinfo);
+               }
+       }
+       EVDEV_UNLOCK(evdev);
+
+       ret = evdev_cdev_destroy(evdev);
+       evdev->ev_cdev = NULL;
+       if (ret == 0 && evdev->ev_lock_type == EV_LOCK_INTERNAL) {
+               lockuninit(&evdev->ev_mtx);
+       }
+
+       /*
+        * For some devices, e.g. keyboards, ev_absinfo and ev_mt
+        * may be NULL, so check before freeing them.
+        */
+       if (evdev->ev_absinfo != NULL)
+           evdev_free_absinfo(evdev->ev_absinfo);
+       if (evdev->ev_mt != NULL)
+           evdev_mt_free(evdev);
+
+       return (ret);
+}
+
+inline void
+evdev_set_name(struct evdev_dev *evdev, const char *name)
+{
+
+       ksnprintf(evdev->ev_name, NAMELEN, "%s", name);
+}
+
+inline void
+evdev_set_id(struct evdev_dev *evdev, uint16_t bustype, uint16_t vendor,
+    uint16_t product, uint16_t version)
+{
+
+       evdev->ev_id = (struct input_id) {
+               .bustype = bustype,
+               .vendor = vendor,
+               .product = product,
+               .version = version
+       };
+}
+
+inline void
+evdev_set_phys(struct evdev_dev *evdev, const char *name)
+{
+
+       ksnprintf(evdev->ev_shortname, NAMELEN, "%s", name);
+}
+
+inline void
+evdev_set_serial(struct evdev_dev *evdev, const char *serial)
+{
+
+       ksnprintf(evdev->ev_serial, NAMELEN, "%s", serial);
+}
+
+inline void
+evdev_set_methods(struct evdev_dev *evdev, void *softc,
+    const struct evdev_methods *methods)
+{
+
+       evdev->ev_methods = methods;
+       evdev->ev_softc = softc;
+}
+
+inline void
+evdev_support_prop(struct evdev_dev *evdev, uint16_t prop)
+{
+
+       KASSERT(prop < INPUT_PROP_CNT, ("invalid evdev input property"));
+       bit_set(evdev->ev_prop_flags, prop);
+}
+
+inline void
+evdev_support_event(struct evdev_dev *evdev, uint16_t type)
+{
+
+       KASSERT(type < EV_CNT, ("invalid evdev event property"));
+       bit_set(evdev->ev_type_flags, type);
+}
+
+inline void
+evdev_support_key(struct evdev_dev *evdev, uint16_t code)
+{
+
+       KASSERT(code < KEY_CNT, ("invalid evdev key property"));
+       bit_set(evdev->ev_key_flags, code);
+}
+
+inline void
+evdev_support_rel(struct evdev_dev *evdev, uint16_t code)
+{
+
+       KASSERT(code < REL_CNT, ("invalid evdev rel property"));
+       bit_set(evdev->ev_rel_flags, code);
+}
+
+inline void
+evdev_support_abs(struct evdev_dev *evdev, uint16_t code, int32_t value,
+    int32_t minimum, int32_t maximum, int32_t fuzz, int32_t flat,
+    int32_t resolution)
+{
+       struct input_absinfo absinfo;
+
+       KASSERT(code < ABS_CNT, ("invalid evdev abs property"));
+
+       absinfo = (struct input_absinfo) {
+               .value = value,
+               .minimum = minimum,
+               .maximum = maximum,
+               .fuzz = fuzz,
+               .flat = flat,
+               .resolution = resolution,
+       };
+       evdev_set_abs_bit(evdev, code);
+       evdev_set_absinfo(evdev, code, &absinfo);
+}
+
+inline void
+evdev_set_abs_bit(struct evdev_dev *evdev, uint16_t code)
+{
+
+       KASSERT(code < ABS_CNT, ("invalid evdev abs property"));
+       if (evdev->ev_absinfo == NULL)
+               evdev->ev_absinfo = evdev_alloc_absinfo();
+       bit_set(evdev->ev_abs_flags, code);
+}
+
+inline void
+evdev_support_msc(struct evdev_dev *evdev, uint16_t code)
+{
+
+       KASSERT(code < MSC_CNT, ("invalid evdev msc property"));
+       bit_set(evdev->ev_msc_flags, code);
+}
+
+
+inline void
+evdev_support_led(struct evdev_dev *evdev, uint16_t code)
+{
+
+       KASSERT(code < LED_CNT, ("invalid evdev led property"));
+       bit_set(evdev->ev_led_flags, code);
+}
+
+inline void
+evdev_support_snd(struct evdev_dev *evdev, uint16_t code)
+{
+
+       KASSERT(code < SND_CNT, ("invalid evdev snd property"));
+       bit_set(evdev->ev_snd_flags, code);
+}
+
+inline void
+evdev_support_sw(struct evdev_dev *evdev, uint16_t code)
+{
+
+       KASSERT(code < SW_CNT, ("invalid evdev sw property"));
+       bit_set(evdev->ev_sw_flags, code);
+}
+
+bool
+evdev_event_supported(struct evdev_dev *evdev, uint16_t type)
+{
+
+       KASSERT(type < EV_CNT, ("invalid evdev event property"));
+       return (bit_test(evdev->ev_type_flags, type));
+}
+
+inline void
+evdev_set_absinfo(struct evdev_dev *evdev, uint16_t axis,
+    struct input_absinfo *absinfo)
+{
+
+       KASSERT(axis < ABS_CNT, ("invalid evdev abs property"));
+
+       if (axis == ABS_MT_SLOT &&
+           (absinfo->maximum < 1 || absinfo->maximum >= MAX_MT_SLOTS))
+               return;
+
+       if (evdev->ev_absinfo == NULL)
+               evdev->ev_absinfo = evdev_alloc_absinfo();
+
+       if (axis == ABS_MT_SLOT)
+               evdev->ev_absinfo[ABS_MT_SLOT].maximum = absinfo->maximum;
+       else
+               memcpy(&evdev->ev_absinfo[axis], absinfo,
+                   sizeof(struct input_absinfo));
+}
+
+inline void
+evdev_set_repeat_params(struct evdev_dev *evdev, uint16_t property, int value)
+{
+
+       KASSERT(property < REP_CNT, ("invalid evdev repeat property"));
+       evdev->ev_rep[property] = value;
+}
+
+inline void
+evdev_set_flag(struct evdev_dev *evdev, uint16_t flag)
+{
+
+       KASSERT(flag < EVDEV_FLAG_CNT, ("invalid evdev flag property"));
+       bit_set(evdev->ev_flags, flag);
+}
+
+static int
+evdev_check_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
+    int32_t value)
+{
+
+       if (type >= EV_CNT)
+               return (EINVAL);
+
+       /* Allow SYN events implicitly */
+       if (type != EV_SYN && !evdev_event_supported(evdev, type))
+               return (EINVAL);
+
+       switch (type) {
+       case EV_SYN:
+               if (code >= SYN_CNT)
+                       return (EINVAL);
+               break;
+
+       case EV_KEY:
+               if (code >= KEY_CNT)
+                       return (EINVAL);
+               if (!bit_test(evdev->ev_key_flags, code))
+                       return (EINVAL);
+               break;
+
+       case EV_REL:
+               if (code >= REL_CNT)
+                       return (EINVAL);
+               if (!bit_test(evdev->ev_rel_flags, code))
+                       return (EINVAL);
+               break;
+
+       case EV_ABS:
+               if (code >= ABS_CNT)
+                       return (EINVAL);
+               if (!bit_test(evdev->ev_abs_flags, code))
+                       return (EINVAL);
+               if (code == ABS_MT_SLOT &&
+                   (value < 0 || value > MAXIMAL_MT_SLOT(evdev)))
+                       return (EINVAL);
+               if (ABS_IS_MT(code) && evdev->ev_mt == NULL &&
+                   bit_test(evdev->ev_abs_flags, ABS_MT_SLOT))
+                       return (EINVAL);
+               break;
+
+       case EV_MSC:
+               if (code >= MSC_CNT)
+                       return (EINVAL);
+               if (!bit_test(evdev->ev_msc_flags, code))
+                       return (EINVAL);
+               break;
+
+       case EV_LED:
+               if (code >= LED_CNT)
+                       return (EINVAL);
+               if (!bit_test(evdev->ev_led_flags, code))
+                       return (EINVAL);
+               break;
+
+       case EV_SND:
+               if (code >= SND_CNT)
+                       return (EINVAL);
+               if (!bit_test(evdev->ev_snd_flags, code))
+                       return (EINVAL);
+               break;
+
+       case EV_SW:
+               if (code >= SW_CNT)
+                       return (EINVAL);
+               if (!bit_test(evdev->ev_sw_flags, code))
+                       return (EINVAL);
+               break;
+
+       case EV_REP:
+               if (code >= REP_CNT)
+                       return (EINVAL);
+               break;
+
+       default:
+               return (EINVAL);
+       }
+
+       return (0);
+}
+
+static void
+evdev_modify_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
+    int32_t *value)
+{
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       switch (type) {
+       case EV_KEY:
+               if (!evdev_event_supported(evdev, EV_REP))
+                       break;
+
+               if (!bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) {
+                       /* Detect driver key repeats. */
+                       if (bit_test(evdev->ev_key_states, code) &&
+                           *value == KEY_EVENT_DOWN)
+                               *value = KEY_EVENT_REPEAT;
+               } else {
+                       /* Start/stop callout for evdev repeats */
+                       if (bit_test(evdev->ev_key_states, code) == !*value) {
+                               if (*value == KEY_EVENT_DOWN)
+                                       evdev_start_repeat(evdev, code);
+                               else
+                                       evdev_stop_repeat(evdev);
+                       }
+               }
+               break;
+
+       case EV_ABS:
+               /* TBD: implement fuzz */
+               break;
+       }
+}
+
+static enum evdev_sparse_result
+evdev_sparse_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
+    int32_t value)
+{
+       int32_t last_mt_slot;
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       /*
+        * For certain event types, update device state bits
+        * and convert level reporting to edge reporting
+        */
+       switch (type) {
+       case EV_KEY:
+               switch (value) {
+               case KEY_EVENT_UP:
+               case KEY_EVENT_DOWN:
+                       if (bit_test(evdev->ev_key_states, code) == value)
+                               return (EV_SKIP_EVENT);
+                       bit_change(evdev->ev_key_states, code, value);
+                       break;
+
+               case KEY_EVENT_REPEAT:
+                       if (bit_test(evdev->ev_key_states, code) == 0 ||
+                           !evdev_event_supported(evdev, EV_REP))
+                               return (EV_SKIP_EVENT);
+                       break;
+
+               default:
+                        return (EV_SKIP_EVENT);
+               }
+               break;
+
+       case EV_LED:
+               if (bit_test(evdev->ev_led_states, code) == value)
+                       return (EV_SKIP_EVENT);
+               bit_change(evdev->ev_led_states, code, value);
+               break;
+
+       case EV_SND:
+               if (bit_test(evdev->ev_snd_states, code) == value)
+                       return (EV_SKIP_EVENT);
+               bit_change(evdev->ev_snd_states, code, value);
+               break;
+
+       case EV_SW:
+               if (bit_test(evdev->ev_sw_states, code) == value)
+                       return (EV_SKIP_EVENT);
+               bit_change(evdev->ev_sw_states, code, value);
+               break;
+
+       case EV_REP:
+               if (evdev->ev_rep[code] == value)
+                       return (EV_SKIP_EVENT);
+               evdev_set_repeat_params(evdev, code, value);
+               break;
+
+       case EV_REL:
+               if (value == 0)
+                       return (EV_SKIP_EVENT);
+               break;
+
+       /* For EV_ABS, save last value in absinfo and ev_mt_states */
+       case EV_ABS:
+               switch (code) {
+               case ABS_MT_SLOT:
+                       /* Postpone ABS_MT_SLOT till next event */
+                       evdev_set_last_mt_slot(evdev, value);
+                       return (EV_SKIP_EVENT);
+
+               case ABS_MT_FIRST ... ABS_MT_LAST:
+                       /* Pass MT protocol type A events as is */
+                       if (!bit_test(evdev->ev_abs_flags, ABS_MT_SLOT))
+                               break;
+                       /* Don`t repeat MT protocol type B events */
+                       last_mt_slot = evdev_get_last_mt_slot(evdev);
+                       if (evdev_get_mt_value(evdev, last_mt_slot, code)
+                            == value)
+                               return (EV_SKIP_EVENT);
+                       evdev_set_mt_value(evdev, last_mt_slot, code, value);
+                       if (last_mt_slot != CURRENT_MT_SLOT(evdev)) {
+                               CURRENT_MT_SLOT(evdev) = last_mt_slot;
+                               evdev->ev_report_opened = true;
+                               return (EV_REPORT_MT_SLOT);
+                       }
+                       break;
+
+               default:
+                       if (evdev->ev_absinfo[code].value == value)
+                               return (EV_SKIP_EVENT);
+                       evdev->ev_absinfo[code].value = value;
+               }
+               break;
+
+       case EV_SYN:
+               if (code == SYN_REPORT) {
+                       /* Count empty reports as well as non empty */
+                       evdev->ev_report_count++;
+                       /* Skip empty reports */
+                       if (!evdev->ev_report_opened)
+                               return (EV_SKIP_EVENT);
+                       evdev->ev_report_opened = false;
+                       return (EV_REPORT_EVENT);
+               }
+               break;
+       }
+
+       evdev->ev_report_opened = true;
+       return (EV_REPORT_EVENT);
+}
+
+static void
+evdev_propagate_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
+    int32_t value)
+{
+       struct evdev_client *client;
+
+       debugf(evdev, "%s pushed event %d/%d/%d",
+           evdev->ev_shortname, type, code, value);
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       /* Propagate event through all clients */
+       LIST_FOREACH(client, &evdev->ev_clients, ec_link) {
+               if (evdev->ev_grabber != NULL && evdev->ev_grabber != client)
+                       continue;
+
+               EVDEV_CLIENT_LOCKQ(client);
+               evdev_client_push(client, type, code, value);
+               if (type == EV_SYN && code == SYN_REPORT)
+                       evdev_notify_event(client);
+               EVDEV_CLIENT_UNLOCKQ(client);
+       }
+
+       evdev->ev_event_count++;
+}
+
+void
+evdev_send_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
+    int32_t value)
+{
+       enum evdev_sparse_result sparse;
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       sparse =  evdev_sparse_event(evdev, type, code, value);
+       switch (sparse) {
+       case EV_REPORT_MT_SLOT:
+               /* report postponed ABS_MT_SLOT */
+               evdev_propagate_event(evdev, EV_ABS, ABS_MT_SLOT,
+                   CURRENT_MT_SLOT(evdev));
+               /* FALLTHROUGH */
+       case EV_REPORT_EVENT:
+               evdev_propagate_event(evdev, type, code, value);
+               /* FALLTHROUGH */
+       case EV_SKIP_EVENT:
+               break;
+       }
+}
+
+int
+evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
+    int32_t value)
+{
+
+       if (evdev_check_event(evdev, type, code, value) != 0)
+               return (EINVAL);
+
+       EVDEV_ENTER(evdev);
+
+       evdev_modify_event(evdev, type, code, &value);
+       if (type == EV_SYN && code == SYN_REPORT &&
+            bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL))
+               evdev_send_mt_autorel(evdev);
+       if (type == EV_SYN && code == SYN_REPORT && evdev->ev_report_opened &&
+           bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT))
+               evdev_send_mt_compat(evdev);
+       evdev_send_event(evdev, type, code, value);
+
+       EVDEV_EXIT(evdev);
+
+       return (0);
+}
+
+int
+evdev_inject_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
+    int32_t value)
+{
+       int ret = 0;
+
+       switch (type) {
+       case EV_REP:
+               /* evdev repeats should not be processed by hardware driver */
+               if (bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT))
+                       goto push;
+               /* FALLTHROUGH */
+       case EV_LED:
+       case EV_MSC:
+       case EV_SND:
+       case EV_FF:
+               if (evdev->ev_methods != NULL &&
+                   evdev->ev_methods->ev_event != NULL)
+                       evdev->ev_methods->ev_event(evdev, evdev->ev_softc,
+                           type, code, value);
+               /*
+                * Leds and driver repeats should be reported in ev_event
+                * method body to interoperate with kbdmux states and rates
+                * propagation so both ways (ioctl and evdev) of changing it
+                * will produce only one evdev event report to client.
+                */
+               if (type == EV_LED || type == EV_REP)
+                       break;
+               /* FALLTHROUGH */
+       case EV_SYN:
+       case EV_KEY:
+       case EV_REL:
+       case EV_ABS:
+       case EV_SW:
+push:
+               ret = evdev_push_event(evdev, type,  code, value);
+               break;
+
+       default:
+               ret = EINVAL;
+       }
+
+       return (ret);
+}
+
+int
+evdev_register_client(struct evdev_dev *evdev, struct evdev_client *client)
+{
+       int ret = 0;
+
+       debugf(evdev, "adding new client for device %s", evdev->ev_shortname);
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       if (LIST_EMPTY(&evdev->ev_clients) && evdev->ev_methods != NULL &&
+           evdev->ev_methods->ev_open != NULL) {
+               debugf(evdev, "calling ev_open() on device %s",
+                   evdev->ev_shortname);
+               ret = evdev->ev_methods->ev_open(evdev, evdev->ev_softc);
+       }
+       if (ret == 0)
+               LIST_INSERT_HEAD(&evdev->ev_clients, client, ec_link);
+       return (ret);
+}
+
+void
+evdev_dispose_client(struct evdev_dev *evdev, struct evdev_client *client)
+{
+       debugf(evdev, "removing client for device %s", evdev->ev_shortname);
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       LIST_REMOVE(client, ec_link);
+       if (LIST_EMPTY(&evdev->ev_clients)) {
+               if (evdev->ev_methods != NULL &&
+                   evdev->ev_methods->ev_close != NULL)
+                       evdev->ev_methods->ev_close(evdev, evdev->ev_softc);
+               if (evdev_event_supported(evdev, EV_REP) &&
+                   bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT))
+                       evdev_stop_repeat(evdev);
+       }
+       evdev_release_client(evdev, client);
+}
+
+int
+evdev_grab_client(struct evdev_dev *evdev, struct evdev_client *client)
+{
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       if (evdev->ev_grabber != NULL)
+               return (EBUSY);
+
+       evdev->ev_grabber = client;
+
+       return (0);
+}
+
+int
+evdev_release_client(struct evdev_dev *evdev, struct evdev_client *client)
+{
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       if (evdev->ev_grabber != client)
+               return (EINVAL);
+
+       evdev->ev_grabber = NULL;
+
+       return (0);
+}
+
+static void
+evdev_repeat_callout(void *arg)
+{
+       struct evdev_dev *evdev = (struct evdev_dev *)arg;
+
+       evdev_send_event(evdev, EV_KEY, evdev->ev_rep_key, KEY_EVENT_REPEAT);
+       evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1);
+
+       if (evdev->ev_rep[REP_PERIOD])
+               callout_reset(&evdev->ev_rep_callout,
+                   evdev->ev_rep[REP_PERIOD] * hz / 1000,
+                   evdev_repeat_callout, evdev);
+       else
+               evdev->ev_rep_key = KEY_RESERVED;
+}
+
+static void
+evdev_start_repeat(struct evdev_dev *evdev, uint16_t key)
+{
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       if (evdev->ev_rep[REP_DELAY]) {
+               evdev->ev_rep_key = key;
+               callout_reset(&evdev->ev_rep_callout,
+                   evdev->ev_rep[REP_DELAY] * hz / 1000,
+                   evdev_repeat_callout, evdev);
+       }
+}
+
+static void
+evdev_stop_repeat(struct evdev_dev *evdev)
+{
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       if (evdev->ev_rep_key != KEY_RESERVED) {
+               callout_stop(&evdev->ev_rep_callout);
+               evdev->ev_rep_key = KEY_RESERVED;
+       }
+}
+
+static int
+evdev_load(module_t mod, int cmd, void *arg)
+{
+       int error = 0;
+
+       switch (cmd) {
+       case MOD_LOAD:
+               kprintf("evdev device loaded.\n");
+               break;
+       case MOD_UNLOAD:
+               kprintf("evdev device unloaded.\n");
+               break;
+
+       default:
+               error = EOPNOTSUPP;
+               break;
+    }
+
+    return (error);
+}
+
+DEV_MODULE(evdev, evdev_load, NULL);
+MODULE_VERSION(evdev, 1);
diff --git a/sys/dev/misc/evdev/evdev.h b/sys/dev/misc/evdev/evdev.h
new file mode 100644 (file)
index 0000000..de177b9
--- /dev/null
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef        _DEV_EVDEV_EVDEV_H
+#define        _DEV_EVDEV_EVDEV_H
+
+#include <sys/types.h>
+#include <sys/kbio.h>
+#include <dev/misc/evdev/input.h>
+#include <dev/misc/kbd/kbdreg.h>
+
+#define        NAMELEN         80
+
+struct evdev_dev;
+
+typedef int (evdev_open_t)(struct evdev_dev *, void *);
+typedef void (evdev_close_t)(struct evdev_dev *, void *);
+typedef void (evdev_event_t)(struct evdev_dev *, void *, uint16_t,
+    uint16_t, int32_t);
+typedef void (evdev_keycode_t)(struct evdev_dev *, void *,
+    struct input_keymap_entry *);
+
+/*
+ * Keyboard and mouse events recipient mask.
+ * evdev_rcpt_mask variable should be respected by keyboard and mouse drivers
+ * that are able to send events through both evdev and sysmouse/kbdmux
+ * interfaces so user can choose prefered one to not receive one event twice.
+ */
+#define        EVDEV_RCPT_SYSMOUSE     (1<<0)
+#define        EVDEV_RCPT_KBDMUX       (1<<1)
+#define        EVDEV_RCPT_HW_MOUSE     (1<<2)
+#define        EVDEV_RCPT_HW_KBD       (1<<3)
+extern int evdev_rcpt_mask;
+/*
+ * Sysmouse protocol does not support horizontal wheel movement reporting.
+ * To overcome this limitation different drivers use different sysmouse proto
+ * extensions. Set kern.evdev.sysmouse_t_axis to tell sysmouse evdev driver
+ * which protocol extension is used.
+ * 0 - do not extract horizontal wheel movement (default).
+ * 1 - ums(4) horizontal wheel encoding. T-axis is mapped to buttons 6 and 7
+ * 2 - psm(4) wheels encoding: z = 1,-1 - vert. wheel, z = 2,-2 - horiz. wheel
+ */
+enum
+{
+       EVDEV_SYSMOUSE_T_AXIS_NONE = 0,
+       EVDEV_SYSMOUSE_T_AXIS_UMS = 1,
+       EVDEV_SYSMOUSE_T_AXIS_PSM = 2,
+};
+extern int evdev_sysmouse_t_axis;
+
+#define        ABS_MT_FIRST    ABS_MT_TOUCH_MAJOR
+#define        ABS_MT_LAST     ABS_MT_TOOL_Y
+#define        ABS_IS_MT(x)    ((x) >= ABS_MT_FIRST && (x) <= ABS_MT_LAST)
+#define        ABS_MT_INDEX(x) ((x) - ABS_MT_FIRST)
+#define        MT_CNT          (ABS_MT_INDEX(ABS_MT_LAST) + 1)
+/* Multitouch protocol type A */
+#define        MAX_MT_REPORTS  5
+/* Multitouch protocol type B interface */
+#define        MAX_MT_SLOTS    16
+
+#define        EVDEV_FLAG_SOFTREPEAT   0x00    /* use evdev to repeat keys */
+#define        EVDEV_FLAG_MT_STCOMPAT  0x01    /* autogenerate ST-compatible events
+                                        * for MT protocol type B reports */
+#define        EVDEV_FLAG_MT_AUTOREL   0x02    /* Autorelease MT-slots not listed in
+                                        * current MT protocol type B report */
+#define        EVDEV_FLAG_MAX          0x1F
+#define        EVDEV_FLAG_CNT          (EVDEV_FLAG_MAX + 1)
+
+struct evdev_methods
+{
+       evdev_open_t            *ev_open;
+       evdev_close_t           *ev_close;
+       evdev_event_t           *ev_event;
+       evdev_keycode_t         *ev_get_keycode;
+       evdev_keycode_t         *ev_set_keycode;
+};
+
+/* Input device interface: */
+struct evdev_dev *evdev_alloc(void);
+void evdev_free(struct evdev_dev *);
+void evdev_set_name(struct evdev_dev *, const char *);
+void evdev_set_id(struct evdev_dev *, uint16_t, uint16_t, uint16_t, uint16_t);
+void evdev_set_phys(struct evdev_dev *, const char *);
+void evdev_set_serial(struct evdev_dev *, const char *);
+void evdev_set_methods(struct evdev_dev *, void *,
+    const struct evdev_methods *);
+int evdev_register(struct evdev_dev *);
+int evdev_register_mtx(struct evdev_dev *, struct lock *); //dfly lock
+int evdev_unregister(struct evdev_dev *);
+int evdev_push_event(struct evdev_dev *, uint16_t, uint16_t, int32_t);
+void evdev_support_prop(struct evdev_dev *, uint16_t);
+void evdev_support_event(struct evdev_dev *, uint16_t);
+void evdev_support_key(struct evdev_dev *, uint16_t);
+void evdev_support_rel(struct evdev_dev *, uint16_t);
+void evdev_support_abs(struct evdev_dev *, uint16_t, int32_t, int32_t, int32_t,
+   int32_t, int32_t, int32_t);
+void evdev_support_msc(struct evdev_dev *, uint16_t);
+void evdev_support_led(struct evdev_dev *, uint16_t);
+void evdev_support_snd(struct evdev_dev *, uint16_t);
+void evdev_support_sw(struct evdev_dev *, uint16_t);
+void evdev_set_repeat_params(struct evdev_dev *, uint16_t, int);
+int evdev_set_report_size(struct evdev_dev *, size_t);
+void evdev_set_flag(struct evdev_dev *, uint16_t);
+
+/* Multitouch related functions: */
+int32_t evdev_get_mt_slot_by_tracking_id(struct evdev_dev *, int32_t);
+void evdev_support_nfingers(struct evdev_dev *, int32_t);
+void evdev_support_mt_compat(struct evdev_dev *);
+void evdev_push_nfingers(struct evdev_dev *, int32_t);
+void evdev_push_mt_compat(struct evdev_dev *);
+
+/* Utility functions: */
+uint16_t evdev_hid2key(int);
+void evdev_support_all_known_keys(struct evdev_dev *);
+uint16_t evdev_scancode2key(int *, int);
+void evdev_push_mouse_btn(struct evdev_dev *, int);
+void evdev_push_leds(struct evdev_dev *, int);
+void evdev_push_repeats(struct evdev_dev *, keyboard_t *);
+evdev_event_t evdev_ev_kbd_event;
+
+/* Event reporting shortcuts: */
+static __inline int
+evdev_sync(struct evdev_dev *evdev)
+{
+
+       return (evdev_push_event(evdev, EV_SYN, SYN_REPORT, 1));
+}
+
+static __inline int
+evdev_mt_sync(struct evdev_dev *evdev)
+{
+
+       return (evdev_push_event(evdev, EV_SYN, SYN_MT_REPORT, 1));
+}
+
+static __inline int
+evdev_push_key(struct evdev_dev *evdev, uint16_t code, int32_t value)
+{
+
+       return (evdev_push_event(evdev, EV_KEY, code, value != 0));
+}
+
+static __inline int
+evdev_push_rel(struct evdev_dev *evdev, uint16_t code, int32_t value)
+{
+
+       return (evdev_push_event(evdev, EV_REL, code, value));
+}
+
+static __inline int
+evdev_push_abs(struct evdev_dev *evdev, uint16_t code, int32_t value)
+{
+
+       return (evdev_push_event(evdev, EV_ABS, code, value));
+}
+
+static __inline int
+evdev_push_msc(struct evdev_dev *evdev, uint16_t code, int32_t value)
+{
+
+       return (evdev_push_event(evdev, EV_MSC, code, value));
+}
+
+static __inline int
+evdev_push_led(struct evdev_dev *evdev, uint16_t code, int32_t value)
+{
+
+       return (evdev_push_event(evdev, EV_LED, code, value != 0));
+}
+
+static __inline int
+evdev_push_snd(struct evdev_dev *evdev, uint16_t code, int32_t value)
+{
+
+       return (evdev_push_event(evdev, EV_SND, code, value != 0));
+}
+
+static __inline int
+evdev_push_sw(struct evdev_dev *evdev, uint16_t code, int32_t value)
+{
+
+       return (evdev_push_event(evdev, EV_SW, code, value != 0));
+}
+
+#endif /* _DEV_EVDEV_EVDEV_H */
diff --git a/sys/dev/misc/evdev/evdev_mt.c b/sys/dev/misc/evdev/evdev_mt.c
new file mode 100644 (file)
index 0000000..cf4bf3c
--- /dev/null
@@ -0,0 +1,285 @@
+/*-
+ * Copyright (c) 2016 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include "opt_evdev.h"
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+
+#include <dev/misc/evdev/evdev.h>
+#include <dev/misc/evdev/evdev_private.h>
+#include <dev/misc/evdev/input.h>
+
+#ifdef DEBUG
+#define        debugf(fmt, args...)    kprintf("evdev: " fmt "\n", ##args)
+#else
+#define        debugf(fmt, args...)
+#endif
+
+static uint16_t evdev_fngmap[] = {
+       BTN_TOOL_FINGER,
+       BTN_TOOL_DOUBLETAP,
+       BTN_TOOL_TRIPLETAP,
+       BTN_TOOL_QUADTAP,
+       BTN_TOOL_QUINTTAP,
+};
+
+static uint16_t evdev_mtstmap[][2] = {
+       { ABS_MT_POSITION_X, ABS_X },
+       { ABS_MT_POSITION_Y, ABS_Y },
+       { ABS_MT_PRESSURE, ABS_PRESSURE },
+       { ABS_MT_TOUCH_MAJOR, ABS_TOOL_WIDTH },
+};
+
+struct evdev_mt_slot {
+       uint64_t ev_report;
+       int32_t ev_mt_states[MT_CNT];
+};
+
+struct evdev_mt {
+       int32_t ev_mt_last_reported_slot;
+       struct evdev_mt_slot ev_mt_slots[];
+};
+
+void
+evdev_mt_init(struct evdev_dev *evdev)
+{
+       int32_t slot, slots;
+
+       slots = MAXIMAL_MT_SLOT(evdev) + 1;
+
+       evdev->ev_mt = kmalloc(offsetof(struct evdev_mt, ev_mt_slots) +
+            sizeof(struct evdev_mt_slot) * slots, M_EVDEV, M_WAITOK | M_ZERO);
+
+       /* Initialize multitouch protocol type B states */
+       for (slot = 0; slot < slots; slot++) {
+               /*
+                * .ev_report should not be initialized to initial value of
+                * report counter (0) as it brokes free slot detection in
+                * evdev_get_mt_slot_by_tracking_id. So initialize it to -1
+                */
+               evdev->ev_mt->ev_mt_slots[slot] = (struct evdev_mt_slot) {
+                       .ev_report = 0xFFFFFFFFFFFFFFFFULL,
+                       .ev_mt_states[ABS_MT_INDEX(ABS_MT_TRACKING_ID)] = -1,
+               };
+       }
+
+       if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT))
+               evdev_support_mt_compat(evdev);
+}
+
+void
+evdev_mt_free(struct evdev_dev *evdev)
+{
+
+       kfree(evdev->ev_mt, M_EVDEV);
+}
+
+int32_t
+evdev_get_last_mt_slot(struct evdev_dev *evdev)
+{
+
+       return (evdev->ev_mt->ev_mt_last_reported_slot);
+}
+
+void
+evdev_set_last_mt_slot(struct evdev_dev *evdev, int32_t slot)
+{
+
+       evdev->ev_mt->ev_mt_slots[slot].ev_report = evdev->ev_report_count;
+       evdev->ev_mt->ev_mt_last_reported_slot = slot;
+}
+
+inline int32_t
+evdev_get_mt_value(struct evdev_dev *evdev, int32_t slot, int16_t code)
+{
+
+       return (evdev->ev_mt->
+           ev_mt_slots[slot].ev_mt_states[ABS_MT_INDEX(code)]);
+}
+
+inline void
+evdev_set_mt_value(struct evdev_dev *evdev, int32_t slot, int16_t code,
+    int32_t value)
+{
+
+       evdev->ev_mt->ev_mt_slots[slot].ev_mt_states[ABS_MT_INDEX(code)] =
+           value;
+}
+
+int32_t
+evdev_get_mt_slot_by_tracking_id(struct evdev_dev *evdev, int32_t tracking_id)
+{
+       int32_t tr_id, slot, free_slot = -1;
+
+       for (slot = 0; slot <= MAXIMAL_MT_SLOT(evdev); slot++) {
+               tr_id = evdev_get_mt_value(evdev, slot, ABS_MT_TRACKING_ID);
+               if (tr_id == tracking_id)
+                       return (slot);
+               /*
+                * Its possible that slot will be reassigned in a place of just
+                * released one within the same report. To avoid this compare
+                * report counter with slot`s report number updated with each
+                * ABS_MT_TRACKING_ID change.
+                */
+               if (free_slot == -1 && tr_id == -1 &&
+                   evdev->ev_mt->ev_mt_slots[slot].ev_report !=
+                   evdev->ev_report_count)
+                       free_slot = slot;
+       }
+
+       return (free_slot);
+}
+
+void
+evdev_support_nfingers(struct evdev_dev *evdev, int32_t nfingers)
+{
+       int32_t i;
+
+       for (i = 0; i < MIN(nitems(evdev_fngmap), nfingers); i++)
+               evdev_support_key(evdev, evdev_fngmap[i]);
+}
+
+void
+evdev_support_mt_compat(struct evdev_dev *evdev)
+{
+       int32_t i;
+
+       if (evdev->ev_absinfo == NULL)
+               return;
+
+       evdev_support_event(evdev, EV_KEY);
+       evdev_support_key(evdev, BTN_TOUCH);
+
+       /* Touchscreens should not advertise tap tool capabilities */
+       if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT))
+               evdev_support_nfingers(evdev, MAXIMAL_MT_SLOT(evdev) + 1);
+
+       /* Echo 0-th MT-slot as ST-slot */
+       for (i = 0; i < nitems(evdev_mtstmap); i++)
+               if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][0]))
+                       evdev_support_abs(evdev, evdev_mtstmap[i][1],
+                           evdev->ev_absinfo[evdev_mtstmap[i][0]].value,
+                           evdev->ev_absinfo[evdev_mtstmap[i][0]].minimum,
+                           evdev->ev_absinfo[evdev_mtstmap[i][0]].maximum,
+                           evdev->ev_absinfo[evdev_mtstmap[i][0]].fuzz,
+                           evdev->ev_absinfo[evdev_mtstmap[i][0]].flat,
+                           evdev->ev_absinfo[evdev_mtstmap[i][0]].resolution);
+}
+
+static int32_t
+evdev_count_fingers(struct evdev_dev *evdev)
+{
+       int32_t nfingers = 0, i;
+
+       for (i = 0; i <= MAXIMAL_MT_SLOT(evdev); i++)
+               if (evdev_get_mt_value(evdev, i, ABS_MT_TRACKING_ID) != -1)
+                       nfingers++;
+
+       return (nfingers);
+}
+
+static void
+evdev_send_nfingers(struct evdev_dev *evdev, int32_t nfingers)
+{
+       int32_t i;
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       if (nfingers > nitems(evdev_fngmap))
+               nfingers = nitems(evdev_fngmap);
+
+       for (i = 0; i < nitems(evdev_fngmap); i++)
+               evdev_send_event(evdev, EV_KEY, evdev_fngmap[i],
+                   nfingers == i + 1);
+}
+
+void
+evdev_push_nfingers(struct evdev_dev *evdev, int32_t nfingers)
+{
+
+       EVDEV_ENTER(evdev);
+       evdev_send_nfingers(evdev, nfingers);
+       EVDEV_EXIT(evdev);
+}
+
+void
+evdev_send_mt_compat(struct evdev_dev *evdev)
+{
+       int32_t nfingers, i;
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       nfingers = evdev_count_fingers(evdev);
+       evdev_send_event(evdev, EV_KEY, BTN_TOUCH, nfingers > 0);
+
+       if (evdev_get_mt_value(evdev, 0, ABS_MT_TRACKING_ID) != -1)
+               /* Echo 0-th MT-slot as ST-slot */
+               for (i = 0; i < nitems(evdev_mtstmap); i++)
+                       if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][1]))
+                               evdev_send_event(evdev, EV_ABS,
+                                   evdev_mtstmap[i][1],
+                                   evdev_get_mt_value(evdev, 0,
+                                   evdev_mtstmap[i][0]));
+
+       /* Touchscreens should not report tool taps */
+       if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT))
+               evdev_send_nfingers(evdev, nfingers);
+
+       if (nfingers == 0)
+               evdev_send_event(evdev, EV_ABS, ABS_PRESSURE, 0);
+}
+
+void
+evdev_push_mt_compat(struct evdev_dev *evdev)
+{
+
+       EVDEV_ENTER(evdev);
+       evdev_send_mt_compat(evdev);
+       EVDEV_EXIT(evdev);
+}
+
+void
+evdev_send_mt_autorel(struct evdev_dev *evdev)
+{
+       int32_t slot;
+
+       EVDEV_LOCK_ASSERT(evdev);
+
+       for (slot = 0; slot <= MAXIMAL_MT_SLOT(evdev); slot++) {
+               if (evdev->ev_mt->ev_mt_slots[slot].ev_report !=
+                   evdev->ev_report_count &&
+                   evdev_get_mt_value(evdev, slot, ABS_MT_TRACKING_ID) != -1){
+                       evdev_send_event(evdev, EV_ABS, ABS_MT_SLOT, slot);
+                       evdev_send_event(evdev, EV_ABS, ABS_MT_TRACKING_ID,
+                           -1);
+               }
+       }
+}
diff --git a/sys/dev/misc/evdev/evdev_private.h b/sys/dev/misc/evdev/evdev_private.h
new file mode 100644 (file)
index 0000000..76e8004
--- /dev/null
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef        _DEV_EVDEV_EVDEV_PRIVATE_H
+#define        _DEV_EVDEV_EVDEV_PRIVATE_H
+
+#include <sys/kbio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/event.h>
+
+/* Use a local copy of FreeBSD's bitstring.h. */
+#include "freebsd-bitstring.h"
+
+#include <dev/misc/evdev/evdev.h>
+#include <dev/misc/evdev/input.h>
+#include <dev/misc/kbd/kbdreg.h>
+
+#define        NAMELEN         80
+
+/*
+ * bitstr_t implementation must be identical to one found in EVIOCG*
+ * libevdev ioctls. Our bitstring(3) API is compatible since r299090.
+ */
+
+/*
+ * Note on DragonFly. We will use a local copy of FreeBSD's bitstring.h
+ * (freebsd-bitstring.h above) to guarantee bitstr_t implementation is
+ * compliant with libevdev ioctls.
+ */
+_Static_assert(sizeof(bitstr_t) == sizeof(unsigned long),
+    "bitstr_t size mismatch");
+
+MALLOC_DECLARE(M_EVDEV);
+
+struct evdev_client;
+struct evdev_mt;
+
+#define        CURRENT_MT_SLOT(evdev)  ((evdev)->ev_absinfo[ABS_MT_SLOT].value)
+#define        MAXIMAL_MT_SLOT(evdev)  ((evdev)->ev_absinfo[ABS_MT_SLOT].maximum)
+
+enum evdev_key_events
+{
+       KEY_EVENT_UP,
+       KEY_EVENT_DOWN,
+       KEY_EVENT_REPEAT
+};
+
+/* evdev clock IDs in Linux semantic */
+enum evdev_clock_id
+{
+       EV_CLOCK_REALTIME = 0,  /* UTC clock */
+       EV_CLOCK_MONOTONIC,     /* monotonic, stops on suspend */
+       EV_CLOCK_BOOTTIME       /* monotonic, suspend-awared */
+};
+
+enum evdev_lock_type
+{
+       EV_LOCK_INTERNAL = 0,   /* Internal evdev mutex */
+       EV_LOCK_MTX,            /* Driver`s mutex */
+};
+
+struct evdev_dev
+{
+       char                    ev_name[NAMELEN];
+       char                    ev_shortname[NAMELEN];
+       char                    ev_serial[NAMELEN];
+       cdev_t                  ev_cdev;
+       int                     ev_unit;
+       enum evdev_lock_type    ev_lock_type;
+       struct lock *           ev_lock;
+       struct lock             ev_mtx;
+       struct input_id         ev_id;
+       struct evdev_client *   ev_grabber;
+       size_t                  ev_report_size;
+
+       /* Supported features: */
+       bitstr_t                bit_decl(ev_prop_flags, INPUT_PROP_CNT);
+       bitstr_t                bit_decl(ev_type_flags, EV_CNT);
+       bitstr_t                bit_decl(ev_key_flags, KEY_CNT);
+       bitstr_t                bit_decl(ev_rel_flags, REL_CNT);
+       bitstr_t                bit_decl(ev_abs_flags, ABS_CNT);
+       bitstr_t                bit_decl(ev_msc_flags, MSC_CNT);
+       bitstr_t                bit_decl(ev_led_flags, LED_CNT);
+       bitstr_t                bit_decl(ev_snd_flags, SND_CNT);
+       bitstr_t                bit_decl(ev_sw_flags, SW_CNT);
+       struct input_absinfo *  ev_absinfo;
+       bitstr_t                bit_decl(ev_flags, EVDEV_FLAG_CNT);
+
+       /* Repeat parameters & callout: */
+       int                     ev_rep[REP_CNT];
+       struct callout          ev_rep_callout;
+       uint16_t                ev_rep_key;
+
+       /* State: */
+       bitstr_t                bit_decl(ev_key_states, KEY_CNT);
+       bitstr_t                bit_decl(ev_led_states, LED_CNT);
+       bitstr_t                bit_decl(ev_snd_states, SND_CNT);
+       bitstr_t                bit_decl(ev_sw_states, SW_CNT);
+       bool                    ev_report_opened;
+
+       /* Multitouch protocol type B state: */
+       struct evdev_mt *       ev_mt;
+
+       /* Counters: */
+       uint64_t                ev_event_count;
+       uint64_t                ev_report_count;
+
+       /* Parent driver callbacks: */
+       const struct evdev_methods * ev_methods;
+       void *                  ev_softc;
+
+       LIST_ENTRY(evdev_dev) ev_link;
+       LIST_HEAD(, evdev_client) ev_clients;
+};
+
+#define        EVDEV_LOCK(evdev)        lockmgr((evdev)->ev_lock, LK_EXCLUSIVE)
+#define        EVDEV_UNLOCK(evdev)      lockmgr((evdev)->ev_lock, LK_RELEASE)
+#define        EVDEV_LOCK_ASSERT(evdev) KKASSERT(lockowned((evdev)->ev_lock) != 0)
+#define        EVDEV_ENTER(evdev)  do {                                        \
+       if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL)                  \
+               EVDEV_LOCK(evdev);                                      \
+       else                                                            \
+               EVDEV_LOCK_ASSERT(evdev);                               \
+} while (0)
+#define        EVDEV_EXIT(evdev)  do {                                         \
+       if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL)                  \
+               EVDEV_UNLOCK(evdev);                                    \
+} while (0)
+
+struct evdev_client
+{
+       struct evdev_dev *      ec_evdev;
+       struct lock             ec_buffer_mtx;
+       size_t                  ec_buffer_size;
+       size_t                  ec_buffer_head;
+       size_t                  ec_buffer_tail;
+       size_t                  ec_buffer_ready;
+       enum evdev_clock_id     ec_clock_id;
+       struct kqinfo           kqinfo;
+       struct sigio *          ec_sigio;
+       bool                    ec_async;
+       bool                    ec_revoked;
+       bool                    ec_blocked;
+       bool                    ec_selected;
+
+       LIST_ENTRY(evdev_client) ec_link;
+
+       struct input_event      ec_buffer[];
+};
+
+#define        EVDEV_CLIENT_LOCKQ(client)      lockmgr(&(client)->ec_buffer_mtx, \
+                                           LK_EXCLUSIVE|LK_CANRECURSE)
+#define        EVDEV_CLIENT_UNLOCKQ(client)    lockmgr(&(client)->ec_buffer_mtx, \
+                                           LK_RELEASE)
+#define EVDEV_CLIENT_LOCKQ_ASSERT(client) \
+    KKASSERT(lockowned(&(client)->ec_buffer_mtx) != 0)
+#define        EVDEV_CLIENT_EMPTYQ(client) \
+    ((client)->ec_buffer_head == (client)->ec_buffer_ready)
+#define        EVDEV_CLIENT_SIZEQ(client) \
+    (((client)->ec_buffer_ready + (client)->ec_buffer_size - \
+      (client)->ec_buffer_head) % (client)->ec_buffer_size)
+
+/* Input device interface: */
+void evdev_send_event(struct evdev_dev *, uint16_t, uint16_t, int32_t);
+int evdev_inject_event(struct evdev_dev *, uint16_t, uint16_t, int32_t);
+int evdev_cdev_create(struct evdev_dev *);
+int evdev_cdev_destroy(struct evdev_dev *);
+bool evdev_event_supported(struct evdev_dev *, uint16_t);
+void evdev_set_abs_bit(struct evdev_dev *, uint16_t);
+void evdev_set_absinfo(struct evdev_dev *, uint16_t, struct input_absinfo *);
+
+/* Client interface: */
+int evdev_register_client(struct evdev_dev *, struct evdev_client *);
+void evdev_dispose_client(struct evdev_dev *, struct evdev_client *);
+int evdev_grab_client(struct evdev_dev *, struct evdev_client *);
+int evdev_release_client(struct evdev_dev *, struct evdev_client *);
+void evdev_client_push(struct evdev_client *, uint16_t, uint16_t, int32_t);
+void evdev_notify_event(struct evdev_client *);
+void evdev_revoke_client(struct evdev_client *);
+
+/* Multitouch related functions: */
+void evdev_mt_init(struct evdev_dev *);
+void evdev_mt_free(struct evdev_dev *);
+int32_t evdev_get_last_mt_slot(struct evdev_dev *);
+void evdev_set_last_mt_slot(struct evdev_dev *, int32_t);
+int32_t evdev_get_mt_value(struct evdev_dev *, int32_t, int16_t);
+void evdev_set_mt_value(struct evdev_dev *, int32_t, int16_t, int32_t);
+void evdev_send_mt_compat(struct evdev_dev *);
+void evdev_send_mt_autorel(struct evdev_dev *);
+
+/* Utility functions: */
+void evdev_client_dumpqueue(struct evdev_client *);
+
+#endif /* _DEV_EVDEV_EVDEV_PRIVATE_H */
diff --git a/sys/dev/misc/evdev/evdev_utils.c b/sys/dev/misc/evdev/evdev_utils.c
new file mode 100644 (file)
index 0000000..b8f4c6a
--- /dev/null
@@ -0,0 +1,331 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kbio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+
+#include <dev/misc/evdev/evdev.h>
+#include <dev/misc/evdev/input.h>
+#include <dev/misc/kbd/kbdreg.h>
+
+#define        NONE    KEY_RESERVED
+
+static uint16_t evdev_usb_scancodes[256] = {
+       /* 0x00 - 0x27 */
+       NONE,   NONE,   NONE,   NONE,   KEY_A,  KEY_B,  KEY_C,  KEY_D,
+       KEY_E,  KEY_F,  KEY_G,  KEY_H,  KEY_I,  KEY_J,  KEY_K,  KEY_L,
+       KEY_M,  KEY_N,  KEY_O,  KEY_P,  KEY_Q,  KEY_R,  KEY_S,  KEY_T,
+       KEY_U,  KEY_V,  KEY_W,  KEY_X,  KEY_Y,  KEY_Z,  KEY_1,  KEY_2,
+       KEY_3,  KEY_4,  KEY_5,  KEY_6,  KEY_7,  KEY_8,  KEY_9,  KEY_0,
+       /* 0x28 - 0x3f */
+       KEY_ENTER,      KEY_ESC,        KEY_BACKSPACE,  KEY_TAB,
+       KEY_SPACE,      KEY_MINUS,      KEY_EQUAL,      KEY_LEFTBRACE,
+       KEY_RIGHTBRACE, KEY_BACKSLASH,  KEY_BACKSLASH,  KEY_SEMICOLON,
+       KEY_APOSTROPHE, KEY_GRAVE,      KEY_COMMA,      KEY_DOT,
+       KEY_SLASH,      KEY_CAPSLOCK,   KEY_F1,         KEY_F2,
+       KEY_F3,         KEY_F4,         KEY_F5,         KEY_F6,
+       /* 0x40 - 0x5f */
+       KEY_F7,         KEY_F8,         KEY_F9,         KEY_F10,
+       KEY_F11,        KEY_F12,        KEY_SYSRQ,      KEY_SCROLLLOCK,
+       KEY_PAUSE,      KEY_INSERT,     KEY_HOME,       KEY_PAGEUP,
+       KEY_DELETE,     KEY_END,        KEY_PAGEDOWN,   KEY_RIGHT,
+       KEY_LEFT,       KEY_DOWN,       KEY_UP,         KEY_NUMLOCK,
+       KEY_SLASH,      KEY_KPASTERISK, KEY_KPMINUS,    KEY_KPPLUS,
+       KEY_KPENTER,    KEY_KP1,        KEY_KP2,        KEY_KP3,
+       KEY_KP4,        KEY_KP5,        KEY_KP6,        KEY_KP7,
+       /* 0x60 - 0x7f */
+       KEY_KP8,        KEY_KP9,        KEY_KP0,        KEY_KPDOT,
+       KEY_102ND,      KEY_COMPOSE,    KEY_POWER,      KEY_KPEQUAL,
+       KEY_F13,        KEY_F14,        KEY_F15,        KEY_F16,
+       KEY_F17,        KEY_F18,        KEY_F19,        KEY_F20,
+       KEY_F21,        KEY_F22,        KEY_F23,        KEY_F24,
+       KEY_OPEN,       KEY_HELP,       KEY_PROPS,      KEY_FRONT,
+       KEY_STOP,       KEY_AGAIN,      KEY_UNDO,       KEY_CUT,
+       KEY_COPY,       KEY_PASTE,      KEY_FIND,       KEY_MUTE,
+       /* 0x80 - 0x9f */
+       KEY_VOLUMEUP,   KEY_VOLUMEDOWN, NONE,           NONE,
+       NONE,           KEY_KPCOMMA,    NONE,           KEY_RO,
+       KEY_KATAKANAHIRAGANA,   KEY_YEN,KEY_HENKAN,     KEY_MUHENKAN,
+       KEY_KPJPCOMMA,  NONE,           NONE,           NONE,
+       KEY_HANGEUL,    KEY_HANJA,      KEY_KATAKANA,   KEY_HIRAGANA,
+       KEY_ZENKAKUHANKAKU,     NONE,   NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       /* 0xa0 - 0xbf */
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       /* 0xc0 - 0xdf */
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       /* 0xe0 - 0xff */
+       KEY_LEFTCTRL,   KEY_LEFTSHIFT,  KEY_LEFTALT,    KEY_LEFTMETA,
+       KEY_RIGHTCTRL,  KEY_RIGHTSHIFT, KEY_RIGHTALT,   KEY_RIGHTMETA,
+       KEY_PLAYPAUSE,  KEY_STOPCD,     KEY_PREVIOUSSONG,KEY_NEXTSONG,
+       KEY_EJECTCD,    KEY_VOLUMEUP,   KEY_VOLUMEDOWN, KEY_MUTE,
+       KEY_WWW,        KEY_BACK,       KEY_FORWARD,    KEY_STOP,
+       KEY_FIND,       KEY_SCROLLUP,   KEY_SCROLLDOWN, KEY_EDIT,
+       KEY_SLEEP,      KEY_COFFEE,     KEY_REFRESH,    KEY_CALC,
+       NONE,           NONE,           NONE,           NONE,
+
+};
+
+static uint16_t evdev_at_set1_scancodes[] = {
+       /* 0x00 - 0x1f */
+       NONE,           KEY_ESC,        KEY_1,          KEY_2,
+       KEY_3,          KEY_4,          KEY_5,          KEY_6,
+       KEY_7,          KEY_8,          KEY_9,          KEY_0,
+       KEY_MINUS,      KEY_EQUAL,      KEY_BACKSPACE,  KEY_TAB,
+       KEY_Q,          KEY_W,          KEY_E,          KEY_R,
+       KEY_T,          KEY_Y,          KEY_U,          KEY_I,
+       KEY_O,          KEY_P,          KEY_LEFTBRACE,  KEY_RIGHTBRACE,
+       KEY_ENTER,      KEY_LEFTCTRL,   KEY_A,          KEY_S,
+       /* 0x20 - 0x3f */
+       KEY_D,          KEY_F,          KEY_G,          KEY_H,
+       KEY_J,          KEY_K,          KEY_L,          KEY_SEMICOLON,
+       KEY_APOSTROPHE, KEY_GRAVE,      KEY_LEFTSHIFT,  KEY_BACKSLASH,
+       KEY_Z,          KEY_X,          KEY_C,          KEY_V,
+       KEY_B,          KEY_N,          KEY_M,          KEY_COMMA,
+       KEY_DOT,        KEY_SLASH,      KEY_RIGHTSHIFT, NONE,
+       KEY_LEFTALT,    KEY_SPACE,      KEY_CAPSLOCK,   KEY_F1,
+       KEY_F2,         KEY_F3,         KEY_F4,         KEY_F5,
+       /* 0x40 - 0x5f */
+       KEY_F6,         KEY_F7,         KEY_F8,         KEY_F9,
+       KEY_F10,        KEY_NUMLOCK,    KEY_SCROLLLOCK, KEY_KP7,
+       KEY_KP8,        KEY_KP9,        KEY_KPMINUS,    KEY_KP4,
+       KEY_KP5,        KEY_KP6,        KEY_KPPLUS,     KEY_KP1,
+       KEY_KP2,        KEY_KP3,        KEY_KP0,        KEY_KPDOT,
+       NONE,           NONE,           NONE,           KEY_F11,
+       KEY_F12,        NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       /* 0x60 - 0x7f */
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       KEY_KATAKANAHIRAGANA,   NONE,   NONE,           KEY_RO,
+       NONE,           NONE,   KEY_ZENKAKUHANKAKU,     KEY_HIRAGANA,
+       KEY_KATAKANA,   KEY_HENKAN,     NONE,           KEY_MUHENKAN,
+       NONE,           KEY_YEN,        KEY_KPCOMMA,    NONE,
+       /* 0x00 - 0x1f. 0xE0 prefixed */
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       KEY_PREVIOUSSONG,       NONE,   NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           KEY_NEXTSONG,   NONE,           NONE,
+       KEY_KPENTER,    KEY_RIGHTCTRL,  NONE,           NONE,
+       /* 0x20 - 0x3f. 0xE0 prefixed */
+       KEY_MUTE,       KEY_CALC,       KEY_PLAYPAUSE,  NONE,
+       KEY_STOPCD,     NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           KEY_VOLUMEDOWN, NONE,
+       KEY_VOLUMEUP,   NONE,           KEY_HOMEPAGE,   NONE,
+       NONE,           KEY_KPASTERISK, NONE,           KEY_SYSRQ,
+       KEY_RIGHTALT,   NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       /* 0x40 - 0x5f. 0xE0 prefixed */
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           KEY_PAUSE,      KEY_HOME,
+       KEY_UP,         KEY_PAGEUP,     NONE,           KEY_LEFT,
+       NONE,           KEY_RIGHT,      NONE,           KEY_END,
+       KEY_DOWN,       KEY_PAGEDOWN,   KEY_INSERT,     KEY_DELETE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           KEY_LEFTMETA,
+       KEY_RIGHTMETA,  KEY_MENU,       KEY_POWER,      KEY_SLEEP,
+       /* 0x60 - 0x7f. 0xE0 prefixed */
+       NONE,           NONE,           NONE,           KEY_WAKEUP,
+       NONE,           KEY_SEARCH,     KEY_BOOKMARKS,  KEY_REFRESH,
+       KEY_STOP,       KEY_FORWARD,    KEY_BACK,       KEY_COMPUTER,
+       KEY_MAIL,       KEY_MEDIA,      NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+       NONE,           NONE,           NONE,           NONE,
+};
+
+static uint16_t evdev_mouse_button_codes[] = {
+       BTN_LEFT,
+       BTN_MIDDLE,
+       BTN_RIGHT,
+       BTN_SIDE,
+       BTN_EXTRA,
+       BTN_FORWARD,
+       BTN_BACK,
+       BTN_TASK,
+};
+
+static uint16_t evdev_led_codes[] = {
+       LED_CAPSL,      /* CLKED */
+       LED_NUML,       /* NLKED */
+       LED_SCROLLL,    /* SLKED */
+};
+
+uint16_t
+evdev_hid2key(int scancode)
+{
+       return evdev_usb_scancodes[scancode];
+}
+
+void
+evdev_support_all_known_keys(struct evdev_dev *evdev)
+{
+       size_t i;
+
+       for (i = KEY_RESERVED; i < nitems(evdev_at_set1_scancodes); i++)
+               if (evdev_at_set1_scancodes[i] != NONE)
+                       evdev_support_key(evdev, evdev_at_set1_scancodes[i]);
+}
+
+uint16_t
+evdev_scancode2key(int *state, int scancode)
+{
+       uint16_t keycode;
+
+       /* translate the scan code into a keycode */
+       keycode = evdev_at_set1_scancodes[scancode & 0x7f];
+       switch (*state) {
+       case 0x00:      /* normal scancode */
+               switch(scancode) {
+               case 0xE0:
+               case 0xE1:
+                       *state = scancode;
+                       return (NONE);
+               }
+               break;
+       case 0xE0:              /* 0xE0 prefix */
+               *state = 0;
+               keycode = evdev_at_set1_scancodes[0x80 + (scancode & 0x7f)];
+               break;
+       case 0xE1:      /* 0xE1 prefix */
+               /*
+                * The pause/break key on the 101 keyboard produces:
+                * E1-1D-45 E1-9D-C5
+                * Ctrl-pause/break produces:
+                * E0-46 E0-C6 (See above.)
+                */
+               *state = 0;
+               if ((scancode & 0x7f) == 0x1D)
+                       *state = 0x1D;
+               return (NONE);
+               /* NOT REACHED */
+       case 0x1D:      /* pause / break */
+               *state = 0;
+               if (scancode != 0x45)
+                       return (NONE);
+               keycode = KEY_PAUSE;
+               break;
+       }
+
+       return (keycode);
+}
+
+void
+evdev_push_mouse_btn(struct evdev_dev *evdev, int buttons)
+{
+       size_t i;
+
+       for (i = 0; i < nitems(evdev_mouse_button_codes); i++)
+               evdev_push_key(evdev, evdev_mouse_button_codes[i],
+                   buttons & (1 << i));
+}
+
+void
+evdev_push_leds(struct evdev_dev *evdev, int leds)
+{
+       size_t i;
+
+       /* Some drivers initialize leds before evdev */
+       if (evdev == NULL)
+               return;
+
+       for (i = 0; i < nitems(evdev_led_codes); i++)
+               evdev_push_led(evdev, evdev_led_codes[i], leds & (1 << i));
+}
+
+void
+evdev_push_repeats(struct evdev_dev *evdev, keyboard_t *kbd)
+{
+       /* Some drivers initialize typematics before evdev */
+       if (evdev == NULL)
+               return;
+
+       evdev_push_event(evdev, EV_REP, REP_DELAY, kbd->kb_delay1);
+       evdev_push_event(evdev, EV_REP, REP_PERIOD, kbd->kb_delay2);
+}
+
+void
+evdev_ev_kbd_event(struct evdev_dev *evdev, void *softc, uint16_t type,
+    uint16_t code, int32_t value)
+{
+       keyboard_t *kbd = (keyboard_t *)softc;
+       int delay[2], leds, oleds;
+       size_t i;
+
+       if (type == EV_LED) {
+               leds = oleds = KBD_LED_VAL(kbd);
+               for (i = 0; i < nitems(evdev_led_codes); i++) {
+                       if (evdev_led_codes[i] == code) {
+                               if (value)
+                                       leds |= 1 << i;
+                               else
+                                       leds &= ~(1 << i);
+                               if (leds != oleds)
+                                       kbd_ioctl(kbd, KDSETLED,
+                                           (caddr_t)&leds);
+                               break;
+                       }
+               }
+       } else if (type == EV_REP && code == REP_DELAY) {
+               delay[0] = value;
+               delay[1] = kbd->kb_delay2;
+               kbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
+       } else if (type == EV_REP && code == REP_PERIOD) {
+               delay[0] = kbd->kb_delay1;
+               delay[1] = value;
+               kbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
+       }
+}
diff --git a/sys/dev/misc/evdev/freebsd-bitstring.h b/sys/dev/misc/evdev/freebsd-bitstring.h
new file mode 100644 (file)
index 0000000..e226149
--- /dev/null
@@ -0,0 +1,358 @@
+/*-
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Vixie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Copyright (c) 2014 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef _SYS_FREEBSD_BITSTRING_H_
+#define        _SYS_FREEBSD_BITSTRING_H_
+
+#include <sys/types.h>
+
+/*
+ * Begin: Ad hoc definitions from FreeBSD sys/sys/types.h
+ */
+
+/*
+ * Population count algorithm using SWAR approach
+ * - "SIMD Within A Register".
+ */
+static __inline __uint16_t
+__bitcount16(__uint16_t _x)
+{
+
+       _x = (_x & 0x5555) + ((_x & 0xaaaa) >> 1);
+       _x = (_x & 0x3333) + ((_x & 0xcccc) >> 2);
+       _x = (_x + (_x >> 4)) & 0x0f0f;
+       _x = (_x + (_x >> 8)) & 0x00ff;
+       return (_x);
+}
+
+static __inline __uint32_t
+__bitcount32(__uint32_t _x)
+{
+
+       _x = (_x & 0x55555555) + ((_x & 0xaaaaaaaa) >> 1);
+       _x = (_x & 0x33333333) + ((_x & 0xcccccccc) >> 2);
+       _x = (_x + (_x >> 4)) & 0x0f0f0f0f;
+       _x = (_x + (_x >> 8));
+       _x = (_x + (_x >> 16)) & 0x000000ff;
+       return (_x);
+}
+
+#ifdef __LP64__
+static __inline __uint64_t
+__bitcount64(__uint64_t _x)
+{
+
+       _x = (_x & 0x5555555555555555) + ((_x & 0xaaaaaaaaaaaaaaaa) >> 1);
+       _x = (_x & 0x3333333333333333) + ((_x & 0xcccccccccccccccc) >> 2);
+       _x = (_x + (_x >> 4)) & 0x0f0f0f0f0f0f0f0f;
+       _x = (_x + (_x >> 8));
+       _x = (_x + (_x >> 16));
+       _x = (_x + (_x >> 32)) & 0x000000ff;
+       return (_x);
+}
+
+#define        __bitcountl(x)  __bitcount64((unsigned long)(x))
+#else
+static __inline __uint64_t
+__bitcount64(__uint64_t _x)
+{
+
+       return (__bitcount32(_x >> 32) + __bitcount32(_x));
+}
+
+#define        __bitcountl(x)  __bitcount32((unsigned long)(x))
+#endif
+#define        __bitcount(x)   __bitcount32((unsigned int)(x))
+/*
+ * End: Ad hoc definitions from FreeBSD sys/sys/types.h
+ */
+
+typedef        unsigned long bitstr_t;
+
+/*---------------------- Private Implementation Details ----------------------*/
+#define        _BITSTR_MASK (~0UL)
+#define        _BITSTR_BITS (sizeof(bitstr_t) * 8)
+
+#ifdef roundup2
+#define        _bit_roundup2 roundup2
+#else
+#define        _bit_roundup2(x, y)        (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
+#endif
+
+/* bitstr_t in bit string containing the bit. */
+static inline int
+_bit_idx(int _bit)
+{
+       return (_bit / _BITSTR_BITS);
+}
+
+/* bit number within bitstr_t at _bit_idx(_bit). */
+static inline int
+_bit_offset(int _bit)
+{
+       return (_bit % _BITSTR_BITS);
+}
+
+/* Mask for the bit within its long. */
+static inline bitstr_t
+_bit_mask(int _bit)
+{
+       return (1UL << _bit_offset(_bit));
+}
+
+static inline bitstr_t
+_bit_make_mask(int _start, int _stop)
+{
+       return ((_BITSTR_MASK << _bit_offset(_start)) &
+           (_BITSTR_MASK >> (_BITSTR_BITS - _bit_offset(_stop) - 1)));
+}
+
+/*----------------------------- Public Interface -----------------------------*/
+/* Number of bytes allocated for a bit string of nbits bits */
+#define        bitstr_size(_nbits) (_bit_roundup2(_nbits, _BITSTR_BITS) / 8)
+
+/* Allocate a bit string initialized with no bits set. */
+#ifdef _KERNEL
+static inline bitstr_t *
+bit_alloc(int _nbits, struct malloc_type *type, int flags)
+{
+       return ((bitstr_t *)kmalloc(bitstr_size(_nbits), type, flags | M_ZERO));
+}
+#else
+static inline bitstr_t *
+bit_alloc(int _nbits)
+{
+       return ((bitstr_t *)calloc(bitstr_size(_nbits), 1));
+}
+#endif
+
+/* Allocate a bit string on the stack */
+#define        bit_decl(name, nbits) \
+       ((name)[bitstr_size(nbits) / sizeof(bitstr_t)])
+
+/* Is bit N of bit string set? */
+static inline int
+bit_test(const bitstr_t *_bitstr, int _bit)
+{
+       return ((_bitstr[_bit_idx(_bit)] & _bit_mask(_bit)) != 0);
+}
+
+/* Set bit N of bit string. */
+static inline void
+bit_set(bitstr_t *_bitstr, int _bit)
+{
+       _bitstr[_bit_idx(_bit)] |= _bit_mask(_bit);
+}
+
+/* clear bit N of bit string name */
+static inline void
+bit_clear(bitstr_t *_bitstr, int _bit)
+{
+       _bitstr[_bit_idx(_bit)] &= ~_bit_mask(_bit);
+}
+
+/* Set bits start ... stop inclusive in bit string. */
+static inline void
+bit_nset(bitstr_t *_bitstr, int _start, int _stop)
+{
+       bitstr_t *_stopbitstr;
+
+       _stopbitstr = _bitstr + _bit_idx(_stop);
+       _bitstr += _bit_idx(_start);
+
+       if (_bitstr == _stopbitstr) {
+               *_bitstr |= _bit_make_mask(_start, _stop);
+       } else {
+               *_bitstr |= _bit_make_mask(_start, _BITSTR_BITS - 1);
+               while (++_bitstr < _stopbitstr)
+                       *_bitstr = _BITSTR_MASK;
+               *_stopbitstr |= _bit_make_mask(0, _stop);
+       }
+}
+
+/* Clear bits start ... stop inclusive in bit string. */
+static inline void
+bit_nclear(bitstr_t *_bitstr, int _start, int _stop)
+{
+       bitstr_t *_stopbitstr;
+
+       _stopbitstr = _bitstr + _bit_idx(_stop);
+       _bitstr += _bit_idx(_start);
+
+       if (_bitstr == _stopbitstr) {
+               *_bitstr &= ~_bit_make_mask(_start, _stop);
+       } else {
+               *_bitstr &= ~_bit_make_mask(_start, _BITSTR_BITS - 1);
+               while (++_bitstr < _stopbitstr)
+                       *_bitstr = 0;
+               *_stopbitstr &= ~_bit_make_mask(0, _stop);
+       }
+}
+
+/* Find the first bit set in bit string at or after bit start. */
+static inline void
+bit_ffs_at(bitstr_t *_bitstr, int _start, int _nbits, int *_result)
+{
+       bitstr_t *_curbitstr;
+       bitstr_t *_stopbitstr;
+       bitstr_t _test;
+       int _value, _offset;
+
+       if (_nbits > 0) {
+               _curbitstr = _bitstr + _bit_idx(_start);
+               _stopbitstr = _bitstr + _bit_idx(_nbits - 1);
+
+               _test = *_curbitstr;
+               if (_bit_offset(_start) != 0)
+                       _test &= _bit_make_mask(_start, _BITSTR_BITS - 1);
+               while (_test == 0 && _curbitstr < _stopbitstr)
+                       _test = *(++_curbitstr);
+
+               _offset = ffsl(_test);
+               _value = ((_curbitstr - _bitstr) * _BITSTR_BITS) + _offset - 1;
+               if (_offset == 0 || _value >= _nbits)
+                       _value = -1;
+       } else {
+               _value = -1;
+       }
+       *_result = _value;
+}
+
+/* Find the first bit clear in bit string at or after bit start. */
+static inline void
+bit_ffc_at(bitstr_t *_bitstr, int _start, int _nbits, int *_result)
+{
+       bitstr_t *_curbitstr;
+       bitstr_t *_stopbitstr;
+       bitstr_t _test;
+       int _value, _offset;
+
+       if (_nbits > 0) {
+               _curbitstr = _bitstr + _bit_idx(_start);
+               _stopbitstr = _bitstr + _bit_idx(_nbits - 1);
+
+               _test = *_curbitstr;
+               if (_bit_offset(_start) != 0)
+                       _test |= _bit_make_mask(0, _start - 1);
+               while (_test == _BITSTR_MASK && _curbitstr < _stopbitstr)
+                       _test = *(++_curbitstr);
+
+               _offset = ffsl(~_test);
+               _value = ((_curbitstr - _bitstr) * _BITSTR_BITS) + _offset - 1;
+               if (_offset == 0 || _value >= _nbits)
+                       _value = -1;
+       } else {
+               _value = -1;
+       }
+       *_result = _value;
+}
+
+/* Find the first bit set in bit string. */
+static inline void
+bit_ffs(bitstr_t *_bitstr, int _nbits, int *_result)
+{
+       bit_ffs_at(_bitstr, /*start*/0, _nbits, _result);
+}
+
+/* Find the first bit clear in bit string. */
+static inline void
+bit_ffc(bitstr_t *_bitstr, int _nbits, int *_result)
+{
+       bit_ffc_at(_bitstr, /*start*/0, _nbits, _result);
+}
+
+/* Count the number of bits set in a bitstr of size _nbits at or after _start */
+static inline void
+bit_count(bitstr_t *_bitstr, int _start, int _nbits, int *_result)
+{
+       bitstr_t *_curbitstr, mask;
+       int _value = 0, curbitstr_len;
+
+       if (_start >= _nbits)
+               goto out;
+
+       _curbitstr = _bitstr + _bit_idx(_start);
+       _nbits -= _BITSTR_BITS * _bit_idx(_start);
+       _start -= _BITSTR_BITS * _bit_idx(_start);
+
+       if (_start > 0) {
+               curbitstr_len = (int)_BITSTR_BITS < _nbits ?
+                               (int)_BITSTR_BITS : _nbits;
+               mask = _bit_make_mask(_start, _bit_offset(curbitstr_len - 1));
+               _value += __bitcountl(*_curbitstr & mask);
+               _curbitstr++;
+               _nbits -= _BITSTR_BITS;
+       }
+       while (_nbits >= (int)_BITSTR_BITS) {
+               _value += __bitcountl(*_curbitstr);
+               _curbitstr++;
+               _nbits -= _BITSTR_BITS;
+       }
+       if (_nbits > 0) {
+               mask = _bit_make_mask(0, _bit_offset(_nbits - 1));
+               _value += __bitcountl(*_curbitstr & mask);
+       }
+
+out:
+       *_result = _value;
+}
+
+#endif /* _SYS_FREEBSD_BITSTRING_H_ */
diff --git a/sys/dev/misc/evdev/input-event-codes.h b/sys/dev/misc/evdev/input-event-codes.h
new file mode 100644 (file)
index 0000000..cc1528f
--- /dev/null
@@ -0,0 +1,819 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef        _EVDEV_INPUT_EVENT_CODES_H
+#define        _EVDEV_INPUT_EVENT_CODES_H
+
+/*
+ * Device properties and quirks
+ */
+
+#define        INPUT_PROP_POINTER              0x00    /* needs a pointer */
+#define        INPUT_PROP_DIRECT               0x01    /* direct input devices */
+#define        INPUT_PROP_BUTTONPAD            0x02    /* has button(s) under pad */
+#define        INPUT_PROP_SEMI_MT              0x03    /* touch rectangle only */
+#define        INPUT_PROP_TOPBUTTONPAD         0x04    /* softbuttons at top of pad */
+#define        INPUT_PROP_POINTING_STICK       0x05    /* is a pointing stick */
+#define        INPUT_PROP_ACCELEROMETER        0x06    /* has accelerometer */
+
+#define        INPUT_PROP_MAX                  0x1f
+#define        INPUT_PROP_CNT                  (INPUT_PROP_MAX + 1)
+
+/*
+ * Event types
+ */
+
+#define        EV_SYN                  0x00
+#define        EV_KEY                  0x01
+#define        EV_REL                  0x02
+#define        EV_ABS                  0x03
+#define        EV_MSC                  0x04
+#define        EV_SW                   0x05
+#define        EV_LED                  0x11
+#define        EV_SND                  0x12
+#define        EV_REP                  0x14
+#define        EV_FF                   0x15
+#define        EV_PWR                  0x16
+#define        EV_FF_STATUS            0x17
+#define        EV_MAX                  0x1f
+#define        EV_CNT                  (EV_MAX+1)
+
+/*
+ * Synchronization events.
+ */
+
+#define        SYN_REPORT              0
+#define        SYN_CONFIG              1
+#define        SYN_MT_REPORT           2
+#define        SYN_DROPPED             3
+#define        SYN_MAX                 0xf
+#define        SYN_CNT                 (SYN_MAX+1)
+
+/*
+ * Keys and buttons
+ */
+
+/*
+ * Abbreviations in the comments:
+ * AC - Application Control
+ * AL - Application Launch Button
+ * SC - System Control
+ */
+
+#define        KEY_RESERVED            0
+#define        KEY_ESC                 1
+#define        KEY_1                   2
+#define        KEY_2                   3
+#define        KEY_3                   4
+#define        KEY_4                   5
+#define        KEY_5                   6
+#define        KEY_6                   7
+#define        KEY_7                   8
+#define        KEY_8                   9
+#define        KEY_9                   10
+#define        KEY_0                   11
+#define        KEY_MINUS               12
+#define        KEY_EQUAL               13
+#define        KEY_BACKSPACE           14
+#define        KEY_TAB                 15
+#define        KEY_Q                   16
+#define        KEY_W                   17
+#define        KEY_E                   18
+#define        KEY_R                   19
+#define        KEY_T                   20
+#define        KEY_Y                   21
+#define        KEY_U                   22
+#define        KEY_I                   23
+#define        KEY_O                   24
+#define        KEY_P                   25
+#define        KEY_LEFTBRACE           26
+#define        KEY_RIGHTBRACE          27
+#define        KEY_ENTER               28
+#define        KEY_LEFTCTRL            29
+#define        KEY_A                   30
+#define        KEY_S                   31
+#define        KEY_D                   32
+#define        KEY_F                   33
+#define        KEY_G                   34
+#define        KEY_H                   35
+#define        KEY_J                   36
+#define        KEY_K                   37
+#define        KEY_L                   38
+#define        KEY_SEMICOLON           39
+#define        KEY_APOSTROPHE          40
+#define        KEY_GRAVE               41
+#define        KEY_LEFTSHIFT           42
+#define        KEY_BACKSLASH           43
+#define        KEY_Z                   44
+#define        KEY_X                   45
+#define        KEY_C                   46
+#define        KEY_V                   47
+#define        KEY_B                   48
+#define        KEY_N                   49
+#define        KEY_M                   50
+#define        KEY_COMMA               51
+#define        KEY_DOT                 52
+#define        KEY_SLASH               53
+#define        KEY_RIGHTSHIFT          54
+#define        KEY_KPASTERISK          55
+#define        KEY_LEFTALT             56
+#define        KEY_SPACE               57
+#define        KEY_CAPSLOCK            58
+#define        KEY_F1                  59
+#define        KEY_F2                  60
+#define        KEY_F3                  61
+#define        KEY_F4                  62
+#define        KEY_F5                  63
+#define        KEY_F6                  64
+#define        KEY_F7                  65
+#define        KEY_F8                  66
+#define        KEY_F9                  67
+#define        KEY_F10                 68
+#define        KEY_NUMLOCK             69
+#define        KEY_SCROLLLOCK          70
+#define        KEY_KP7                 71
+#define        KEY_KP8                 72
+#define        KEY_KP9                 73
+#define        KEY_KPMINUS             74
+#define        KEY_KP4                 75
+#define        KEY_KP5                 76
+#define        KEY_KP6                 77
+#define        KEY_KPPLUS              78
+#define        KEY_KP1                 79
+#define        KEY_KP2                 80
+#define        KEY_KP3                 81
+#define        KEY_KP0                 82
+#define        KEY_KPDOT               83
+
+#define        KEY_ZENKAKUHANKAKU      85
+#define        KEY_102ND               86
+#define        KEY_F11                 87
+#define        KEY_F12                 88
+#define        KEY_RO                  89
+#define        KEY_KATAKANA            90
+#define        KEY_HIRAGANA            91
+#define        KEY_HENKAN              92
+#define        KEY_KATAKANAHIRAGANA    93
+#define        KEY_MUHENKAN            94
+#define        KEY_KPJPCOMMA           95
+#define        KEY_KPENTER             96
+#define        KEY_RIGHTCTRL           97
+#define        KEY_KPSLASH             98
+#define        KEY_SYSRQ               99
+#define        KEY_RIGHTALT            100
+#define        KEY_LINEFEED            101
+#define        KEY_HOME                102
+#define        KEY_UP                  103
+#define        KEY_PAGEUP              104
+#define        KEY_LEFT                105
+#define        KEY_RIGHT               106
+#define        KEY_END                 107
+#define        KEY_DOWN                108
+#define        KEY_PAGEDOWN            109
+#define        KEY_INSERT              110
+#define        KEY_DELETE              111
+#define        KEY_MACRO               112
+#define        KEY_MUTE                113
+#define        KEY_VOLUMEDOWN          114
+#define        KEY_VOLUMEUP            115
+#define        KEY_POWER               116     /* SC System Power Down */
+#define        KEY_KPEQUAL             117
+#define        KEY_KPPLUSMINUS         118
+#define        KEY_PAUSE               119
+#define        KEY_SCALE               120     /* AL Compiz Scale (Expose) */
+
+#define        KEY_KPCOMMA             121
+#define        KEY_HANGEUL             122
+#define        KEY_HANGUEL             KEY_HANGEUL
+#define        KEY_HANJA               123
+#define        KEY_YEN                 124
+#define        KEY_LEFTMETA            125
+#define        KEY_RIGHTMETA           126
+#define        KEY_COMPOSE             127
+
+#define        KEY_STOP                128     /* AC Stop */
+#define        KEY_AGAIN               129
+#define        KEY_PROPS               130     /* AC Properties */
+#define        KEY_UNDO                131     /* AC Undo */
+#define        KEY_FRONT               132
+#define        KEY_COPY                133     /* AC Copy */
+#define        KEY_OPEN                134     /* AC Open */
+#define        KEY_PASTE               135     /* AC Paste */
+#define        KEY_FIND                136     /* AC Search */
+#define        KEY_CUT                 137     /* AC Cut */
+#define        KEY_HELP                138     /* AL Integrated Help Center */
+#define        KEY_MENU                139     /* Menu (show menu) */
+#define        KEY_CALC                140     /* AL Calculator */
+#define        KEY_SETUP               141
+#define        KEY_SLEEP               142     /* SC System Sleep */
+#define        KEY_WAKEUP              143     /* System Wake Up */
+#define        KEY_FILE                144     /* AL Local Machine Browser */
+#define        KEY_SENDFILE            145
+#define        KEY_DELETEFILE          146
+#define        KEY_XFER                147
+#define        KEY_PROG1               148
+#define        KEY_PROG2               149
+#define        KEY_WWW                 150     /* AL Internet Browser */
+#define        KEY_MSDOS               151
+#define        KEY_COFFEE              152     /* AL Terminal Lock/Screensaver */
+#define        KEY_SCREENLOCK          KEY_COFFEE
+#define        KEY_ROTATE_DISPLAY      153     /* Display orientation for e.g. tablets */
+#define        KEY_DIRECTION           KEY_ROTATE_DISPLAY
+#define        KEY_CYCLEWINDOWS        154
+#define        KEY_MAIL                155
+#define        KEY_BOOKMARKS           156     /* AC Bookmarks */
+#define        KEY_COMPUTER            157
+#define        KEY_BACK                158     /* AC Back */
+#define        KEY_FORWARD             159     /* AC Forward */
+#define        KEY_CLOSECD             160
+#define        KEY_EJECTCD             161
+#define        KEY_EJECTCLOSECD        162
+#define        KEY_NEXTSONG            163
+#define        KEY_PLAYPAUSE           164
+#define        KEY_PREVIOUSSONG        165
+#define        KEY_STOPCD              166
+#define        KEY_RECORD              167
+#define        KEY_REWIND              168
+#define        KEY_PHONE               169     /* Media Select Telephone */
+#define        KEY_ISO                 170
+#define        KEY_CONFIG              171     /* AL Consumer Control Configuration */
+#define        KEY_HOMEPAGE            172     /* AC Home */
+#define        KEY_REFRESH             173     /* AC Refresh */
+#define        KEY_EXIT                174     /* AC Exit */
+#define        KEY_MOVE                175
+#define        KEY_EDIT                176
+#define        KEY_SCROLLUP            177
+#define        KEY_SCROLLDOWN          178
+#define        KEY_KPLEFTPAREN         179
+#define        KEY_KPRIGHTPAREN        180
+#define        KEY_NEW                 181     /* AC New */
+#define        KEY_REDO                182     /* AC Redo/Repeat */
+
+#define        KEY_F13                 183
+#define        KEY_F14                 184
+#define        KEY_F15                 185
+#define        KEY_F16                 186
+#define        KEY_F17                 187
+#define        KEY_F18                 188
+#define        KEY_F19                 189
+#define        KEY_F20                 190
+#define        KEY_F21                 191
+#define        KEY_F22                 192
+#define        KEY_F23                 193
+#define        KEY_F24                 194
+
+#define        KEY_PLAYCD              200
+#define        KEY_PAUSECD             201
+#define        KEY_PROG3               202
+#define        KEY_PROG4               203
+#define        KEY_DASHBOARD           204     /* AL Dashboard */
+#define        KEY_SUSPEND             205
+#define        KEY_CLOSE               206     /* AC Close */
+#define        KEY_PLAY                207
+#define        KEY_FASTFORWARD         208
+#define        KEY_BASSBOOST           209
+#define        KEY_PRINT               210     /* AC Print */
+#define        KEY_HP                  211
+#define        KEY_CAMERA              212
+#define        KEY_SOUND               213
+#define        KEY_QUESTION            214
+#define        KEY_EMAIL               215
+#define        KEY_CHAT                216
+#define        KEY_SEARCH              217
+#define        KEY_CONNECT             218
+#define        KEY_FINANCE             219     /* AL Checkbook/Finance */
+#define        KEY_SPORT               220
+#define        KEY_SHOP                221
+#define        KEY_ALTERASE            222
+#define        KEY_CANCEL              223     /* AC Cancel */
+#define        KEY_BRIGHTNESSDOWN      224
+#define        KEY_BRIGHTNESSUP        225
+#define        KEY_MEDIA               226
+
+#define        KEY_SWITCHVIDEOMODE     227     /* Cycle between available video
+                                          outputs (Monitor/LCD/TV-out/etc) */
+#define        KEY_KBDILLUMTOGGLE      228
+#define        KEY_KBDILLUMDOWN        229
+#define        KEY_KBDILLUMUP          230
+
+#define        KEY_SEND                231     /* AC Send */
+#define        KEY_REPLY               232     /* AC Reply */
+#define        KEY_FORWARDMAIL         233     /* AC Forward Msg */
+#define        KEY_SAVE                234     /* AC Save */
+#define        KEY_DOCUMENTS           235
+
+#define        KEY_BATTERY             236
+
+#define        KEY_BLUETOOTH           237
+#define        KEY_WLAN                238
+#define        KEY_UWB                 239
+
+#define        KEY_UNKNOWN             240
+
+#define        KEY_VIDEO_NEXT          241     /* drive next video source */
+#define        KEY_VIDEO_PREV          242     /* drive previous video source */
+#define        KEY_BRIGHTNESS_CYCLE    243     /* brightness up, after max is min */
+#define        KEY_BRIGHTNESS_AUTO     244     /* Set Auto Brightness: manual
+                                         brightness control is off,
+                                         rely on ambient */
+#define        KEY_BRIGHTNESS_ZERO     KEY_BRIGHTNESS_AUTO
+#define        KEY_DISPLAY_OFF         245     /* display device to off state */
+
+#define        KEY_WWAN                246     /* Wireless WAN (LTE, UMTS, GSM, etc.) */
+#define        KEY_WIMAX               KEY_WWAN
+#define        KEY_RFKILL              247     /* Key that controls all radios */
+
+#define        KEY_MICMUTE             248     /* Mute / unmute the microphone */
+
+/* Code 255 is reserved for special needs of AT keyboard driver */
+
+#define        BTN_MISC                0x100
+#define        BTN_0                   0x100
+#define        BTN_1                   0x101
+#define        BTN_2                   0x102
+#define        BTN_3                   0x103
+#define        BTN_4                   0x104
+#define        BTN_5                   0x105
+#define        BTN_6                   0x106
+#define        BTN_7                   0x107
+#define        BTN_8                   0x108
+#define        BTN_9                   0x109
+
+#define        BTN_MOUSE               0x110
+#define        BTN_LEFT                0x110
+#define        BTN_RIGHT               0x111
+#define        BTN_MIDDLE              0x112
+#define        BTN_SIDE                0x113
+#define        BTN_EXTRA               0x114
+#define        BTN_FORWARD             0x115
+#define        BTN_BACK                0x116
+#define        BTN_TASK                0x117
+
+#define        BTN_JOYSTICK            0x120
+#define        BTN_TRIGGER             0x120
+#define        BTN_THUMB               0x121
+#define        BTN_THUMB2              0x122
+#define        BTN_TOP                 0x123
+#define        BTN_TOP2                0x124
+#define        BTN_PINKIE              0x125
+#define        BTN_BASE                0x126
+#define        BTN_BASE2               0x127
+#define        BTN_BASE3               0x128
+#define        BTN_BASE4               0x129
+#define        BTN_BASE5               0x12a
+#define        BTN_BASE6               0x12b
+#define        BTN_DEAD                0x12f
+
+#define        BTN_GAMEPAD             0x130
+#define        BTN_SOUTH               0x130
+#define        BTN_A                   BTN_SOUTH
+#define        BTN_EAST                0x131
+#define        BTN_B                   BTN_EAST
+#define        BTN_C                   0x132
+#define        BTN_NORTH               0x133
+#define        BTN_X                   BTN_NORTH
+#define        BTN_WEST                0x134
+#define        BTN_Y                   BTN_WEST
+#define        BTN_Z                   0x135
+#define        BTN_TL                  0x136
+#define        BTN_TR                  0x137
+#define        BTN_TL2                 0x138
+#define        BTN_TR2                 0x139
+#define        BTN_SELECT              0x13a
+#define        BTN_START               0x13b
+#define        BTN_MODE                0x13c
+#define        BTN_THUMBL              0x13d
+#define        BTN_THUMBR              0x13e
+
+#define        BTN_DIGI                0x140
+#define        BTN_TOOL_PEN            0x140
+#define        BTN_TOOL_RUBBER         0x141
+#define        BTN_TOOL_BRUSH          0x142
+#define        BTN_TOOL_PENCIL         0x143
+#define        BTN_TOOL_AIRBRUSH       0x144
+#define        BTN_TOOL_FINGER         0x145
+#define        BTN_TOOL_MOUSE          0x146
+#define        BTN_TOOL_LENS           0x147
+#define        BTN_TOOL_QUINTTAP       0x148   /* Five fingers on trackpad */
+#define        BTN_TOUCH               0x14a
+#define        BTN_STYLUS              0x14b
+#define        BTN_STYLUS2             0x14c
+#define        BTN_TOOL_DOUBLETAP      0x14d
+#define        BTN_TOOL_TRIPLETAP      0x14e
+#define        BTN_TOOL_QUADTAP        0x14f   /* Four fingers on trackpad */
+
+#define        BTN_WHEEL               0x150
+#define        BTN_GEAR_DOWN           0x150
+#define        BTN_GEAR_UP             0x151
+
+#define        KEY_OK                  0x160
+#define        KEY_SELECT              0x161
+#define        KEY_GOTO                0x162
+#define        KEY_CLEAR               0x163
+#define        KEY_POWER2              0x164
+#define        KEY_OPTION              0x165
+#define        KEY_INFO                0x166   /* AL OEM Features/Tips/Tutorial */
+#define        KEY_TIME                0x167
+#define        KEY_VENDOR              0x168
+#define        KEY_ARCHIVE             0x169
+#define        KEY_PROGRAM             0x16a   /* Media Select Program Guide */
+#define        KEY_CHANNEL             0x16b
+#define        KEY_FAVORITES           0x16c
+#define        KEY_EPG                 0x16d
+#define        KEY_PVR                 0x16e   /* Media Select Home */
+#define        KEY_MHP                 0x16f
+#define        KEY_LANGUAGE            0x170
+#define        KEY_TITLE               0x171
+#define        KEY_SUBTITLE            0x172
+#define        KEY_ANGLE               0x173
+#define        KEY_ZOOM                0x174
+#define        KEY_MODE                0x175
+#define        KEY_KEYBOARD            0x176
+#define        KEY_SCREEN              0x177
+#define        KEY_PC                  0x178   /* Media Select Computer */
+#define        KEY_TV                  0x179   /* Media Select TV */
+#define        KEY_TV2                 0x17a   /* Media Select Cable */
+#define        KEY_VCR                 0x17b   /* Media Select VCR */
+#define        KEY_VCR2                0x17c   /* VCR Plus */
+#define        KEY_SAT                 0x17d   /* Media Select Satellite */
+#define        KEY_SAT2                0x17e
+#define        KEY_CD                  0x17f   /* Media Select CD */
+#define        KEY_TAPE                0x180   /* Media Select Tape */
+#define        KEY_RADIO               0x181
+#define        KEY_TUNER               0x182   /* Media Select Tuner */
+#define        KEY_PLAYER              0x183
+#define        KEY_TEXT                0x184
+#define        KEY_DVD                 0x185   /* Media Select DVD */
+#define        KEY_AUX                 0x186
+#define        KEY_MP3                 0x187
+#define        KEY_AUDIO               0x188   /* AL Audio Browser */
+#define        KEY_VIDEO               0x189   /* AL Movie Browser */
+#define        KEY_DIRECTORY           0x18a
+#define        KEY_LIST                0x18b
+#define        KEY_MEMO                0x18c   /* Media Select Messages */
+#define        KEY_CALENDAR            0x18d
+#define        KEY_RED                 0x18e
+#define        KEY_GREEN               0x18f
+#define        KEY_YELLOW              0x190
+#define        KEY_BLUE                0x191
+#define        KEY_CHANNELUP           0x192   /* Channel Increment */
+#define        KEY_CHANNELDOWN         0x193   /* Channel Decrement */
+#define        KEY_FIRST               0x194
+#define        KEY_LAST                0x195   /* Recall Last */
+#define        KEY_AB                  0x196
+#define        KEY_NEXT                0x197
+#define        KEY_RESTART             0x198
+#define        KEY_SLOW                0x199
+#define        KEY_SHUFFLE             0x19a
+#define        KEY_BREAK               0x19b
+#define        KEY_PREVIOUS            0x19c
+#define        KEY_DIGITS              0x19d
+#define        KEY_TEEN                0x19e
+#define        KEY_TWEN                0x19f
+#define        KEY_VIDEOPHONE          0x1a0   /* Media Select Video Phone */
+#define        KEY_GAMES               0x1a1   /* Media Select Games */
+#define        KEY_ZOOMIN              0x1a2   /* AC Zoom In */
+#define        KEY_ZOOMOUT             0x1a3   /* AC Zoom Out */
+#define        KEY_ZOOMRESET           0x1a4   /* AC Zoom */
+#define        KEY_WORDPROCESSOR       0x1a5   /* AL Word Processor */
+#define        KEY_EDITOR              0x1a6   /* AL Text Editor */
+#define        KEY_SPREADSHEET         0x1a7   /* AL Spreadsheet */
+#define        KEY_GRAPHICSEDITOR      0x1a8   /* AL Graphics Editor */
+#define        KEY_PRESENTATION        0x1a9   /* AL Presentation App */
+#define        KEY_DATABASE            0x1aa   /* AL Database App */
+#define        KEY_NEWS                0x1ab   /* AL Newsreader */
+#define        KEY_VOICEMAIL           0x1ac   /* AL Voicemail */
+#define        KEY_ADDRESSBOOK         0x1ad   /* AL Contacts/Address Book */
+#define        KEY_MESSENGER           0x1ae   /* AL Instant Messaging */
+#define        KEY_DISPLAYTOGGLE       0x1af   /* Turn display (LCD) on and off */
+#define        KEY_BRIGHTNESS_TOGGLE   KEY_DISPLAYTOGGLE
+#define        KEY_SPELLCHECK          0x1b0   /* AL Spell Check */
+#define        KEY_LOGOFF              0x1b1   /* AL Logoff */
+
+#define        KEY_DOLLAR              0x1b2
+#define        KEY_EURO                0x1b3
+
+#define        KEY_FRAMEBACK           0x1b4   /* Consumer - transport controls */
+#define        KEY_FRAMEFORWARD        0x1b5
+#define        KEY_CONTEXT_MENU        0x1b6   /* GenDesc - system context menu */
+#define        KEY_MEDIA_REPEAT        0x1b7   /* Consumer - transport control */
+#define        KEY_10CHANNELSUP        0x1b8   /* 10 channels up (10+) */
+#define        KEY_10CHANNELSDOWN      0x1b9   /* 10 channels down (10-) */
+#define        KEY_IMAGES              0x1ba   /* AL Image Browser */
+
+#define        KEY_DEL_EOL             0x1c0
+#define        KEY_DEL_EOS             0x1c1
+#define        KEY_INS_LINE            0x1c2
+#define        KEY_DEL_LINE            0x1c3
+
+#define        KEY_FN                  0x1d0
+#define        KEY_FN_ESC              0x1d1
+#define        KEY_FN_F1               0x1d2
+#define        KEY_FN_F2               0x1d3
+#define        KEY_FN_F3               0x1d4
+#define        KEY_FN_F4               0x1d5
+#define        KEY_FN_F5               0x1d6
+#define        KEY_FN_F6               0x1d7
+#define        KEY_FN_F7               0x1d8
+#define        KEY_FN_F8               0x1d9
+#define        KEY_FN_F9               0x1da
+#define        KEY_FN_F10              0x1db
+#define        KEY_FN_F11              0x1dc
+#define        KEY_FN_F12              0x1dd
+#define        KEY_FN_1                0x1de
+#define        KEY_FN_2                0x1df
+#define        KEY_FN_D                0x1e0
+#define        KEY_FN_E                0x1e1
+#define        KEY_FN_F                0x1e2
+#define        KEY_FN_S                0x1e3
+#define        KEY_FN_B                0x1e4
+
+#define        KEY_BRL_DOT1            0x1f1
+#define        KEY_BRL_DOT2            0x1f2
+#define        KEY_BRL_DOT3            0x1f3
+#define        KEY_BRL_DOT4            0x1f4
+#define        KEY_BRL_DOT5            0x1f5
+#define        KEY_BRL_DOT6            0x1f6
+#define        KEY_BRL_DOT7            0x1f7
+#define        KEY_BRL_DOT8            0x1f8
+#define        KEY_BRL_DOT9            0x1f9
+#define        KEY_BRL_DOT10           0x1fa
+
+#define        KEY_NUMERIC_0           0x200   /* used by phones, remote controls, */
+#define        KEY_NUMERIC_1           0x201   /* and other keypads */
+#define        KEY_NUMERIC_2           0x202
+#define        KEY_NUMERIC_3           0x203
+#define        KEY_NUMERIC_4           0x204
+#define        KEY_NUMERIC_5           0x205
+#define        KEY_NUMERIC_6           0x206
+#define        KEY_NUMERIC_7           0x207
+#define        KEY_NUMERIC_8           0x208
+#define        KEY_NUMERIC_9           0x209
+#define        KEY_NUMERIC_STAR        0x20a
+#define        KEY_NUMERIC_POUND       0x20b
+#define        KEY_NUMERIC_A           0x20c   /* Phone key A - HUT Telephony 0xb9 */
+#define        KEY_NUMERIC_B           0x20d
+#define        KEY_NUMERIC_C           0x20e
+#define        KEY_NUMERIC_D           0x20f
+
+#define        KEY_CAMERA_FOCUS        0x210
+#define        KEY_WPS_BUTTON          0x211   /* WiFi Protected Setup key */
+
+#define        KEY_TOUCHPAD_TOGGLE     0x212   /* Request switch touchpad on or off */
+#define        KEY_TOUCHPAD_ON         0x213
+#define        KEY_TOUCHPAD_OFF        0x214
+
+#define        KEY_CAMERA_ZOOMIN       0x215
+#define        KEY_CAMERA_ZOOMOUT      0x216
+#define        KEY_CAMERA_UP           0x217
+#define        KEY_CAMERA_DOWN         0x218
+#define        KEY_CAMERA_LEFT         0x219
+#define        KEY_CAMERA_RIGHT        0x21a
+
+#define        KEY_ATTENDANT_ON        0x21b
+#define        KEY_ATTENDANT_OFF       0x21c
+#define        KEY_ATTENDANT_TOGGLE    0x21d   /* Attendant call on or off */
+#define        KEY_LIGHTS_TOGGLE       0x21e   /* Reading light on or off */
+
+#define        BTN_DPAD_UP             0x220
+#define        BTN_DPAD_DOWN           0x221
+#define        BTN_DPAD_LEFT           0x222
+#define        BTN_DPAD_RIGHT          0x223
+
+#define        KEY_ALS_TOGGLE          0x230   /* Ambient light sensor */
+
+#define        KEY_BUTTONCONFIG                0x240   /* AL Button Configuration */
+#define        KEY_TASKMANAGER         0x241   /* AL Task/Project Manager */
+#define        KEY_JOURNAL             0x242   /* AL Log/Journal/Timecard */
+#define        KEY_CONTROLPANEL                0x243   /* AL Control Panel */
+#define        KEY_APPSELECT           0x244   /* AL Select Task/Application */
+#define        KEY_SCREENSAVER         0x245   /* AL Screen Saver */
+#define        KEY_VOICECOMMAND                0x246   /* Listening Voice Command */
+
+#define        KEY_BRIGHTNESS_MIN              0x250   /* Set Brightness to Minimum */
+#define        KEY_BRIGHTNESS_MAX              0x251   /* Set Brightness to Maximum */
+
+#define        KEY_KBDINPUTASSIST_PREV         0x260
+#define        KEY_KBDINPUTASSIST_NEXT         0x261
+#define        KEY_KBDINPUTASSIST_PREVGROUP            0x262
+#define        KEY_KBDINPUTASSIST_NEXTGROUP            0x263
+#define        KEY_KBDINPUTASSIST_ACCEPT               0x264
+#define        KEY_KBDINPUTASSIST_CANCEL               0x265
+
+#define        BTN_TRIGGER_HAPPY               0x2c0
+#define        BTN_TRIGGER_HAPPY1              0x2c0
+#define        BTN_TRIGGER_HAPPY2              0x2c1
+#define        BTN_TRIGGER_HAPPY3              0x2c2
+#define        BTN_TRIGGER_HAPPY4              0x2c3
+#define        BTN_TRIGGER_HAPPY5              0x2c4
+#define        BTN_TRIGGER_HAPPY6              0x2c5
+#define        BTN_TRIGGER_HAPPY7              0x2c6
+#define        BTN_TRIGGER_HAPPY8              0x2c7
+#define        BTN_TRIGGER_HAPPY9              0x2c8
+#define        BTN_TRIGGER_HAPPY10             0x2c9
+#define        BTN_TRIGGER_HAPPY11             0x2ca
+#define        BTN_TRIGGER_HAPPY12             0x2cb
+#define        BTN_TRIGGER_HAPPY13             0x2cc
+#define        BTN_TRIGGER_HAPPY14             0x2cd
+#define        BTN_TRIGGER_HAPPY15             0x2ce
+#define        BTN_TRIGGER_HAPPY16             0x2cf
+#define        BTN_TRIGGER_HAPPY17             0x2d0
+#define        BTN_TRIGGER_HAPPY18             0x2d1
+#define        BTN_TRIGGER_HAPPY19             0x2d2
+#define        BTN_TRIGGER_HAPPY20             0x2d3
+#define        BTN_TRIGGER_HAPPY21             0x2d4
+#define        BTN_TRIGGER_HAPPY22             0x2d5
+#define        BTN_TRIGGER_HAPPY23             0x2d6
+#define        BTN_TRIGGER_HAPPY24             0x2d7
+#define        BTN_TRIGGER_HAPPY25             0x2d8
+#define        BTN_TRIGGER_HAPPY26             0x2d9
+#define        BTN_TRIGGER_HAPPY27             0x2da
+#define        BTN_TRIGGER_HAPPY28             0x2db
+#define        BTN_TRIGGER_HAPPY29             0x2dc
+#define        BTN_TRIGGER_HAPPY30             0x2dd
+#define        BTN_TRIGGER_HAPPY31             0x2de
+#define        BTN_TRIGGER_HAPPY32             0x2df
+#define        BTN_TRIGGER_HAPPY33             0x2e0
+#define        BTN_TRIGGER_HAPPY34             0x2e1
+#define        BTN_TRIGGER_HAPPY35             0x2e2
+#define        BTN_TRIGGER_HAPPY36             0x2e3
+#define        BTN_TRIGGER_HAPPY37             0x2e4
+#define        BTN_TRIGGER_HAPPY38             0x2e5
+#define        BTN_TRIGGER_HAPPY39             0x2e6
+#define        BTN_TRIGGER_HAPPY40             0x2e7
+
+/* We avoid low common keys in module aliases so they don't get huge. */
+#define        KEY_MIN_INTERESTING     KEY_MUTE
+#define        KEY_MAX                 0x2ff
+#define        KEY_CNT                 (KEY_MAX+1)
+
+/*
+ * Relative axes
+ */
+
+#define        REL_X                   0x00
+#define        REL_Y                   0x01
+#define        REL_Z                   0x02
+#define        REL_RX                  0x03
+#define        REL_RY                  0x04
+#define        REL_RZ                  0x05
+#define        REL_HWHEEL              0x06
+#define        REL_DIAL                0x07
+#define        REL_WHEEL               0x08
+#define        REL_MISC                0x09
+#define        REL_MAX                 0x0f
+#define        REL_CNT                 (REL_MAX+1)
+
+/*
+ * Absolute axes
+ */
+
+#define        ABS_X                   0x00
+#define        ABS_Y                   0x01
+#define        ABS_Z                   0x02
+#define        ABS_RX                  0x03
+#define        ABS_RY                  0x04
+#define        ABS_RZ                  0x05
+#define        ABS_THROTTLE            0x06
+#define        ABS_RUDDER              0x07
+#define        ABS_WHEEL               0x08
+#define        ABS_GAS                 0x09
+#define        ABS_BRAKE               0x0a
+#define        ABS_HAT0X               0x10
+#define        ABS_HAT0Y               0x11
+#define        ABS_HAT1X               0x12
+#define        ABS_HAT1Y               0x13
+#define        ABS_HAT2X               0x14
+#define        ABS_HAT2Y               0x15
+#define        ABS_HAT3X               0x16
+#define        ABS_HAT3Y               0x17
+#define        ABS_PRESSURE            0x18
+#define        ABS_DISTANCE            0x19
+#define        ABS_TILT_X              0x1a
+#define        ABS_TILT_Y              0x1b
+#define        ABS_TOOL_WIDTH          0x1c
+
+#define        ABS_VOLUME              0x20
+
+#define        ABS_MISC                0x28
+
+#define        ABS_MT_SLOT             0x2f    /* MT slot being modified */
+#define        ABS_MT_TOUCH_MAJOR      0x30    /* Major axis of touching ellipse */
+#define        ABS_MT_TOUCH_MINOR      0x31    /* Minor axis (omit if circular) */
+#define        ABS_MT_WIDTH_MAJOR      0x32    /* Major axis of approaching ellipse */
+#define        ABS_MT_WIDTH_MINOR      0x33    /* Minor axis (omit if circular) */
+#define        ABS_MT_ORIENTATION      0x34    /* Ellipse orientation */
+#define        ABS_MT_POSITION_X       0x35    /* Center X touch position */
+#define        ABS_MT_POSITION_Y       0x36    /* Center Y touch position */
+#define        ABS_MT_TOOL_TYPE        0x37    /* Type of touching device */
+#define        ABS_MT_BLOB_ID          0x38    /* Group a set of packets as a blob */
+#define        ABS_MT_TRACKING_ID      0x39    /* Unique ID of initiated contact */
+#define        ABS_MT_PRESSURE         0x3a    /* Pressure on contact area */
+#define        ABS_MT_DISTANCE         0x3b    /* Contact hover distance */
+#define        ABS_MT_TOOL_X           0x3c    /* Center X tool position */
+#define        ABS_MT_TOOL_Y           0x3d    /* Center Y tool position */
+
+#define        ABS_MAX                 0x3f
+#define        ABS_CNT                 (ABS_MAX+1)
+
+/*
+ * Switch events
+ */
+
+#define        SW_LID                  0x00  /* set = lid shut */
+#define        SW_TABLET_MODE          0x01  /* set = tablet mode */
+#define        SW_HEADPHONE_INSERT     0x02  /* set = inserted */
+#define        SW_RFKILL_ALL           0x03  /* rfkill master switch, type "any"
+                                        set = radio enabled */
+#define        SW_RADIO                SW_RFKILL_ALL   /* deprecated */
+#define        SW_MICROPHONE_INSERT    0x04  /* set = inserted */
+#define        SW_DOCK                 0x05  /* set = plugged into dock */
+#define        SW_LINEOUT_INSERT       0x06  /* set = inserted */
+#define        SW_JACK_PHYSICAL_INSERT 0x07  /* set = mechanical switch set */
+#define        SW_VIDEOOUT_INSERT      0x08  /* set = inserted */
+#define        SW_CAMERA_LENS_COVER    0x09  /* set = lens covered */
+#define        SW_KEYPAD_SLIDE         0x0a  /* set = keypad slide out */
+#define        SW_FRONT_PROXIMITY      0x0b  /* set = front proximity sensor active */
+#define        SW_ROTATE_LOCK          0x0c  /* set = rotate locked/disabled */
+#define        SW_LINEIN_INSERT        0x0d  /* set = inserted */
+#define        SW_MUTE_DEVICE          0x0e  /* set = device disabled */
+#define        SW_MAX                  0x0f
+#define        SW_CNT                  (SW_MAX+1)
+
+/*
+ * Misc events
+ */
+
+#define        MSC_SERIAL              0x00
+#define        MSC_PULSELED            0x01
+#define        MSC_GESTURE             0x02
+#define        MSC_RAW                 0x03
+#define        MSC_SCAN                0x04
+#define        MSC_TIMESTAMP           0x05
+#define        MSC_MAX                 0x07
+#define        MSC_CNT                 (MSC_MAX+1)
+
+/*
+ * LEDs
+ */
+
+#define        LED_NUML                0x00
+#define        LED_CAPSL               0x01
+#define        LED_SCROLLL             0x02
+#define        LED_COMPOSE             0x03
+#define        LED_KANA                0x04
+#define        LED_SLEEP               0x05
+#define        LED_SUSPEND             0x06
+#define        LED_MUTE                0x07
+#define        LED_MISC                0x08
+#define        LED_MAIL                0x09
+#define        LED_CHARGING            0x0a
+#define        LED_MAX                 0x0f
+#define        LED_CNT                 (LED_MAX+1)
+
+/*
+ * Autorepeat values
+ */
+
+#define        REP_DELAY               0x00
+#define        REP_PERIOD              0x01
+#define        REP_MAX                 0x01
+#define        REP_CNT                 (REP_MAX+1)
+
+/*
+ * Sounds
+ */
+
+#define        SND_CLICK               0x00
+#define        SND_BELL                0x01
+#define        SND_TONE                0x02
+#define        SND_MAX                 0x07
+#define        SND_CNT                 (SND_MAX+1)
+
+#endif /* _EVDEV_INPUT_EVENT_CODES_H */
diff --git a/sys/dev/misc/evdev/input.h b/sys/dev/misc/evdev/input.h
new file mode 100644 (file)
index 0000000..a5fb422
--- /dev/null
@@ -0,0 +1,271 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef        _EVDEV_INPUT_H
+#define        _EVDEV_INPUT_H
+
+#include <sys/ioccom.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "input-event-codes.h"
+
+struct input_event {
+       struct timeval  time;
+       uint16_t        type;
+       uint16_t        code;
+       int32_t         value;
+};
+
+#define        EV_VERSION              0x010001
+
+struct input_id {
+       uint16_t        bustype;
+       uint16_t        vendor;
+       uint16_t        product;
+       uint16_t        version;
+};
+
+struct input_absinfo {
+       int32_t         value;
+       int32_t         minimum;
+       int32_t         maximum;
+       int32_t         fuzz;
+       int32_t         flat;
+       int32_t         resolution;
+};
+
+#define        INPUT_KEYMAP_BY_INDEX   (1 << 0)
+
+struct input_keymap_entry {
+       uint8_t         flags;
+       uint8_t         len;
+       uint16_t        index;
+       uint32_t        keycode;
+       uint8_t         scancode[32];
+};
+
+#define        EVDEV_IOC_MAGIC 'E'
+#define        EVIOCGVERSION           _IOR(EVDEV_IOC_MAGIC, 0x01, int)                /* get driver version */
+#define        EVIOCGID                _IOR(EVDEV_IOC_MAGIC, 0x02, struct input_id)    /* get device ID */
+#define        EVIOCGREP               _IOR(EVDEV_IOC_MAGIC, 0x03, unsigned int[2])    /* get repeat settings */
+#define        EVIOCSREP               _IOW(EVDEV_IOC_MAGIC, 0x03, unsigned int[2])    /* set repeat settings */
+
+#define        EVIOCGKEYCODE           _IOWR(EVDEV_IOC_MAGIC, 0x04, unsigned int[2])   /* get keycode */
+#define        EVIOCGKEYCODE_V2        _IOWR(EVDEV_IOC_MAGIC, 0x04, struct input_keymap_entry)
+#define        EVIOCSKEYCODE           _IOW(EVDEV_IOC_MAGIC, 0x04, unsigned int[2])    /* set keycode */
+#define        EVIOCSKEYCODE_V2        _IOW(EVDEV_IOC_MAGIC, 0x04, struct input_keymap_entry)
+
+#define        EVIOCGNAME(len)         _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x06, len)       /* get device name */
+#define        EVIOCGPHYS(len)         _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x07, len)       /* get physical location */
+#define        EVIOCGUNIQ(len)         _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x08, len)       /* get unique identifier */
+#define        EVIOCGPROP(len)         _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x09, len)       /* get device properties */
+
+#define        EVIOCGMTSLOTS(len)      _IOC(IOC_INOUT, EVDEV_IOC_MAGIC, 0x0a, len)     /* get MT slots values */
+
+#define        EVIOCGKEY(len)          _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x18, len)       /* get global key state */
+#define        EVIOCGLED(len)          _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x19, len)       /* get all LEDs */
+#define        EVIOCGSND(len)          _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x1a, len)       /* get all sounds status */
+#define        EVIOCGSW(len)           _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x1b, len)       /* get all switch states */
+
+#define        EVIOCGBIT(ev,len)       _IOC(IOC_OUT, EVDEV_IOC_MAGIC, 0x20 + (ev), len)        /* get event bits */
+#define        EVIOCGABS(abs)          _IOR(EVDEV_IOC_MAGIC, 0x40 + (abs), struct input_absinfo)       /* get abs value/limits */
+#define        EVIOCSABS(abs)          _IOW(EVDEV_IOC_MAGIC, 0xc0 + (abs), struct input_absinfo)       /* set abs value/limits */
+
+#define        EVIOCSFF                _IOW(EVDEV_IOC_MAGIC, 0x80, struct ff_effect)   /* send a force effect to a force feedback device */
+#define        EVIOCRMFF               _IOWINT(EVDEV_IOC_MAGIC, 0x81)                  /* Erase a force effect */
+#define        EVIOCGEFFECTS           _IOR(EVDEV_IOC_MAGIC, 0x84, int)                /* Report number of effects playable at the same time */
+
+#define        EVIOCGRAB               _IOWINT(EVDEV_IOC_MAGIC, 0x90)                  /* Grab/Release device */
+#define        EVIOCREVOKE             _IOWINT(EVDEV_IOC_MAGIC, 0x91)                  /* Revoke device access */
+
+#define        EVIOCSCLOCKID           _IOW(EVDEV_IOC_MAGIC, 0xa0, int)                /* Set clockid to be used for timestamps */
+
+/*
+ * IDs.
+ */
+
+#define        ID_BUS                  0
+#define        ID_VENDOR               1
+#define        ID_PRODUCT              2
+#define        ID_VERSION              3
+
+#define        BUS_PCI                 0x01
+#define        BUS_ISAPNP              0x02
+#define        BUS_USB                 0x03
+#define        BUS_HIL                 0x04
+#define        BUS_BLUETOOTH           0x05
+#define        BUS_VIRTUAL             0x06
+
+#define        BUS_ISA                 0x10
+#define        BUS_I8042               0x11
+#define        BUS_XTKBD               0x12
+#define        BUS_RS232               0x13
+#define        BUS_GAMEPORT            0x14
+#define        BUS_PARPORT             0x15
+#define        BUS_AMIGA               0x16
+#define        BUS_ADB                 0x17
+#define        BUS_I2C                 0x18
+#define        BUS_HOST                0x19
+#define        BUS_GSC                 0x1A
+#define        BUS_ATARI               0x1B
+#define        BUS_SPI                 0x1C
+
+/*
+ * MT_TOOL types
+ */
+#define        MT_TOOL_FINGER          0
+#define        MT_TOOL_PEN             1
+#define        MT_TOOL_PALM            2
+#define        MT_TOOL_MAX             2
+
+/*
+ * Values describing the status of a force-feedback effect
+ */
+#define        FF_STATUS_STOPPED       0x00
+#define        FF_STATUS_PLAYING       0x01
+#define        FF_STATUS_MAX           0x01
+
+/* scheduling info for force feedback effect */
+struct ff_replay {
+       uint16_t        length;         /* length of the effect (ms) */
+       uint16_t        delay;          /* delay before effect starts (ms) */
+};
+
+/* trigger for force feedback effect */
+struct ff_trigger {
+       uint16_t        button;         /* trigger button number */
+       uint16_t        interval;       /* delay between re-triggers */
+};
+
+/* force feedback effect envelop */
+struct ff_envelope {
+       uint16_t        attack_length;  /* duration of the attach (ms) */
+       uint16_t        attack_level;   /* level at the beginning (0x0000 - 0x7fff) */
+       uint16_t        fade_length;    /* duratin of fade (ms) */
+       uint16_t        fade_level;     /* level at the end of fade */
+};
+
+struct ff_constant_effect {
+       int16_t                 level;
+       struct ff_envelope      envelope;
+};
+
+struct ff_ramp_effect {
+       int16_t                 start_level;
+       int16_t                 end_level;
+       struct ff_envelope      envelope;
+};
+
+struct ff_condition_effect {
+       /* maximum level when joystick moved to respective side */
+       uint16_t        right_saturation;
+
+       uint16_t        left_saturation;
+       /* how fast force grows when joystick move to the respective side */
+       int16_t         right_coeff;
+       int16_t         left_coeff;
+
+       uint16_t        deadband;       /* size of dead zone when no force is produced */
+       int16_t         center;         /* center of dead zone */
+};
+
+/*
+ * Force feedback periodic effect types
+ */
+
+#define        FF_SQUARE       0x58
+#define        FF_TRIANGLE     0x59
+#define        FF_SINE         0x5a
+#define        FF_SAW_UP       0x5b
+#define        FF_SAW_DOWN     0x5c
+#define        FF_CUSTOM       0x5d
+
+#define        FF_WAVEFORM_MIN FF_SQUARE
+#define        FF_WAVEFORM_MAX FF_CUSTOM
+
+struct ff_periodic_effect {
+       uint16_t                waveform;
+       uint16_t                period;         /* ms */
+       int16_t                 magnitude;      /* peak */
+       int16_t                 offset;         /* mean, roughly */
+       uint16_t                phase;          /* horizontal shift */
+       struct ff_envelope      envelope;
+       uint32_t                custom_len;     /* FF_CUSTOM waveform only */
+       int16_t                 *custom_data;   /* FF_CUSTOM waveform only */
+};
+
+struct ff_rumble_effect {
+       uint16_t        strong_magnitude;       /* magnitude of the heavy motor */
+       uint16_t        weak_magnitude;         /* magnitude of the light motor */
+};
+
+/*
+ * Force feedback effect types
+ */
+
+#define        FF_RUMBLE       0x50
+#define        FF_PERIODIC     0x51
+#define        FF_CONSTANT     0x52
+#define        FF_SPRING       0x53
+#define        FF_FRICTION     0x54
+#define        FF_DAMPER       0x55
+#define        FF_INERTIA      0x56
+#define        FF_RAMP         0x57
+
+#define        FF_EFFECT_MIN   FF_RUMBLE
+#define        FF_EFFECT_MAX   FF_RAMP
+
+struct ff_effect {
+       uint16_t                type;
+       int16_t                 id;
+       uint16_t                direction;      /* [0 .. 360) degrees -> [0 .. 0x10000) */
+       struct ff_trigger       trigger;
+       struct ff_replay        replay;
+
+       union {
+               struct ff_constant_effect       constant;
+               struct ff_ramp_effect           ramp;
+               struct ff_periodic_effect       periodic;
+               struct ff_condition_effect      condition[2]; /* One for each axis */
+               struct ff_rumble_effect         rumble;
+       } u;
+};
+
+/*
+ * force feedback device properties
+ */
+
+#define        FF_GAIN         0x60
+#define        FF_AUTOCENTER   0x61
+
+#define        FF_MAX          0x7f
+#define        FF_CNT          (FF_MAX+1)
+
+#endif /* _EVDEV_INPUT_H */