summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Kirspel <kevin-kirspel@idexx.com>2017-05-17 08:40:26 -0400
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-05-19 07:24:29 +0200
commit67de3b57f448b47bc22a271825ae8ccfd8e6db97 (patch)
tree9ebb75c7cdc781849da9c4ebfd89058cbc4259db
parentdevfs: Fix some issues (diff)
downloadrtems-libbsd-67de3b57f448b47bc22a271825ae8ccfd8e6db97.tar.bz2
Adding EVDEV FREEBSD files
-rw-r--r--freebsd/sys/dev/evdev/cdev.c863
-rw-r--r--freebsd/sys/dev/evdev/evdev.c939
-rw-r--r--freebsd/sys/dev/evdev/evdev.h210
-rw-r--r--freebsd/sys/dev/evdev/evdev_mt.c294
-rw-r--r--freebsd/sys/dev/evdev/evdev_private.h200
-rw-r--r--freebsd/sys/dev/evdev/evdev_utils.c335
-rw-r--r--freebsd/sys/dev/evdev/input-event-codes.h819
-rw-r--r--freebsd/sys/dev/evdev/input.h273
-rw-r--r--freebsd/sys/dev/evdev/uinput.c715
-rw-r--r--freebsd/sys/dev/evdev/uinput.h107
10 files changed, 4755 insertions, 0 deletions
diff --git a/freebsd/sys/dev/evdev/cdev.c b/freebsd/sys/dev/evdev/cdev.c
new file mode 100644
index 00000000..b9cc4cd5
--- /dev/null
+++ b/freebsd/sys/dev/evdev/cdev.c
@@ -0,0 +1,863 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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 <rtems/bsd/local/opt_evdev.h>
+
+#include <sys/types.h>
+#include <sys/bitstring.h>
+#include <sys/systm.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/selinfo.h>
+#include <sys/malloc.h>
+#include <sys/time.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#include <dev/evdev/evdev_private.h>
+
+#ifdef EVDEV_DEBUG
+#define debugf(client, fmt, args...) printf("evdev cdev: "fmt"\n", ##args)
+#else
+#define debugf(client, fmt, args...)
+#endif
+
+#define DEF_RING_REPORTS 8
+
+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_poll_t evdev_poll;
+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 cdevsw evdev_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = evdev_open,
+ .d_read = evdev_read,
+ .d_write = evdev_write,
+ .d_ioctl = evdev_ioctl,
+ .d_poll = evdev_poll,
+ .d_kqfilter = evdev_kqfilter,
+ .d_name = "evdev",
+};
+
+static struct filterops evdev_cdev_filterops = {
+ .f_isfd = 1,
+ .f_attach = NULL,
+ .f_detach = evdev_kqdetach,
+ .f_event = evdev_kqread,
+};
+
+static int
+evdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ 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 = malloc(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;
+ mtx_init(&client->ec_buffer_mtx, "evclient", "evdev", MTX_DEF);
+ knlist_init_mtx(&client->ec_selp.si_note, &client->ec_buffer_mtx);
+
+ /* 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(client, evdev_dtor);
+
+ if (ret != 0) {
+ debugf(client, "cannot register evdev client");
+ evdev_dtor(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);
+
+ knlist_clear(&client->ec_selp.si_note, 0);
+ seldrain(&client->ec_selp);
+ knlist_destroy(&client->ec_selp.si_note);
+ funsetown(&client->ec_sigio);
+ mtx_destroy(&client->ec_buffer_mtx);
+ free(client, M_EVDEV);
+}
+
+static int
+evdev_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct evdev_client *client;
+ struct input_event event;
+ int ret = 0;
+ int remaining;
+
+ ret = devfs_get_cdevpriv((void **)&client);
+ if (ret != 0)
+ return (ret);
+
+ debugf(client, "read %zd bytes by thread %d", uio->uio_resid,
+ uio->uio_td->td_tid);
+
+ 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 & O_NONBLOCK)
+ ret = EWOULDBLOCK;
+ else {
+ if (remaining != 0) {
+ client->ec_blocked = true;
+ ret = mtx_sleep(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(&event, sizeof(struct input_event), uio);
+ EVDEV_CLIENT_LOCKQ(client);
+ }
+
+ EVDEV_CLIENT_UNLOCKQ(client);
+
+ return (ret);
+}
+
+static int
+evdev_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct evdev_dev *evdev = dev->si_drv1;
+ struct evdev_client *client;
+ struct input_event event;
+ int ret = 0;
+
+ ret = devfs_get_cdevpriv((void **)&client);
+ if (ret != 0)
+ return (ret);
+
+ debugf(client, "write %zd bytes by thread %d", uio->uio_resid,
+ uio->uio_td->td_tid);
+
+ 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(&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_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct evdev_client *client;
+ int ret;
+ int revents = 0;
+
+ ret = devfs_get_cdevpriv((void **)&client);
+ if (ret != 0)
+ return (POLLNVAL);
+
+ debugf(client, "poll by thread %d", td->td_tid);
+
+ if (client->ec_revoked)
+ return (POLLHUP);
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ EVDEV_CLIENT_LOCKQ(client);
+ if (!EVDEV_CLIENT_EMPTYQ(client))
+ revents = events & (POLLIN | POLLRDNORM);
+ else {
+ client->ec_selected = true;
+ selrecord(td, &client->ec_selp);
+ }
+ EVDEV_CLIENT_UNLOCKQ(client);
+ }
+
+ return (revents);
+}
+
+static int
+evdev_kqfilter(struct cdev *dev, struct knote *kn)
+{
+ struct evdev_client *client;
+ int ret;
+
+ ret = devfs_get_cdevpriv((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;
+
+ knlist_add(&client->ec_selp.si_note, kn, 0);
+ return (0);
+}
+
+static int
+evdev_kqread(struct knote *kn, long hint)
+{
+ struct evdev_client *client;
+ int ret;
+
+ client = (struct evdev_client *)kn->kn_hook;
+
+ 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);
+ }
+ return (ret);
+}
+
+static void
+evdev_kqdetach(struct knote *kn)
+{
+ struct evdev_client *client;
+
+ client = (struct evdev_client *)kn->kn_hook;
+ knlist_remove(&client->ec_selp.si_note, kn, 0);
+}
+
+static int
+evdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
+{
+ 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((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)
+{
+ 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;
+ selwakeup(&client->ec_selp);
+ }
+ KNOTE_LOCKED(&client->ec_selp.si_note, 0);
+
+ if (client->ec_async && client->ec_sigio != NULL)
+ pgsigio(&client->ec_sigio, SIGIO, 0);
+}
+
+int
+evdev_cdev_create(struct evdev_dev *evdev)
+{
+ struct make_dev_args mda;
+ int ret, unit = 0;
+
+ make_dev_args_init(&mda);
+ mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
+ mda.mda_devsw = &evdev_cdevsw;
+ mda.mda_uid = UID_ROOT;
+ mda.mda_gid = GID_WHEEL;
+ mda.mda_mode = 0600;
+ mda.mda_si_drv1 = evdev;
+
+ /* Try to coexist with cuse-backed input/event devices */
+ while ((ret = make_dev_s(&mda, &evdev->ev_cdev, "input/event%d", unit))
+ == EEXIST)
+ unit++;
+
+ if (ret == 0)
+ evdev->ev_unit = unit;
+
+ return (ret);
+}
+
+int
+evdev_cdev_destroy(struct evdev_dev *evdev)
+{
+
+ destroy_dev(evdev->ev_cdev);
+ 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;
+
+ printf("evdev client: %p\n", client);
+ printf("event queue: head=%zu ready=%zu tail=%zu size=%zu\n",
+ head, ready, tail, size);
+
+ printf("queue contents:\n");
+
+ for (i = 0; i < size; i++) {
+ event = &client->ec_buffer[i];
+ printf("%zu: ", i);
+
+ if (i < head || i > tail)
+ printf("unused\n");
+ else
+ printf("type=%d code=%d value=%d ", event->type,
+ event->code, event->value);
+
+ if (i == head)
+ printf("<- head\n");
+ else if (i == tail)
+ printf("<- tail\n");
+ else if (i == ready)
+ printf("<- ready\n");
+ else
+ printf("\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/freebsd/sys/dev/evdev/evdev.c b/freebsd/sys/dev/evdev/evdev.c
new file mode 100644
index 00000000..e7575d5a
--- /dev/null
+++ b/freebsd/sys/dev/evdev/evdev.c
@@ -0,0 +1,939 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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 <rtems/bsd/local/opt_evdev.h>
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/bitstring.h>
+#include <sys/sysctl.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#include <dev/evdev/evdev_private.h>
+
+#ifdef EVDEV_DEBUG
+#define debugf(evdev, fmt, args...) printf("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;
+
+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 is receiving 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 malloc(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);
+
+ free(evdev, M_EVDEV);
+}
+
+static struct input_absinfo *
+evdev_alloc_absinfo(void)
+{
+
+ return (malloc(sizeof(struct input_absinfo) * ABS_CNT, M_EVDEV,
+ M_WAITOK | M_ZERO));
+}
+
+static void
+evdev_free_absinfo(struct input_absinfo *absinfo)
+{
+
+ free(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_mtx(&evdev->ev_rep_callout, &evdev->ev_mtx, 0);
+
+ 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;
+ mtx_init(&evdev->ev_mtx, "evmtx", NULL, MTX_DEF);
+
+ ret = evdev_register_common(evdev);
+ if (ret != 0)
+ mtx_destroy(&evdev->ev_mtx);
+
+ return (ret);
+}
+
+int
+evdev_register_mtx(struct evdev_dev *evdev, struct mtx *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);
+ }
+ EVDEV_UNLOCK(evdev);
+
+ /* destroy_dev can sleep so release lock */
+ ret = evdev_cdev_destroy(evdev);
+ evdev->ev_cdev = NULL;
+ if (ret == 0 && evdev->ev_lock_type == EV_LOCK_INTERNAL)
+ mtx_destroy(&evdev->ev_mtx);
+
+ evdev_free_absinfo(evdev->ev_absinfo);
+ evdev_mt_free(evdev);
+
+ return (ret);
+}
+
+inline void
+evdev_set_name(struct evdev_dev *evdev, const char *name)
+{
+
+ snprintf(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)
+{
+
+ snprintf(evdev->ev_shortname, NAMELEN, "%s", name);
+}
+
+inline void
+evdev_set_serial(struct evdev_dev *evdev, const char *serial)
+{
+
+ snprintf(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->ev_lock_type != EV_LOCK_INTERNAL)
+ EVDEV_LOCK_ASSERT(evdev);
+
+ if (evdev_check_event(evdev, type, code, value) != 0)
+ return (EINVAL);
+
+ if (evdev->ev_lock_type == EV_LOCK_INTERNAL)
+ EVDEV_LOCK(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);
+ if (evdev->ev_lock_type == EV_LOCK_INTERNAL)
+ EVDEV_UNLOCK(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;
+ }
+}
+
+MODULE_VERSION(evdev, 1);
diff --git a/freebsd/sys/dev/evdev/evdev.h b/freebsd/sys/dev/evdev/evdev.h
new file mode 100644
index 00000000..946913c4
--- /dev/null
+++ b/freebsd/sys/dev/evdev/evdev.h
@@ -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/evdev/input.h>
+#include <dev/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 mtx *);
+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/freebsd/sys/dev/evdev/evdev_mt.c b/freebsd/sys/dev/evdev/evdev_mt.c
new file mode 100644
index 00000000..9eb8a54e
--- /dev/null
+++ b/freebsd/sys/dev/evdev/evdev_mt.c
@@ -0,0 +1,294 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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 <rtems/bsd/sys/param.h>
+#include <sys/malloc.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#include <dev/evdev/evdev_private.h>
+
+#ifdef DEBUG
+#define debugf(fmt, args...) printf("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 = malloc(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)
+{
+
+ free(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)
+{
+
+ if (evdev->ev_lock_type == EV_LOCK_INTERNAL)
+ EVDEV_LOCK(evdev);
+ else
+ EVDEV_LOCK_ASSERT(evdev);
+ evdev_send_nfingers(evdev, nfingers);
+ if (evdev->ev_lock_type == EV_LOCK_INTERNAL)
+ EVDEV_UNLOCK(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)
+{
+
+ if (evdev->ev_lock_type == EV_LOCK_INTERNAL)
+ EVDEV_LOCK(evdev);
+ else
+ EVDEV_LOCK_ASSERT(evdev);
+ evdev_send_mt_compat(evdev);
+ if (evdev->ev_lock_type == EV_LOCK_INTERNAL)
+ EVDEV_UNLOCK(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/freebsd/sys/dev/evdev/evdev_private.h b/freebsd/sys/dev/evdev/evdev_private.h
new file mode 100644
index 00000000..b3de1bf0
--- /dev/null
+++ b/freebsd/sys/dev/evdev/evdev_private.h
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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/bitstring.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/kbio.h>
+#include <sys/selinfo.h>
+#include <dev/evdev/evdev.h>
+#include <dev/evdev/input.h>
+#include <dev/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.
+ */
+_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];
+ struct cdev * ev_cdev;
+ int ev_unit;
+ enum evdev_lock_type ev_lock_type;
+ struct mtx * ev_lock;
+ struct mtx 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) mtx_lock((evdev)->ev_lock)
+#define EVDEV_UNLOCK(evdev) mtx_unlock((evdev)->ev_lock)
+#define EVDEV_LOCK_ASSERT(evdev) mtx_assert((evdev)->ev_lock, MA_OWNED)
+
+struct evdev_client
+{
+ struct evdev_dev * ec_evdev;
+ struct mtx 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 selinfo ec_selp;
+ 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) mtx_lock(&(client)->ec_buffer_mtx)
+#define EVDEV_CLIENT_UNLOCKQ(client) mtx_unlock(&(client)->ec_buffer_mtx)
+#define EVDEV_CLIENT_LOCKQ_ASSERT(client) \
+ mtx_assert(&(client)->ec_buffer_mtx, MA_OWNED)
+#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/freebsd/sys/dev/evdev/evdev_utils.c b/freebsd/sys/dev/evdev/evdev_utils.c
new file mode 100644
index 00000000..787cc2d3
--- /dev/null
+++ b/freebsd/sys/dev/evdev/evdev_utils.c
@@ -0,0 +1,335 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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/types.h>
+#include <sys/systm.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/kbio.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#include <dev/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)
+ kbdd_ioctl(kbd, KDSETLED,
+ (caddr_t)&leds);
+ break;
+ }
+ }
+ } else if (type == EV_REP && code == REP_DELAY) {
+ delay[0] = value;
+ delay[1] = kbd->kb_delay2;
+ kbdd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
+ } else if (type == EV_REP && code == REP_PERIOD) {
+ delay[0] = kbd->kb_delay1;
+ delay[1] = value;
+ kbdd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
+ }
+}
diff --git a/freebsd/sys/dev/evdev/input-event-codes.h b/freebsd/sys/dev/evdev/input-event-codes.h
new file mode 100644
index 00000000..78ba7d2e
--- /dev/null
+++ b/freebsd/sys/dev/evdev/input-event-codes.h
@@ -0,0 +1,819 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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/freebsd/sys/dev/evdev/input.h b/freebsd/sys/dev/evdev/input.h
new file mode 100644
index 00000000..04638444
--- /dev/null
+++ b/freebsd/sys/dev/evdev/input.h
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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
+
+#ifndef __KERNEL__
+#include <sys/time.h>
+#include <sys/ioccom.h>
+#include <sys/types.h>
+#endif
+
+#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 */
diff --git a/freebsd/sys/dev/evdev/uinput.c b/freebsd/sys/dev/evdev/uinput.c
new file mode 100644
index 00000000..c66afe63
--- /dev/null
+++ b/freebsd/sys/dev/evdev/uinput.c
@@ -0,0 +1,715 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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 <rtems/bsd/local/opt_evdev.h>
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+#include <sys/malloc.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/sx.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/uinput.h>
+#include <dev/evdev/evdev.h>
+#include <dev/evdev/evdev_private.h>
+
+#ifdef UINPUT_DEBUG
+#define debugf(state, fmt, args...) printf("uinput: " fmt "\n", ##args)
+#else
+#define debugf(state, fmt, args...)
+#endif
+
+#define UINPUT_BUFFER_SIZE 16
+
+#define UINPUT_LOCK(state) sx_xlock(&(state)->ucs_lock)
+#define UINPUT_UNLOCK(state) sx_unlock(&(state)->ucs_lock)
+#define UINPUT_LOCK_ASSERT(state) sx_assert(&(state)->ucs_lock, SA_LOCKED)
+#define UINPUT_EMPTYQ(state) \
+ ((state)->ucs_buffer_head == (state)->ucs_buffer_tail)
+
+enum uinput_state
+{
+ UINPUT_NEW = 0,
+ UINPUT_CONFIGURED,
+ UINPUT_RUNNING
+};
+
+static evdev_event_t uinput_ev_event;
+
+static d_open_t uinput_open;
+static d_read_t uinput_read;
+static d_write_t uinput_write;
+static d_ioctl_t uinput_ioctl;
+static d_poll_t uinput_poll;
+static d_kqfilter_t uinput_kqfilter;
+static void uinput_dtor(void *);
+
+static int uinput_kqread(struct knote *kn, long hint);
+static void uinput_kqdetach(struct knote *kn);
+
+static struct cdevsw uinput_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = uinput_open,
+ .d_read = uinput_read,
+ .d_write = uinput_write,
+ .d_ioctl = uinput_ioctl,
+ .d_poll = uinput_poll,
+ .d_kqfilter = uinput_kqfilter,
+ .d_name = "uinput",
+};
+
+static struct cdev *uinput_cdev;
+
+static struct evdev_methods uinput_ev_methods = {
+ .ev_open = NULL,
+ .ev_close = NULL,
+ .ev_event = uinput_ev_event,
+};
+
+static struct filterops uinput_filterops = {
+ .f_isfd = 1,
+ .f_attach = NULL,
+ .f_detach = uinput_kqdetach,
+ .f_event = uinput_kqread,
+};
+
+struct uinput_cdev_state
+{
+ enum uinput_state ucs_state;
+ struct evdev_dev * ucs_evdev;
+ struct sx ucs_lock;
+ size_t ucs_buffer_head;
+ size_t ucs_buffer_tail;
+ struct selinfo ucs_selp;
+ bool ucs_blocked;
+ bool ucs_selected;
+ struct input_event ucs_buffer[UINPUT_BUFFER_SIZE];
+};
+
+static void uinput_enqueue_event(struct uinput_cdev_state *, uint16_t,
+ uint16_t, int32_t);
+static int uinput_setup_provider(struct uinput_cdev_state *,
+ struct uinput_user_dev *);
+static int uinput_cdev_create(void);
+static void uinput_notify(struct uinput_cdev_state *);
+
+static void
+uinput_knllock(void *arg)
+{
+ struct sx *sx = arg;
+
+ sx_xlock(sx);
+}
+
+static void
+uinput_knlunlock(void *arg)
+{
+ struct sx *sx = arg;
+
+ sx_unlock(sx);
+}
+
+static void
+uinput_knl_assert_locked(void *arg)
+{
+
+ sx_assert((struct sx*)arg, SA_XLOCKED);
+}
+
+static void
+uinput_knl_assert_unlocked(void *arg)
+{
+
+ sx_assert((struct sx*)arg, SA_UNLOCKED);
+}
+
+static void
+uinput_ev_event(struct evdev_dev *evdev, void *softc, uint16_t type,
+ uint16_t code, int32_t value)
+{
+ struct uinput_cdev_state *state = softc;
+
+ if (type == EV_LED)
+ evdev_push_event(evdev, type, code, value);
+
+ UINPUT_LOCK(state);
+ if (state->ucs_state == UINPUT_RUNNING) {
+ uinput_enqueue_event(state, type, code, value);
+ uinput_notify(state);
+ }
+ UINPUT_UNLOCK(state);
+}
+
+static void
+uinput_enqueue_event(struct uinput_cdev_state *state, uint16_t type,
+ uint16_t code, int32_t value)
+{
+ size_t head, tail;
+
+ UINPUT_LOCK_ASSERT(state);
+
+ head = state->ucs_buffer_head;
+ tail = (state->ucs_buffer_tail + 1) % UINPUT_BUFFER_SIZE;
+
+ microtime(&state->ucs_buffer[tail].time);
+ state->ucs_buffer[tail].type = type;
+ state->ucs_buffer[tail].code = code;
+ state->ucs_buffer[tail].value = value;
+ state->ucs_buffer_tail = tail;
+
+ /* If queue is full remove oldest event */
+ if (tail == head) {
+ debugf(state, "state %p: buffer overflow", state);
+
+ head = (head + 1) % UINPUT_BUFFER_SIZE;
+ state->ucs_buffer_head = head;
+ }
+}
+
+static int
+uinput_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct uinput_cdev_state *state;
+
+ state = malloc(sizeof(struct uinput_cdev_state), M_EVDEV,
+ M_WAITOK | M_ZERO);
+ state->ucs_evdev = evdev_alloc();
+
+ sx_init(&state->ucs_lock, "uinput");
+ knlist_init(&state->ucs_selp.si_note, &state->ucs_lock, uinput_knllock,
+ uinput_knlunlock, uinput_knl_assert_locked,
+ uinput_knl_assert_unlocked);
+
+ devfs_set_cdevpriv(state, uinput_dtor);
+ return (0);
+}
+
+static void
+uinput_dtor(void *data)
+{
+ struct uinput_cdev_state *state = (struct uinput_cdev_state *)data;
+
+ evdev_free(state->ucs_evdev);
+
+ knlist_clear(&state->ucs_selp.si_note, 0);
+ seldrain(&state->ucs_selp);
+ knlist_destroy(&state->ucs_selp.si_note);
+ sx_destroy(&state->ucs_lock);
+ free(data, M_EVDEV);
+}
+
+static int
+uinput_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct uinput_cdev_state *state;
+ struct input_event *event;
+ int remaining, ret;
+
+ ret = devfs_get_cdevpriv((void **)&state);
+ if (ret != 0)
+ return (ret);
+
+ debugf(state, "read %zd bytes by thread %d", uio->uio_resid,
+ uio->uio_td->td_tid);
+
+ /* 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);
+
+ UINPUT_LOCK(state);
+
+ if (state->ucs_state != UINPUT_RUNNING)
+ ret = EINVAL;
+
+ if (ret == 0 && UINPUT_EMPTYQ(state)) {
+ if (ioflag & O_NONBLOCK)
+ ret = EWOULDBLOCK;
+ else {
+ if (remaining != 0) {
+ state->ucs_blocked = true;
+ ret = sx_sleep(state, &state->ucs_lock,
+ PCATCH, "uiread", 0);
+ }
+ }
+ }
+
+ while (ret == 0 && !UINPUT_EMPTYQ(state) && remaining > 0) {
+ event = &state->ucs_buffer[state->ucs_buffer_head];
+ state->ucs_buffer_head = (state->ucs_buffer_head + 1) %
+ UINPUT_BUFFER_SIZE;
+ remaining--;
+ ret = uiomove(event, sizeof(struct input_event), uio);
+ }
+
+ UINPUT_UNLOCK(state);
+
+ return (ret);
+}
+
+static int
+uinput_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct uinput_cdev_state *state;
+ struct uinput_user_dev userdev;
+ struct input_event event;
+ int ret = 0;
+
+ ret = devfs_get_cdevpriv((void **)&state);
+ if (ret != 0)
+ return (ret);
+
+ debugf(state, "write %zd bytes by thread %d", uio->uio_resid,
+ uio->uio_td->td_tid);
+
+ UINPUT_LOCK(state);
+
+ if (state->ucs_state != UINPUT_RUNNING) {
+ /* Process written struct uinput_user_dev */
+ if (uio->uio_resid != sizeof(struct uinput_user_dev)) {
+ debugf(state, "write size not multiple of "
+ "struct uinput_user_dev size");
+ ret = EINVAL;
+ } else {
+ ret = uiomove(&userdev, sizeof(struct uinput_user_dev),
+ uio);
+ if (ret == 0)
+ uinput_setup_provider(state, &userdev);
+ }
+ } else {
+ /* Process written event */
+ if (uio->uio_resid % sizeof(struct input_event) != 0) {
+ debugf(state, "write size not multiple of "
+ "struct input_event size");
+ ret = EINVAL;
+ }
+
+ while (ret == 0 && uio->uio_resid > 0) {
+ uiomove(&event, sizeof(struct input_event), uio);
+ ret = evdev_push_event(state->ucs_evdev, event.type,
+ event.code, event.value);
+ }
+ }
+
+ UINPUT_UNLOCK(state);
+
+ return (ret);
+}
+
+static int
+uinput_setup_dev(struct uinput_cdev_state *state, struct input_id *id,
+ char *name, uint32_t ff_effects_max)
+{
+
+ if (name[0] == 0)
+ return (EINVAL);
+
+ evdev_set_name(state->ucs_evdev, name);
+ evdev_set_id(state->ucs_evdev, id->bustype, id->vendor, id->product,
+ id->version);
+ state->ucs_state = UINPUT_CONFIGURED;
+
+ return (0);
+}
+
+static int
+uinput_setup_provider(struct uinput_cdev_state *state,
+ struct uinput_user_dev *udev)
+{
+ struct input_absinfo absinfo;
+ int i, ret;
+
+ debugf(state, "setup_provider called, udev=%p", udev);
+
+ ret = uinput_setup_dev(state, &udev->id, udev->name,
+ udev->ff_effects_max);
+ if (ret)
+ return (ret);
+
+ bzero(&absinfo, sizeof(struct input_absinfo));
+ for (i = 0; i < ABS_CNT; i++) {
+ if (!bit_test(state->ucs_evdev->ev_abs_flags, i))
+ continue;
+
+ absinfo.minimum = udev->absmin[i];
+ absinfo.maximum = udev->absmax[i];
+ absinfo.fuzz = udev->absfuzz[i];
+ absinfo.flat = udev->absflat[i];
+ evdev_set_absinfo(state->ucs_evdev, i, &absinfo);
+ }
+
+ return (0);
+}
+
+static int
+uinput_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct uinput_cdev_state *state;
+ int revents = 0;
+
+ if (devfs_get_cdevpriv((void **)&state) != 0)
+ return (POLLNVAL);
+
+ debugf(state, "poll by thread %d", td->td_tid);
+
+ /* Always allow write */
+ if (events & (POLLOUT | POLLWRNORM))
+ revents |= (events & (POLLOUT | POLLWRNORM));
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ UINPUT_LOCK(state);
+ if (!UINPUT_EMPTYQ(state))
+ revents = events & (POLLIN | POLLRDNORM);
+ else {
+ state->ucs_selected = true;
+ selrecord(td, &state->ucs_selp);
+ }
+ UINPUT_UNLOCK(state);
+ }
+
+ return (revents);
+}
+
+static int
+uinput_kqfilter(struct cdev *dev, struct knote *kn)
+{
+ struct uinput_cdev_state *state;
+ int ret;
+
+ ret = devfs_get_cdevpriv((void **)&state);
+ if (ret != 0)
+ return (ret);
+
+ switch(kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_fop = &uinput_filterops;
+ break;
+ default:
+ return(EINVAL);
+ }
+ kn->kn_hook = (caddr_t)state;
+
+ knlist_add(&state->ucs_selp.si_note, kn, 0);
+ return (0);
+}
+
+static int
+uinput_kqread(struct knote *kn, long hint)
+{
+ struct uinput_cdev_state *state;
+ int ret;
+
+ state = (struct uinput_cdev_state *)kn->kn_hook;
+
+ UINPUT_LOCK_ASSERT(state);
+
+ ret = !UINPUT_EMPTYQ(state);
+ return (ret);
+}
+
+static void
+uinput_kqdetach(struct knote *kn)
+{
+ struct uinput_cdev_state *state;
+
+ state = (struct uinput_cdev_state *)kn->kn_hook;
+ knlist_remove(&state->ucs_selp.si_note, kn, 0);
+}
+
+static void
+uinput_notify(struct uinput_cdev_state *state)
+{
+
+ UINPUT_LOCK_ASSERT(state);
+
+ if (state->ucs_blocked) {
+ state->ucs_blocked = false;
+ wakeup(state);
+ }
+ if (state->ucs_selected) {
+ state->ucs_selected = false;
+ selwakeup(&state->ucs_selp);
+ }
+ KNOTE_LOCKED(&state->ucs_selp.si_note, 0);
+}
+
+static int
+uinput_ioctl_sub(struct uinput_cdev_state *state, u_long cmd, caddr_t data)
+{
+ struct uinput_setup *us;
+ struct uinput_abs_setup *uabs;
+ int ret, len, intdata;
+ char buf[NAMELEN];
+
+ UINPUT_LOCK_ASSERT(state);
+
+ len = IOCPARM_LEN(cmd);
+ if ((cmd & IOC_DIRMASK) == IOC_VOID && len == sizeof(int))
+ intdata = *(int *)data;
+
+ switch (IOCBASECMD(cmd)) {
+ case UI_GET_SYSNAME(0):
+ if (state->ucs_state != UINPUT_RUNNING)
+ return (ENOENT);
+ if (len == 0)
+ return (EINVAL);
+ snprintf(data, len, "event%d", state->ucs_evdev->ev_unit);
+ return (0);
+ }
+
+ switch (cmd) {
+ case UI_DEV_CREATE:
+ if (state->ucs_state != UINPUT_CONFIGURED)
+ return (EINVAL);
+
+ evdev_set_methods(state->ucs_evdev, state, &uinput_ev_methods);
+ evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_SOFTREPEAT);
+ ret = evdev_register(state->ucs_evdev);
+ if (ret == 0)
+ state->ucs_state = UINPUT_RUNNING;
+ return (ret);
+
+ case UI_DEV_DESTROY:
+ if (state->ucs_state != UINPUT_RUNNING)
+ return (0);
+
+ evdev_unregister(state->ucs_evdev);
+ bzero(state->ucs_evdev, sizeof(struct evdev_dev));
+ state->ucs_state = UINPUT_NEW;
+ return (0);
+
+ case UI_DEV_SETUP:
+ if (state->ucs_state == UINPUT_RUNNING)
+ return (EINVAL);
+
+ us = (struct uinput_setup *)data;
+ return (uinput_setup_dev(state, &us->id, us->name,
+ us->ff_effects_max));
+
+ case UI_ABS_SETUP:
+ if (state->ucs_state == UINPUT_RUNNING)
+ return (EINVAL);
+
+ uabs = (struct uinput_abs_setup *)data;
+ if (uabs->code > ABS_MAX)
+ return (EINVAL);
+
+ evdev_support_abs(state->ucs_evdev, uabs->code,
+ uabs->absinfo.value, uabs->absinfo.minimum,
+ uabs->absinfo.maximum, uabs->absinfo.fuzz,
+ uabs->absinfo.flat, uabs->absinfo.resolution);
+ return (0);
+
+ case UI_SET_EVBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > EV_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_event(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_KEYBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > KEY_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_key(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_RELBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > REL_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_rel(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_ABSBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > ABS_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_set_abs_bit(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_MSCBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > MSC_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_msc(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_LEDBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > LED_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_led(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_SNDBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > SND_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_snd(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_FFBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > FF_MAX || intdata < 0)
+ return (EINVAL);
+ /* Fake unsupported ioctl */
+ return (0);
+
+ case UI_SET_PHYS:
+ if (state->ucs_state == UINPUT_RUNNING)
+ return (EINVAL);
+ ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
+ /* Linux returns EINVAL when string does not fit the buffer */
+ if (ret == ENAMETOOLONG)
+ ret = EINVAL;
+ if (ret != 0)
+ return (ret);
+ evdev_set_phys(state->ucs_evdev, buf);
+ return (0);
+
+ case UI_SET_SWBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > SW_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_sw(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_SET_PROPBIT:
+ if (state->ucs_state == UINPUT_RUNNING ||
+ intdata > INPUT_PROP_MAX || intdata < 0)
+ return (EINVAL);
+ evdev_support_prop(state->ucs_evdev, intdata);
+ return (0);
+
+ case UI_BEGIN_FF_UPLOAD:
+ case UI_END_FF_UPLOAD:
+ case UI_BEGIN_FF_ERASE:
+ case UI_END_FF_ERASE:
+ if (state->ucs_state == UINPUT_RUNNING)
+ return (EINVAL);
+ /* Fake unsupported ioctl */
+ return (0);
+
+ case UI_GET_VERSION:
+ *(unsigned int *)data = UINPUT_VERSION;
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+uinput_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
+{
+ struct uinput_cdev_state *state;
+ int ret;
+
+ ret = devfs_get_cdevpriv((void **)&state);
+ if (ret != 0)
+ return (ret);
+
+ debugf(state, "ioctl called: cmd=0x%08lx, data=%p", cmd, data);
+
+ UINPUT_LOCK(state);
+ ret = uinput_ioctl_sub(state, cmd, data);
+ UINPUT_UNLOCK(state);
+
+ return (ret);
+}
+
+static int
+uinput_cdev_create(void)
+{
+ struct make_dev_args mda;
+ int ret;
+
+ make_dev_args_init(&mda);
+ mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
+ mda.mda_devsw = &uinput_cdevsw;
+ mda.mda_uid = UID_ROOT;
+ mda.mda_gid = GID_WHEEL;
+ mda.mda_mode = 0600;
+
+ ret = make_dev_s(&mda, &uinput_cdev, "uinput");
+
+ return (ret);
+}
+
+static int
+uinput_cdev_destroy(void)
+{
+
+ destroy_dev(uinput_cdev);
+
+ return (0);
+}
+
+static int
+uinput_modevent(module_t mod __unused, int cmd, void *data)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case MOD_LOAD:
+ ret = uinput_cdev_create();
+ break;
+
+ case MOD_UNLOAD:
+ ret = uinput_cdev_destroy();
+ break;
+
+ case MOD_SHUTDOWN:
+ break;
+
+ default:
+ ret = EINVAL;
+ break;
+ }
+
+ return (ret);
+}
+
+DEV_MODULE(uinput, uinput_modevent, NULL);
+MODULE_VERSION(uinput, 1);
+MODULE_DEPEND(uinput, evdev, 1, 1, 1);
diff --git a/freebsd/sys/dev/evdev/uinput.h b/freebsd/sys/dev/evdev/uinput.h
new file mode 100644
index 00000000..f1721e19
--- /dev/null
+++ b/freebsd/sys/dev/evdev/uinput.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru>
+ * 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_UINPUT_H_
+#define _EVDEV_UINPUT_H_
+
+#include <sys/types.h>
+#include <dev/evdev/input.h>
+
+#define UINPUT_VERSION 5
+#define UINPUT_MAX_NAME_SIZE 80
+
+struct uinput_ff_upload {
+ uint32_t request_id;
+ int32_t retval;
+ struct ff_effect effect;
+ struct ff_effect old;
+};
+
+struct uinput_ff_erase {
+ uint32_t request_id;
+ int32_t retval;
+ uint32_t effect_id;
+};
+
+/* ioctl */
+#define UINPUT_IOCTL_BASE 'U'
+
+#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
+#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
+
+struct uinput_setup {
+ struct input_id id;
+ char name[UINPUT_MAX_NAME_SIZE];
+ uint32_t ff_effects_max;
+};
+
+#define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
+
+struct uinput_abs_setup {
+ uint16_t code; /* axis code */
+ struct input_absinfo absinfo;
+};
+
+#define UI_ABS_SETUP _IOW(UINPUT_IOCTL_BASE, 4, struct uinput_abs_setup)
+
+#define UI_GET_SYSNAME(len) _IOC(IOC_OUT, UINPUT_IOCTL_BASE, 44, len)
+#define UI_GET_VERSION _IOR(UINPUT_IOCTL_BASE, 45, unsigned int)
+
+#define UI_SET_EVBIT _IOWINT(UINPUT_IOCTL_BASE, 100)
+#define UI_SET_KEYBIT _IOWINT(UINPUT_IOCTL_BASE, 101)
+#define UI_SET_RELBIT _IOWINT(UINPUT_IOCTL_BASE, 102)
+#define UI_SET_ABSBIT _IOWINT(UINPUT_IOCTL_BASE, 103)
+#define UI_SET_MSCBIT _IOWINT(UINPUT_IOCTL_BASE, 104)
+#define UI_SET_LEDBIT _IOWINT(UINPUT_IOCTL_BASE, 105)
+#define UI_SET_SNDBIT _IOWINT(UINPUT_IOCTL_BASE, 106)
+#define UI_SET_FFBIT _IOWINT(UINPUT_IOCTL_BASE, 107)
+#define UI_SET_PHYS _IO(UINPUT_IOCTL_BASE, 108)
+#define UI_SET_SWBIT _IOWINT(UINPUT_IOCTL_BASE, 109)
+#define UI_SET_PROPBIT _IOWINT(UINPUT_IOCTL_BASE, 110)
+
+#define UI_BEGIN_FF_UPLOAD _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload)
+#define UI_END_FF_UPLOAD _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload)
+#define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase)
+#define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase)
+
+#define EV_UINPUT 0x0101
+#define UI_FF_UPLOAD 1
+#define UI_FF_ERASE 2
+
+struct uinput_user_dev {
+ char name[UINPUT_MAX_NAME_SIZE];
+ struct input_id id;
+ uint32_t ff_effects_max;
+ int32_t absmax[ABS_CNT];
+ int32_t absmin[ABS_CNT];
+ int32_t absfuzz[ABS_CNT];
+ int32_t absflat[ABS_CNT];
+};
+
+#endif /* _EVDEV_UINPUT_H_ */