[PATCH 01/11] Adding EVDEV FREEBSD files

Kevin Kirspel kevin-kirspel at idexx.com
Mon May 15 17:26:28 UTC 2017


---
 freebsd/sys/dev/evdev/cdev.c              | 863 +++++++++++++++++++++++++++
 freebsd/sys/dev/evdev/evdev.c             | 939 ++++++++++++++++++++++++++++++
 freebsd/sys/dev/evdev/evdev.h             | 210 +++++++
 freebsd/sys/dev/evdev/evdev_mt.c          | 294 ++++++++++
 freebsd/sys/dev/evdev/evdev_private.h     | 200 +++++++
 freebsd/sys/dev/evdev/evdev_utils.c       | 335 +++++++++++
 freebsd/sys/dev/evdev/input-event-codes.h | 819 ++++++++++++++++++++++++++
 freebsd/sys/dev/evdev/input.h             | 273 +++++++++
 freebsd/sys/dev/evdev/uinput.c            | 715 +++++++++++++++++++++++
 freebsd/sys/dev/evdev/uinput.h            | 107 ++++
 10 files changed, 4755 insertions(+)
 create mode 100644 freebsd/sys/dev/evdev/cdev.c
 create mode 100644 freebsd/sys/dev/evdev/evdev.c
 create mode 100644 freebsd/sys/dev/evdev/evdev.h
 create mode 100644 freebsd/sys/dev/evdev/evdev_mt.c
 create mode 100644 freebsd/sys/dev/evdev/evdev_private.h
 create mode 100644 freebsd/sys/dev/evdev/evdev_utils.c
 create mode 100644 freebsd/sys/dev/evdev/input-event-codes.h
 create mode 100644 freebsd/sys/dev/evdev/input.h
 create mode 100644 freebsd/sys/dev/evdev/uinput.c
 create mode 100644 freebsd/sys/dev/evdev/uinput.h

diff --git a/freebsd/sys/dev/evdev/cdev.c b/freebsd/sys/dev/evdev/cdev.c
new file mode 100644
index 0000000..b9cc4cd
--- /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 at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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 0000000..e7575d5
--- /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 at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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 0000000..946913c
--- /dev/null
+++ b/freebsd/sys/dev/evdev/evdev.h
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel at 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 0000000..9eb8a54
--- /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 at 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 0000000..b3de1bf
--- /dev/null
+++ b/freebsd/sys/dev/evdev/evdev_private.h
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 2014 Jakub Wojciech Klama <jceel at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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 0000000..787cc2d
--- /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 at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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 0000000..78ba7d2
--- /dev/null
+++ b/freebsd/sys/dev/evdev/input-event-codes.h
@@ -0,0 +1,819 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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 0000000..0463844
--- /dev/null
+++ b/freebsd/sys/dev/evdev/input.h
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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 0000000..c66afe6
--- /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 at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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 0000000..f1721e1
--- /dev/null
+++ b/freebsd/sys/dev/evdev/uinput.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+ * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf at 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_ */
-- 
1.9.1



More information about the devel mailing list