xref: /freebsd/sys/dev/evdev/uinput.c (revision 911aed94fa3a6b7c99aae26c48c0724ada8ab787)
12b3f6d66SOleksandr Tymoshenko /*-
22b3f6d66SOleksandr Tymoshenko  * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
3e6502802SVladimir Kondratyev  * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
42b3f6d66SOleksandr Tymoshenko  * All rights reserved.
52b3f6d66SOleksandr Tymoshenko  *
62b3f6d66SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
72b3f6d66SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
82b3f6d66SOleksandr Tymoshenko  * are met:
92b3f6d66SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
102b3f6d66SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
112b3f6d66SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
122b3f6d66SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
132b3f6d66SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
142b3f6d66SOleksandr Tymoshenko  *
152b3f6d66SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162b3f6d66SOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172b3f6d66SOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182b3f6d66SOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192b3f6d66SOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202b3f6d66SOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212b3f6d66SOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222b3f6d66SOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232b3f6d66SOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242b3f6d66SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252b3f6d66SOleksandr Tymoshenko  * SUCH DAMAGE.
262b3f6d66SOleksandr Tymoshenko  *
272b3f6d66SOleksandr Tymoshenko  * $FreeBSD$
282b3f6d66SOleksandr Tymoshenko  */
292b3f6d66SOleksandr Tymoshenko 
302b3f6d66SOleksandr Tymoshenko #include "opt_evdev.h"
312b3f6d66SOleksandr Tymoshenko 
322b3f6d66SOleksandr Tymoshenko #include <sys/param.h>
33ea2e26b1SVladimir Kondratyev #include <sys/conf.h>
342b3f6d66SOleksandr Tymoshenko #include <sys/fcntl.h>
352b3f6d66SOleksandr Tymoshenko #include <sys/kernel.h>
362b3f6d66SOleksandr Tymoshenko #include <sys/lock.h>
37ea2e26b1SVladimir Kondratyev #include <sys/malloc.h>
38ea2e26b1SVladimir Kondratyev #include <sys/module.h>
39ea2e26b1SVladimir Kondratyev #include <sys/poll.h>
40ea2e26b1SVladimir Kondratyev #include <sys/proc.h>
41ea2e26b1SVladimir Kondratyev #include <sys/selinfo.h>
42ea2e26b1SVladimir Kondratyev #include <sys/systm.h>
432b3f6d66SOleksandr Tymoshenko #include <sys/sx.h>
44ea2e26b1SVladimir Kondratyev #include <sys/uio.h>
452b3f6d66SOleksandr Tymoshenko 
462b3f6d66SOleksandr Tymoshenko #include <dev/evdev/evdev.h>
472b3f6d66SOleksandr Tymoshenko #include <dev/evdev/evdev_private.h>
48ea2e26b1SVladimir Kondratyev #include <dev/evdev/input.h>
49ea2e26b1SVladimir Kondratyev #include <dev/evdev/uinput.h>
502b3f6d66SOleksandr Tymoshenko 
512b3f6d66SOleksandr Tymoshenko #ifdef UINPUT_DEBUG
52e0cfa1bcSOleksandr Tymoshenko #define	debugf(state, fmt, args...)	printf("uinput: " fmt "\n", ##args)
532b3f6d66SOleksandr Tymoshenko #else
542b3f6d66SOleksandr Tymoshenko #define	debugf(state, fmt, args...)
552b3f6d66SOleksandr Tymoshenko #endif
562b3f6d66SOleksandr Tymoshenko 
572b3f6d66SOleksandr Tymoshenko #define	UINPUT_BUFFER_SIZE	16
582b3f6d66SOleksandr Tymoshenko 
592b3f6d66SOleksandr Tymoshenko #define	UINPUT_LOCK(state)		sx_xlock(&(state)->ucs_lock)
602b3f6d66SOleksandr Tymoshenko #define	UINPUT_UNLOCK(state)		sx_unlock(&(state)->ucs_lock)
612b3f6d66SOleksandr Tymoshenko #define	UINPUT_LOCK_ASSERT(state)	sx_assert(&(state)->ucs_lock, SA_LOCKED)
622b3f6d66SOleksandr Tymoshenko #define UINPUT_EMPTYQ(state) \
632b3f6d66SOleksandr Tymoshenko     ((state)->ucs_buffer_head == (state)->ucs_buffer_tail)
642b3f6d66SOleksandr Tymoshenko 
652b3f6d66SOleksandr Tymoshenko enum uinput_state
662b3f6d66SOleksandr Tymoshenko {
672b3f6d66SOleksandr Tymoshenko 	UINPUT_NEW = 0,
682b3f6d66SOleksandr Tymoshenko 	UINPUT_CONFIGURED,
692b3f6d66SOleksandr Tymoshenko 	UINPUT_RUNNING
702b3f6d66SOleksandr Tymoshenko };
712b3f6d66SOleksandr Tymoshenko 
722b3f6d66SOleksandr Tymoshenko static evdev_event_t	uinput_ev_event;
732b3f6d66SOleksandr Tymoshenko 
742b3f6d66SOleksandr Tymoshenko static d_open_t		uinput_open;
752b3f6d66SOleksandr Tymoshenko static d_read_t		uinput_read;
762b3f6d66SOleksandr Tymoshenko static d_write_t	uinput_write;
772b3f6d66SOleksandr Tymoshenko static d_ioctl_t	uinput_ioctl;
782b3f6d66SOleksandr Tymoshenko static d_poll_t		uinput_poll;
792b3f6d66SOleksandr Tymoshenko static d_kqfilter_t	uinput_kqfilter;
802b3f6d66SOleksandr Tymoshenko static void uinput_dtor(void *);
812b3f6d66SOleksandr Tymoshenko 
822b3f6d66SOleksandr Tymoshenko static int uinput_kqread(struct knote *kn, long hint);
832b3f6d66SOleksandr Tymoshenko static void uinput_kqdetach(struct knote *kn);
842b3f6d66SOleksandr Tymoshenko 
852b3f6d66SOleksandr Tymoshenko static struct cdevsw uinput_cdevsw = {
862b3f6d66SOleksandr Tymoshenko 	.d_version = D_VERSION,
872b3f6d66SOleksandr Tymoshenko 	.d_open = uinput_open,
882b3f6d66SOleksandr Tymoshenko 	.d_read = uinput_read,
892b3f6d66SOleksandr Tymoshenko 	.d_write = uinput_write,
902b3f6d66SOleksandr Tymoshenko 	.d_ioctl = uinput_ioctl,
912b3f6d66SOleksandr Tymoshenko 	.d_poll = uinput_poll,
922b3f6d66SOleksandr Tymoshenko 	.d_kqfilter = uinput_kqfilter,
932b3f6d66SOleksandr Tymoshenko 	.d_name = "uinput",
942b3f6d66SOleksandr Tymoshenko };
952b3f6d66SOleksandr Tymoshenko 
962b3f6d66SOleksandr Tymoshenko static struct cdev *uinput_cdev;
972b3f6d66SOleksandr Tymoshenko 
982b3f6d66SOleksandr Tymoshenko static struct evdev_methods uinput_ev_methods = {
992b3f6d66SOleksandr Tymoshenko 	.ev_open = NULL,
1002b3f6d66SOleksandr Tymoshenko 	.ev_close = NULL,
1012b3f6d66SOleksandr Tymoshenko 	.ev_event = uinput_ev_event,
1022b3f6d66SOleksandr Tymoshenko };
1032b3f6d66SOleksandr Tymoshenko 
1042b3f6d66SOleksandr Tymoshenko static struct filterops uinput_filterops = {
1052b3f6d66SOleksandr Tymoshenko 	.f_isfd = 1,
1062b3f6d66SOleksandr Tymoshenko 	.f_attach = NULL,
1072b3f6d66SOleksandr Tymoshenko 	.f_detach = uinput_kqdetach,
1082b3f6d66SOleksandr Tymoshenko 	.f_event = uinput_kqread,
1092b3f6d66SOleksandr Tymoshenko };
1102b3f6d66SOleksandr Tymoshenko 
1112b3f6d66SOleksandr Tymoshenko struct uinput_cdev_state
1122b3f6d66SOleksandr Tymoshenko {
1132b3f6d66SOleksandr Tymoshenko 	enum uinput_state	ucs_state;
1142b3f6d66SOleksandr Tymoshenko 	struct evdev_dev *	ucs_evdev;
1152b3f6d66SOleksandr Tymoshenko 	struct sx		ucs_lock;
1162b3f6d66SOleksandr Tymoshenko 	size_t			ucs_buffer_head;
1172b3f6d66SOleksandr Tymoshenko 	size_t			ucs_buffer_tail;
1182b3f6d66SOleksandr Tymoshenko 	struct selinfo		ucs_selp;
1192b3f6d66SOleksandr Tymoshenko 	bool			ucs_blocked;
1202b3f6d66SOleksandr Tymoshenko 	bool			ucs_selected;
1212b3f6d66SOleksandr Tymoshenko 	struct input_event      ucs_buffer[UINPUT_BUFFER_SIZE];
1222b3f6d66SOleksandr Tymoshenko };
1232b3f6d66SOleksandr Tymoshenko 
1242b3f6d66SOleksandr Tymoshenko static void uinput_enqueue_event(struct uinput_cdev_state *, uint16_t,
1252b3f6d66SOleksandr Tymoshenko     uint16_t, int32_t);
1262b3f6d66SOleksandr Tymoshenko static int uinput_setup_provider(struct uinput_cdev_state *,
1272b3f6d66SOleksandr Tymoshenko     struct uinput_user_dev *);
1282b3f6d66SOleksandr Tymoshenko static int uinput_cdev_create(void);
1292b3f6d66SOleksandr Tymoshenko static void uinput_notify(struct uinput_cdev_state *);
1302b3f6d66SOleksandr Tymoshenko 
1312b3f6d66SOleksandr Tymoshenko static void
1322b3f6d66SOleksandr Tymoshenko uinput_knllock(void *arg)
1332b3f6d66SOleksandr Tymoshenko {
1342b3f6d66SOleksandr Tymoshenko 	struct sx *sx = arg;
1352b3f6d66SOleksandr Tymoshenko 
1362b3f6d66SOleksandr Tymoshenko 	sx_xlock(sx);
1372b3f6d66SOleksandr Tymoshenko }
1382b3f6d66SOleksandr Tymoshenko 
1392b3f6d66SOleksandr Tymoshenko static void
1402b3f6d66SOleksandr Tymoshenko uinput_knlunlock(void *arg)
1412b3f6d66SOleksandr Tymoshenko {
1422b3f6d66SOleksandr Tymoshenko 	struct sx *sx = arg;
1432b3f6d66SOleksandr Tymoshenko 
1442b3f6d66SOleksandr Tymoshenko 	sx_unlock(sx);
1452b3f6d66SOleksandr Tymoshenko }
1462b3f6d66SOleksandr Tymoshenko 
1472b3f6d66SOleksandr Tymoshenko static void
1482b3f6d66SOleksandr Tymoshenko uinput_knl_assert_locked(void *arg)
1492b3f6d66SOleksandr Tymoshenko {
1502b3f6d66SOleksandr Tymoshenko 
1512b3f6d66SOleksandr Tymoshenko 	sx_assert((struct sx*)arg, SA_XLOCKED);
1522b3f6d66SOleksandr Tymoshenko }
1532b3f6d66SOleksandr Tymoshenko 
1542b3f6d66SOleksandr Tymoshenko static void
1552b3f6d66SOleksandr Tymoshenko uinput_knl_assert_unlocked(void *arg)
1562b3f6d66SOleksandr Tymoshenko {
1572b3f6d66SOleksandr Tymoshenko 
1582b3f6d66SOleksandr Tymoshenko 	sx_assert((struct sx*)arg, SA_UNLOCKED);
1592b3f6d66SOleksandr Tymoshenko }
1602b3f6d66SOleksandr Tymoshenko 
1612b3f6d66SOleksandr Tymoshenko static void
162*911aed94SVladimir Kondratyev uinput_ev_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
163*911aed94SVladimir Kondratyev     int32_t value)
1642b3f6d66SOleksandr Tymoshenko {
165*911aed94SVladimir Kondratyev 	struct uinput_cdev_state *state = evdev_get_softc(evdev);
1662b3f6d66SOleksandr Tymoshenko 
1672b3f6d66SOleksandr Tymoshenko 	if (type == EV_LED)
1682b3f6d66SOleksandr Tymoshenko 		evdev_push_event(evdev, type, code, value);
1692b3f6d66SOleksandr Tymoshenko 
1702b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
1712b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state == UINPUT_RUNNING) {
1722b3f6d66SOleksandr Tymoshenko 		uinput_enqueue_event(state, type, code, value);
1732b3f6d66SOleksandr Tymoshenko 		uinput_notify(state);
1742b3f6d66SOleksandr Tymoshenko 	}
1752b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
1762b3f6d66SOleksandr Tymoshenko }
1772b3f6d66SOleksandr Tymoshenko 
1782b3f6d66SOleksandr Tymoshenko static void
1792b3f6d66SOleksandr Tymoshenko uinput_enqueue_event(struct uinput_cdev_state *state, uint16_t type,
1802b3f6d66SOleksandr Tymoshenko     uint16_t code, int32_t value)
1812b3f6d66SOleksandr Tymoshenko {
1822b3f6d66SOleksandr Tymoshenko 	size_t head, tail;
1832b3f6d66SOleksandr Tymoshenko 
1842b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
1852b3f6d66SOleksandr Tymoshenko 
1862b3f6d66SOleksandr Tymoshenko 	head = state->ucs_buffer_head;
1872b3f6d66SOleksandr Tymoshenko 	tail = (state->ucs_buffer_tail + 1) % UINPUT_BUFFER_SIZE;
1882b3f6d66SOleksandr Tymoshenko 
1892b3f6d66SOleksandr Tymoshenko 	microtime(&state->ucs_buffer[tail].time);
1902b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].type = type;
1912b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].code = code;
1922b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].value = value;
1932b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer_tail = tail;
1942b3f6d66SOleksandr Tymoshenko 
1952b3f6d66SOleksandr Tymoshenko 	/* If queue is full remove oldest event */
1962b3f6d66SOleksandr Tymoshenko 	if (tail == head) {
1972b3f6d66SOleksandr Tymoshenko 		debugf(state, "state %p: buffer overflow", state);
1982b3f6d66SOleksandr Tymoshenko 
1992b3f6d66SOleksandr Tymoshenko 		head = (head + 1) % UINPUT_BUFFER_SIZE;
2002b3f6d66SOleksandr Tymoshenko 		state->ucs_buffer_head = head;
2012b3f6d66SOleksandr Tymoshenko 	}
2022b3f6d66SOleksandr Tymoshenko }
2032b3f6d66SOleksandr Tymoshenko 
2042b3f6d66SOleksandr Tymoshenko static int
2052b3f6d66SOleksandr Tymoshenko uinput_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
2062b3f6d66SOleksandr Tymoshenko {
2072b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2082b3f6d66SOleksandr Tymoshenko 
2092b3f6d66SOleksandr Tymoshenko 	state = malloc(sizeof(struct uinput_cdev_state), M_EVDEV,
2102b3f6d66SOleksandr Tymoshenko 	    M_WAITOK | M_ZERO);
2112b3f6d66SOleksandr Tymoshenko 	state->ucs_evdev = evdev_alloc();
2122b3f6d66SOleksandr Tymoshenko 
2132b3f6d66SOleksandr Tymoshenko 	sx_init(&state->ucs_lock, "uinput");
2142b3f6d66SOleksandr Tymoshenko 	knlist_init(&state->ucs_selp.si_note, &state->ucs_lock, uinput_knllock,
2152b3f6d66SOleksandr Tymoshenko 	    uinput_knlunlock, uinput_knl_assert_locked,
2162b3f6d66SOleksandr Tymoshenko 	    uinput_knl_assert_unlocked);
2172b3f6d66SOleksandr Tymoshenko 
2182b3f6d66SOleksandr Tymoshenko 	devfs_set_cdevpriv(state, uinput_dtor);
2192b3f6d66SOleksandr Tymoshenko 	return (0);
2202b3f6d66SOleksandr Tymoshenko }
2212b3f6d66SOleksandr Tymoshenko 
2222b3f6d66SOleksandr Tymoshenko static void
2232b3f6d66SOleksandr Tymoshenko uinput_dtor(void *data)
2242b3f6d66SOleksandr Tymoshenko {
2252b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state = (struct uinput_cdev_state *)data;
2262b3f6d66SOleksandr Tymoshenko 
2272b3f6d66SOleksandr Tymoshenko 	evdev_free(state->ucs_evdev);
2282b3f6d66SOleksandr Tymoshenko 
2292b3f6d66SOleksandr Tymoshenko 	knlist_clear(&state->ucs_selp.si_note, 0);
2302b3f6d66SOleksandr Tymoshenko 	seldrain(&state->ucs_selp);
2312b3f6d66SOleksandr Tymoshenko 	knlist_destroy(&state->ucs_selp.si_note);
2322b3f6d66SOleksandr Tymoshenko 	sx_destroy(&state->ucs_lock);
2332b3f6d66SOleksandr Tymoshenko 	free(data, M_EVDEV);
2342b3f6d66SOleksandr Tymoshenko }
2352b3f6d66SOleksandr Tymoshenko 
2362b3f6d66SOleksandr Tymoshenko static int
2372b3f6d66SOleksandr Tymoshenko uinput_read(struct cdev *dev, struct uio *uio, int ioflag)
2382b3f6d66SOleksandr Tymoshenko {
2392b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2402b3f6d66SOleksandr Tymoshenko 	struct input_event *event;
2412b3f6d66SOleksandr Tymoshenko 	int remaining, ret;
2422b3f6d66SOleksandr Tymoshenko 
2432b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
2442b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
2452b3f6d66SOleksandr Tymoshenko 		return (ret);
2462b3f6d66SOleksandr Tymoshenko 
2472b3f6d66SOleksandr Tymoshenko 	debugf(state, "read %zd bytes by thread %d", uio->uio_resid,
2482b3f6d66SOleksandr Tymoshenko 	    uio->uio_td->td_tid);
2492b3f6d66SOleksandr Tymoshenko 
2502b3f6d66SOleksandr Tymoshenko 	/* Zero-sized reads are allowed for error checking */
2512b3f6d66SOleksandr Tymoshenko 	if (uio->uio_resid != 0 && uio->uio_resid < sizeof(struct input_event))
2522b3f6d66SOleksandr Tymoshenko 		return (EINVAL);
2532b3f6d66SOleksandr Tymoshenko 
2542b3f6d66SOleksandr Tymoshenko 	remaining = uio->uio_resid / sizeof(struct input_event);
2552b3f6d66SOleksandr Tymoshenko 
2562b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
2572b3f6d66SOleksandr Tymoshenko 
2582b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state != UINPUT_RUNNING)
2592b3f6d66SOleksandr Tymoshenko 		ret = EINVAL;
2602b3f6d66SOleksandr Tymoshenko 
2612b3f6d66SOleksandr Tymoshenko 	if (ret == 0 && UINPUT_EMPTYQ(state)) {
2622b3f6d66SOleksandr Tymoshenko 		if (ioflag & O_NONBLOCK)
2632b3f6d66SOleksandr Tymoshenko 			ret = EWOULDBLOCK;
2642b3f6d66SOleksandr Tymoshenko 		else {
2652b3f6d66SOleksandr Tymoshenko 			if (remaining != 0) {
2662b3f6d66SOleksandr Tymoshenko 				state->ucs_blocked = true;
2672b3f6d66SOleksandr Tymoshenko 				ret = sx_sleep(state, &state->ucs_lock,
2682b3f6d66SOleksandr Tymoshenko 				    PCATCH, "uiread", 0);
2692b3f6d66SOleksandr Tymoshenko 			}
2702b3f6d66SOleksandr Tymoshenko 		}
2712b3f6d66SOleksandr Tymoshenko 	}
2722b3f6d66SOleksandr Tymoshenko 
2732b3f6d66SOleksandr Tymoshenko 	while (ret == 0 && !UINPUT_EMPTYQ(state) && remaining > 0) {
2742b3f6d66SOleksandr Tymoshenko 		event = &state->ucs_buffer[state->ucs_buffer_head];
2752b3f6d66SOleksandr Tymoshenko 		state->ucs_buffer_head = (state->ucs_buffer_head + 1) %
2762b3f6d66SOleksandr Tymoshenko 		    UINPUT_BUFFER_SIZE;
2772b3f6d66SOleksandr Tymoshenko 		remaining--;
2782b3f6d66SOleksandr Tymoshenko 		ret = uiomove(event, sizeof(struct input_event), uio);
2792b3f6d66SOleksandr Tymoshenko 	}
2802b3f6d66SOleksandr Tymoshenko 
2812b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
2822b3f6d66SOleksandr Tymoshenko 
2832b3f6d66SOleksandr Tymoshenko 	return (ret);
2842b3f6d66SOleksandr Tymoshenko }
2852b3f6d66SOleksandr Tymoshenko 
2862b3f6d66SOleksandr Tymoshenko static int
2872b3f6d66SOleksandr Tymoshenko uinput_write(struct cdev *dev, struct uio *uio, int ioflag)
2882b3f6d66SOleksandr Tymoshenko {
2892b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2902b3f6d66SOleksandr Tymoshenko 	struct uinput_user_dev userdev;
2912b3f6d66SOleksandr Tymoshenko 	struct input_event event;
2922b3f6d66SOleksandr Tymoshenko 	int ret = 0;
2932b3f6d66SOleksandr Tymoshenko 
2942b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
2952b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
2962b3f6d66SOleksandr Tymoshenko 		return (ret);
2972b3f6d66SOleksandr Tymoshenko 
2982b3f6d66SOleksandr Tymoshenko 	debugf(state, "write %zd bytes by thread %d", uio->uio_resid,
2992b3f6d66SOleksandr Tymoshenko 	    uio->uio_td->td_tid);
3002b3f6d66SOleksandr Tymoshenko 
3012b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
3022b3f6d66SOleksandr Tymoshenko 
3032b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state != UINPUT_RUNNING) {
3042b3f6d66SOleksandr Tymoshenko 		/* Process written struct uinput_user_dev */
3052b3f6d66SOleksandr Tymoshenko 		if (uio->uio_resid != sizeof(struct uinput_user_dev)) {
3062b3f6d66SOleksandr Tymoshenko 			debugf(state, "write size not multiple of "
3072b3f6d66SOleksandr Tymoshenko 			    "struct uinput_user_dev size");
3082b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
3092b3f6d66SOleksandr Tymoshenko 		} else {
3102b3f6d66SOleksandr Tymoshenko 			ret = uiomove(&userdev, sizeof(struct uinput_user_dev),
3112b3f6d66SOleksandr Tymoshenko 			    uio);
3122b3f6d66SOleksandr Tymoshenko 			if (ret == 0)
3132b3f6d66SOleksandr Tymoshenko 				uinput_setup_provider(state, &userdev);
3142b3f6d66SOleksandr Tymoshenko 		}
3152b3f6d66SOleksandr Tymoshenko 	} else {
3162b3f6d66SOleksandr Tymoshenko 		/* Process written event */
3172b3f6d66SOleksandr Tymoshenko 		if (uio->uio_resid % sizeof(struct input_event) != 0) {
3182b3f6d66SOleksandr Tymoshenko 			debugf(state, "write size not multiple of "
3192b3f6d66SOleksandr Tymoshenko 			    "struct input_event size");
3202b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
3212b3f6d66SOleksandr Tymoshenko 		}
3222b3f6d66SOleksandr Tymoshenko 
3232b3f6d66SOleksandr Tymoshenko 		while (ret == 0 && uio->uio_resid > 0) {
3242b3f6d66SOleksandr Tymoshenko 			uiomove(&event, sizeof(struct input_event), uio);
3252b3f6d66SOleksandr Tymoshenko 			ret = evdev_push_event(state->ucs_evdev, event.type,
3262b3f6d66SOleksandr Tymoshenko 			    event.code, event.value);
3272b3f6d66SOleksandr Tymoshenko 		}
3282b3f6d66SOleksandr Tymoshenko 	}
3292b3f6d66SOleksandr Tymoshenko 
3302b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
3312b3f6d66SOleksandr Tymoshenko 
3322b3f6d66SOleksandr Tymoshenko 	return (ret);
3332b3f6d66SOleksandr Tymoshenko }
3342b3f6d66SOleksandr Tymoshenko 
3352b3f6d66SOleksandr Tymoshenko static int
3362b3f6d66SOleksandr Tymoshenko uinput_setup_dev(struct uinput_cdev_state *state, struct input_id *id,
3372b3f6d66SOleksandr Tymoshenko     char *name, uint32_t ff_effects_max)
3382b3f6d66SOleksandr Tymoshenko {
3392b3f6d66SOleksandr Tymoshenko 
3402b3f6d66SOleksandr Tymoshenko 	if (name[0] == 0)
3412b3f6d66SOleksandr Tymoshenko 		return (EINVAL);
3422b3f6d66SOleksandr Tymoshenko 
3432b3f6d66SOleksandr Tymoshenko 	evdev_set_name(state->ucs_evdev, name);
3442b3f6d66SOleksandr Tymoshenko 	evdev_set_id(state->ucs_evdev, id->bustype, id->vendor, id->product,
3452b3f6d66SOleksandr Tymoshenko 	    id->version);
3462b3f6d66SOleksandr Tymoshenko 	state->ucs_state = UINPUT_CONFIGURED;
3472b3f6d66SOleksandr Tymoshenko 
3482b3f6d66SOleksandr Tymoshenko 	return (0);
3492b3f6d66SOleksandr Tymoshenko }
3502b3f6d66SOleksandr Tymoshenko 
3512b3f6d66SOleksandr Tymoshenko static int
3522b3f6d66SOleksandr Tymoshenko uinput_setup_provider(struct uinput_cdev_state *state,
3532b3f6d66SOleksandr Tymoshenko     struct uinput_user_dev *udev)
3542b3f6d66SOleksandr Tymoshenko {
3552b3f6d66SOleksandr Tymoshenko 	struct input_absinfo absinfo;
3562b3f6d66SOleksandr Tymoshenko 	int i, ret;
3572b3f6d66SOleksandr Tymoshenko 
3582b3f6d66SOleksandr Tymoshenko 	debugf(state, "setup_provider called, udev=%p", udev);
3592b3f6d66SOleksandr Tymoshenko 
3602b3f6d66SOleksandr Tymoshenko 	ret = uinput_setup_dev(state, &udev->id, udev->name,
3612b3f6d66SOleksandr Tymoshenko 	    udev->ff_effects_max);
3622b3f6d66SOleksandr Tymoshenko 	if (ret)
3632b3f6d66SOleksandr Tymoshenko 		return (ret);
3642b3f6d66SOleksandr Tymoshenko 
3652b3f6d66SOleksandr Tymoshenko 	bzero(&absinfo, sizeof(struct input_absinfo));
3662b3f6d66SOleksandr Tymoshenko 	for (i = 0; i < ABS_CNT; i++) {
3672b3f6d66SOleksandr Tymoshenko 		if (!bit_test(state->ucs_evdev->ev_abs_flags, i))
3682b3f6d66SOleksandr Tymoshenko 			continue;
3692b3f6d66SOleksandr Tymoshenko 
3702b3f6d66SOleksandr Tymoshenko 		absinfo.minimum = udev->absmin[i];
3712b3f6d66SOleksandr Tymoshenko 		absinfo.maximum = udev->absmax[i];
3722b3f6d66SOleksandr Tymoshenko 		absinfo.fuzz = udev->absfuzz[i];
3732b3f6d66SOleksandr Tymoshenko 		absinfo.flat = udev->absflat[i];
3742b3f6d66SOleksandr Tymoshenko 		evdev_set_absinfo(state->ucs_evdev, i, &absinfo);
3752b3f6d66SOleksandr Tymoshenko 	}
3762b3f6d66SOleksandr Tymoshenko 
3772b3f6d66SOleksandr Tymoshenko 	return (0);
3782b3f6d66SOleksandr Tymoshenko }
3792b3f6d66SOleksandr Tymoshenko 
3802b3f6d66SOleksandr Tymoshenko static int
3812b3f6d66SOleksandr Tymoshenko uinput_poll(struct cdev *dev, int events, struct thread *td)
3822b3f6d66SOleksandr Tymoshenko {
3832b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
3842b3f6d66SOleksandr Tymoshenko 	int revents = 0;
3852b3f6d66SOleksandr Tymoshenko 
3862b3f6d66SOleksandr Tymoshenko 	if (devfs_get_cdevpriv((void **)&state) != 0)
3872b3f6d66SOleksandr Tymoshenko 		return (POLLNVAL);
3882b3f6d66SOleksandr Tymoshenko 
3892b3f6d66SOleksandr Tymoshenko 	debugf(state, "poll by thread %d", td->td_tid);
3902b3f6d66SOleksandr Tymoshenko 
3912b3f6d66SOleksandr Tymoshenko 	/* Always allow write */
3922b3f6d66SOleksandr Tymoshenko 	if (events & (POLLOUT | POLLWRNORM))
3932b3f6d66SOleksandr Tymoshenko 		revents |= (events & (POLLOUT | POLLWRNORM));
3942b3f6d66SOleksandr Tymoshenko 
3952b3f6d66SOleksandr Tymoshenko 	if (events & (POLLIN | POLLRDNORM)) {
3962b3f6d66SOleksandr Tymoshenko 		UINPUT_LOCK(state);
3972b3f6d66SOleksandr Tymoshenko 		if (!UINPUT_EMPTYQ(state))
3982b3f6d66SOleksandr Tymoshenko 			revents = events & (POLLIN | POLLRDNORM);
3992b3f6d66SOleksandr Tymoshenko 		else {
4002b3f6d66SOleksandr Tymoshenko 			state->ucs_selected = true;
4012b3f6d66SOleksandr Tymoshenko 			selrecord(td, &state->ucs_selp);
4022b3f6d66SOleksandr Tymoshenko 		}
4032b3f6d66SOleksandr Tymoshenko 		UINPUT_UNLOCK(state);
4042b3f6d66SOleksandr Tymoshenko 	}
4052b3f6d66SOleksandr Tymoshenko 
4062b3f6d66SOleksandr Tymoshenko 	return (revents);
4072b3f6d66SOleksandr Tymoshenko }
4082b3f6d66SOleksandr Tymoshenko 
4092b3f6d66SOleksandr Tymoshenko static int
4102b3f6d66SOleksandr Tymoshenko uinput_kqfilter(struct cdev *dev, struct knote *kn)
4112b3f6d66SOleksandr Tymoshenko {
4122b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4132b3f6d66SOleksandr Tymoshenko 	int ret;
4142b3f6d66SOleksandr Tymoshenko 
4152b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
4162b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
4172b3f6d66SOleksandr Tymoshenko 		return (ret);
4182b3f6d66SOleksandr Tymoshenko 
4192b3f6d66SOleksandr Tymoshenko 	switch(kn->kn_filter) {
4202b3f6d66SOleksandr Tymoshenko 	case EVFILT_READ:
4212b3f6d66SOleksandr Tymoshenko 		kn->kn_fop = &uinput_filterops;
4222b3f6d66SOleksandr Tymoshenko 		break;
4232b3f6d66SOleksandr Tymoshenko 	default:
4242b3f6d66SOleksandr Tymoshenko 		return(EINVAL);
4252b3f6d66SOleksandr Tymoshenko 	}
4262b3f6d66SOleksandr Tymoshenko 	kn->kn_hook = (caddr_t)state;
4272b3f6d66SOleksandr Tymoshenko 
4282b3f6d66SOleksandr Tymoshenko 	knlist_add(&state->ucs_selp.si_note, kn, 0);
4292b3f6d66SOleksandr Tymoshenko 	return (0);
4302b3f6d66SOleksandr Tymoshenko }
4312b3f6d66SOleksandr Tymoshenko 
4322b3f6d66SOleksandr Tymoshenko static int
4332b3f6d66SOleksandr Tymoshenko uinput_kqread(struct knote *kn, long hint)
4342b3f6d66SOleksandr Tymoshenko {
4352b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4362b3f6d66SOleksandr Tymoshenko 	int ret;
4372b3f6d66SOleksandr Tymoshenko 
4382b3f6d66SOleksandr Tymoshenko 	state = (struct uinput_cdev_state *)kn->kn_hook;
4392b3f6d66SOleksandr Tymoshenko 
4402b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4412b3f6d66SOleksandr Tymoshenko 
4422b3f6d66SOleksandr Tymoshenko 	ret = !UINPUT_EMPTYQ(state);
4432b3f6d66SOleksandr Tymoshenko 	return (ret);
4442b3f6d66SOleksandr Tymoshenko }
4452b3f6d66SOleksandr Tymoshenko 
4462b3f6d66SOleksandr Tymoshenko static void
4472b3f6d66SOleksandr Tymoshenko uinput_kqdetach(struct knote *kn)
4482b3f6d66SOleksandr Tymoshenko {
4492b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4502b3f6d66SOleksandr Tymoshenko 
4512b3f6d66SOleksandr Tymoshenko 	state = (struct uinput_cdev_state *)kn->kn_hook;
4522b3f6d66SOleksandr Tymoshenko 	knlist_remove(&state->ucs_selp.si_note, kn, 0);
4532b3f6d66SOleksandr Tymoshenko }
4542b3f6d66SOleksandr Tymoshenko 
4552b3f6d66SOleksandr Tymoshenko static void
4562b3f6d66SOleksandr Tymoshenko uinput_notify(struct uinput_cdev_state *state)
4572b3f6d66SOleksandr Tymoshenko {
4582b3f6d66SOleksandr Tymoshenko 
4592b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4602b3f6d66SOleksandr Tymoshenko 
4612b3f6d66SOleksandr Tymoshenko 	if (state->ucs_blocked) {
4622b3f6d66SOleksandr Tymoshenko 		state->ucs_blocked = false;
4632b3f6d66SOleksandr Tymoshenko 		wakeup(state);
4642b3f6d66SOleksandr Tymoshenko 	}
4652b3f6d66SOleksandr Tymoshenko 	if (state->ucs_selected) {
4662b3f6d66SOleksandr Tymoshenko 		state->ucs_selected = false;
4672b3f6d66SOleksandr Tymoshenko 		selwakeup(&state->ucs_selp);
4682b3f6d66SOleksandr Tymoshenko 	}
4692b3f6d66SOleksandr Tymoshenko 	KNOTE_LOCKED(&state->ucs_selp.si_note, 0);
4702b3f6d66SOleksandr Tymoshenko }
4712b3f6d66SOleksandr Tymoshenko 
4722b3f6d66SOleksandr Tymoshenko static int
4732b3f6d66SOleksandr Tymoshenko uinput_ioctl_sub(struct uinput_cdev_state *state, u_long cmd, caddr_t data)
4742b3f6d66SOleksandr Tymoshenko {
4752b3f6d66SOleksandr Tymoshenko 	struct uinput_setup *us;
4762b3f6d66SOleksandr Tymoshenko 	struct uinput_abs_setup *uabs;
4772b3f6d66SOleksandr Tymoshenko 	int ret, len, intdata;
4782b3f6d66SOleksandr Tymoshenko 	char buf[NAMELEN];
4792b3f6d66SOleksandr Tymoshenko 
4802b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4812b3f6d66SOleksandr Tymoshenko 
4822b3f6d66SOleksandr Tymoshenko 	len = IOCPARM_LEN(cmd);
4832b3f6d66SOleksandr Tymoshenko 	if ((cmd & IOC_DIRMASK) == IOC_VOID && len == sizeof(int))
4842b3f6d66SOleksandr Tymoshenko 		intdata = *(int *)data;
4852b3f6d66SOleksandr Tymoshenko 
4862b3f6d66SOleksandr Tymoshenko 	switch (IOCBASECMD(cmd)) {
4872b3f6d66SOleksandr Tymoshenko 	case UI_GET_SYSNAME(0):
4882b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_RUNNING)
4892b3f6d66SOleksandr Tymoshenko 			return (ENOENT);
4902b3f6d66SOleksandr Tymoshenko 		if (len == 0)
4912b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
4922b3f6d66SOleksandr Tymoshenko 		snprintf(data, len, "event%d", state->ucs_evdev->ev_unit);
4932b3f6d66SOleksandr Tymoshenko 		return (0);
4942b3f6d66SOleksandr Tymoshenko 	}
4952b3f6d66SOleksandr Tymoshenko 
4962b3f6d66SOleksandr Tymoshenko 	switch (cmd) {
4972b3f6d66SOleksandr Tymoshenko 	case UI_DEV_CREATE:
4982b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_CONFIGURED)
4992b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5002b3f6d66SOleksandr Tymoshenko 
5012b3f6d66SOleksandr Tymoshenko 		evdev_set_methods(state->ucs_evdev, state, &uinput_ev_methods);
5022b3f6d66SOleksandr Tymoshenko 		evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_SOFTREPEAT);
503670aa764SOleksandr Tymoshenko 		ret = evdev_register(state->ucs_evdev);
504670aa764SOleksandr Tymoshenko 		if (ret == 0)
5052b3f6d66SOleksandr Tymoshenko 			state->ucs_state = UINPUT_RUNNING;
506670aa764SOleksandr Tymoshenko 		return (ret);
5072b3f6d66SOleksandr Tymoshenko 
5082b3f6d66SOleksandr Tymoshenko 	case UI_DEV_DESTROY:
5092b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_RUNNING)
5102b3f6d66SOleksandr Tymoshenko 			return (0);
5112b3f6d66SOleksandr Tymoshenko 
5122b3f6d66SOleksandr Tymoshenko 		evdev_unregister(state->ucs_evdev);
5132b3f6d66SOleksandr Tymoshenko 		bzero(state->ucs_evdev, sizeof(struct evdev_dev));
5142b3f6d66SOleksandr Tymoshenko 		state->ucs_state = UINPUT_NEW;
5152b3f6d66SOleksandr Tymoshenko 		return (0);
5162b3f6d66SOleksandr Tymoshenko 
5172b3f6d66SOleksandr Tymoshenko 	case UI_DEV_SETUP:
5182b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5192b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5202b3f6d66SOleksandr Tymoshenko 
5212b3f6d66SOleksandr Tymoshenko 		us = (struct uinput_setup *)data;
5222b3f6d66SOleksandr Tymoshenko 		return (uinput_setup_dev(state, &us->id, us->name,
5232b3f6d66SOleksandr Tymoshenko 		    us->ff_effects_max));
5242b3f6d66SOleksandr Tymoshenko 
5252b3f6d66SOleksandr Tymoshenko 	case UI_ABS_SETUP:
5262b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5272b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5282b3f6d66SOleksandr Tymoshenko 
5292b3f6d66SOleksandr Tymoshenko 		uabs = (struct uinput_abs_setup *)data;
530886f6623SOleksandr Tymoshenko 		if (uabs->code > ABS_MAX)
5312b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5322b3f6d66SOleksandr Tymoshenko 
5332b3f6d66SOleksandr Tymoshenko 		evdev_support_abs(state->ucs_evdev, uabs->code,
5342b3f6d66SOleksandr Tymoshenko 		    uabs->absinfo.value, uabs->absinfo.minimum,
5352b3f6d66SOleksandr Tymoshenko 		    uabs->absinfo.maximum, uabs->absinfo.fuzz,
5362b3f6d66SOleksandr Tymoshenko 		    uabs->absinfo.flat, uabs->absinfo.resolution);
5372b3f6d66SOleksandr Tymoshenko 		return (0);
5382b3f6d66SOleksandr Tymoshenko 
5392b3f6d66SOleksandr Tymoshenko 	case UI_SET_EVBIT:
5402b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5412b3f6d66SOleksandr Tymoshenko 		    intdata > EV_MAX || intdata < 0)
5422b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5432b3f6d66SOleksandr Tymoshenko 		evdev_support_event(state->ucs_evdev, intdata);
5442b3f6d66SOleksandr Tymoshenko 		return (0);
5452b3f6d66SOleksandr Tymoshenko 
5462b3f6d66SOleksandr Tymoshenko 	case UI_SET_KEYBIT:
5472b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5482b3f6d66SOleksandr Tymoshenko 		    intdata > KEY_MAX || intdata < 0)
5492b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5502b3f6d66SOleksandr Tymoshenko 		evdev_support_key(state->ucs_evdev, intdata);
5512b3f6d66SOleksandr Tymoshenko 		return (0);
5522b3f6d66SOleksandr Tymoshenko 
5532b3f6d66SOleksandr Tymoshenko 	case UI_SET_RELBIT:
5542b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5552b3f6d66SOleksandr Tymoshenko 		    intdata > REL_MAX || intdata < 0)
5562b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5572b3f6d66SOleksandr Tymoshenko 		evdev_support_rel(state->ucs_evdev, intdata);
5582b3f6d66SOleksandr Tymoshenko 		return (0);
5592b3f6d66SOleksandr Tymoshenko 
5602b3f6d66SOleksandr Tymoshenko 	case UI_SET_ABSBIT:
5612b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5622b3f6d66SOleksandr Tymoshenko 		    intdata > ABS_MAX || intdata < 0)
5632b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5642b3f6d66SOleksandr Tymoshenko 		evdev_set_abs_bit(state->ucs_evdev, intdata);
5652b3f6d66SOleksandr Tymoshenko 		return (0);
5662b3f6d66SOleksandr Tymoshenko 
5672b3f6d66SOleksandr Tymoshenko 	case UI_SET_MSCBIT:
5682b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5692b3f6d66SOleksandr Tymoshenko 		    intdata > MSC_MAX || intdata < 0)
5702b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5712b3f6d66SOleksandr Tymoshenko 		evdev_support_msc(state->ucs_evdev, intdata);
5722b3f6d66SOleksandr Tymoshenko 		return (0);
5732b3f6d66SOleksandr Tymoshenko 
5742b3f6d66SOleksandr Tymoshenko 	case UI_SET_LEDBIT:
5752b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5762b3f6d66SOleksandr Tymoshenko 		    intdata > LED_MAX || intdata < 0)
5772b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5782b3f6d66SOleksandr Tymoshenko 		evdev_support_led(state->ucs_evdev, intdata);
5792b3f6d66SOleksandr Tymoshenko 		return (0);
5802b3f6d66SOleksandr Tymoshenko 
5812b3f6d66SOleksandr Tymoshenko 	case UI_SET_SNDBIT:
5822b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5832b3f6d66SOleksandr Tymoshenko 		    intdata > SND_MAX || intdata < 0)
5842b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5852b3f6d66SOleksandr Tymoshenko 		evdev_support_snd(state->ucs_evdev, intdata);
5862b3f6d66SOleksandr Tymoshenko 		return (0);
5872b3f6d66SOleksandr Tymoshenko 
5882b3f6d66SOleksandr Tymoshenko 	case UI_SET_FFBIT:
5892b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5902b3f6d66SOleksandr Tymoshenko 		    intdata > FF_MAX || intdata < 0)
5912b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5922b3f6d66SOleksandr Tymoshenko 		/* Fake unsupported ioctl */
5932b3f6d66SOleksandr Tymoshenko 		return (0);
5942b3f6d66SOleksandr Tymoshenko 
5952b3f6d66SOleksandr Tymoshenko 	case UI_SET_PHYS:
5962b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5972b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5982b3f6d66SOleksandr Tymoshenko 		ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
5992b3f6d66SOleksandr Tymoshenko 		/* Linux returns EINVAL when string does not fit the buffer */
6002b3f6d66SOleksandr Tymoshenko 		if (ret == ENAMETOOLONG)
6012b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
6022b3f6d66SOleksandr Tymoshenko 		if (ret != 0)
6032b3f6d66SOleksandr Tymoshenko 			return (ret);
6042b3f6d66SOleksandr Tymoshenko 		evdev_set_phys(state->ucs_evdev, buf);
6052b3f6d66SOleksandr Tymoshenko 		return (0);
6062b3f6d66SOleksandr Tymoshenko 
6074b58fa12SVladimir Kondratyev 	case UI_SET_BSDUNIQ:
6084b58fa12SVladimir Kondratyev 		if (state->ucs_state == UINPUT_RUNNING)
6094b58fa12SVladimir Kondratyev 			return (EINVAL);
6104b58fa12SVladimir Kondratyev 		ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
6114b58fa12SVladimir Kondratyev 		if (ret != 0)
6124b58fa12SVladimir Kondratyev 			return (ret);
6134b58fa12SVladimir Kondratyev 		evdev_set_serial(state->ucs_evdev, buf);
6144b58fa12SVladimir Kondratyev 		return (0);
6154b58fa12SVladimir Kondratyev 
6162b3f6d66SOleksandr Tymoshenko 	case UI_SET_SWBIT:
6172b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
6182b3f6d66SOleksandr Tymoshenko 		    intdata > SW_MAX || intdata < 0)
6192b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6202b3f6d66SOleksandr Tymoshenko 		evdev_support_sw(state->ucs_evdev, intdata);
6212b3f6d66SOleksandr Tymoshenko 		return (0);
6222b3f6d66SOleksandr Tymoshenko 
6232b3f6d66SOleksandr Tymoshenko 	case UI_SET_PROPBIT:
6242b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
6252b3f6d66SOleksandr Tymoshenko 		    intdata > INPUT_PROP_MAX || intdata < 0)
6262b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6272b3f6d66SOleksandr Tymoshenko 		evdev_support_prop(state->ucs_evdev, intdata);
6282b3f6d66SOleksandr Tymoshenko 		return (0);
6292b3f6d66SOleksandr Tymoshenko 
6302b3f6d66SOleksandr Tymoshenko 	case UI_BEGIN_FF_UPLOAD:
6312b3f6d66SOleksandr Tymoshenko 	case UI_END_FF_UPLOAD:
6322b3f6d66SOleksandr Tymoshenko 	case UI_BEGIN_FF_ERASE:
6332b3f6d66SOleksandr Tymoshenko 	case UI_END_FF_ERASE:
6342b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
6352b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6362b3f6d66SOleksandr Tymoshenko 		/* Fake unsupported ioctl */
6372b3f6d66SOleksandr Tymoshenko 		return (0);
6382b3f6d66SOleksandr Tymoshenko 
6392b3f6d66SOleksandr Tymoshenko 	case UI_GET_VERSION:
6402b3f6d66SOleksandr Tymoshenko 		*(unsigned int *)data = UINPUT_VERSION;
6412b3f6d66SOleksandr Tymoshenko 		return (0);
6422b3f6d66SOleksandr Tymoshenko 	}
6432b3f6d66SOleksandr Tymoshenko 
6442b3f6d66SOleksandr Tymoshenko 	return (EINVAL);
6452b3f6d66SOleksandr Tymoshenko }
6462b3f6d66SOleksandr Tymoshenko 
6472b3f6d66SOleksandr Tymoshenko static int
6482b3f6d66SOleksandr Tymoshenko uinput_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
6492b3f6d66SOleksandr Tymoshenko     struct thread *td)
6502b3f6d66SOleksandr Tymoshenko {
6512b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
6522b3f6d66SOleksandr Tymoshenko 	int ret;
6532b3f6d66SOleksandr Tymoshenko 
6542b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
6552b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
6562b3f6d66SOleksandr Tymoshenko 		return (ret);
6572b3f6d66SOleksandr Tymoshenko 
6582b3f6d66SOleksandr Tymoshenko 	debugf(state, "ioctl called: cmd=0x%08lx, data=%p", cmd, data);
6592b3f6d66SOleksandr Tymoshenko 
6602b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
6612b3f6d66SOleksandr Tymoshenko 	ret = uinput_ioctl_sub(state, cmd, data);
6622b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
6632b3f6d66SOleksandr Tymoshenko 
6642b3f6d66SOleksandr Tymoshenko 	return (ret);
6652b3f6d66SOleksandr Tymoshenko }
6662b3f6d66SOleksandr Tymoshenko 
6672b3f6d66SOleksandr Tymoshenko static int
6682b3f6d66SOleksandr Tymoshenko uinput_cdev_create(void)
6692b3f6d66SOleksandr Tymoshenko {
6702b3f6d66SOleksandr Tymoshenko 	struct make_dev_args mda;
6712b3f6d66SOleksandr Tymoshenko 	int ret;
6722b3f6d66SOleksandr Tymoshenko 
6732b3f6d66SOleksandr Tymoshenko 	make_dev_args_init(&mda);
6742b3f6d66SOleksandr Tymoshenko 	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
6752b3f6d66SOleksandr Tymoshenko 	mda.mda_devsw = &uinput_cdevsw;
6762b3f6d66SOleksandr Tymoshenko 	mda.mda_uid = UID_ROOT;
6772b3f6d66SOleksandr Tymoshenko 	mda.mda_gid = GID_WHEEL;
6782b3f6d66SOleksandr Tymoshenko 	mda.mda_mode = 0600;
6792b3f6d66SOleksandr Tymoshenko 
6802b3f6d66SOleksandr Tymoshenko 	ret = make_dev_s(&mda, &uinput_cdev, "uinput");
6812b3f6d66SOleksandr Tymoshenko 
6822b3f6d66SOleksandr Tymoshenko 	return (ret);
6832b3f6d66SOleksandr Tymoshenko }
6842b3f6d66SOleksandr Tymoshenko 
6852b3f6d66SOleksandr Tymoshenko static int
6862b3f6d66SOleksandr Tymoshenko uinput_cdev_destroy(void)
6872b3f6d66SOleksandr Tymoshenko {
6882b3f6d66SOleksandr Tymoshenko 
6892b3f6d66SOleksandr Tymoshenko 	destroy_dev(uinput_cdev);
6902b3f6d66SOleksandr Tymoshenko 
6912b3f6d66SOleksandr Tymoshenko 	return (0);
6922b3f6d66SOleksandr Tymoshenko }
6932b3f6d66SOleksandr Tymoshenko 
6942b3f6d66SOleksandr Tymoshenko static int
6952b3f6d66SOleksandr Tymoshenko uinput_modevent(module_t mod __unused, int cmd, void *data)
6962b3f6d66SOleksandr Tymoshenko {
6972b3f6d66SOleksandr Tymoshenko 	int ret = 0;
6982b3f6d66SOleksandr Tymoshenko 
6992b3f6d66SOleksandr Tymoshenko 	switch (cmd) {
7002b3f6d66SOleksandr Tymoshenko 	case MOD_LOAD:
7012b3f6d66SOleksandr Tymoshenko 		ret = uinput_cdev_create();
7022b3f6d66SOleksandr Tymoshenko 		break;
7032b3f6d66SOleksandr Tymoshenko 
7042b3f6d66SOleksandr Tymoshenko 	case MOD_UNLOAD:
7052b3f6d66SOleksandr Tymoshenko 		ret = uinput_cdev_destroy();
7062b3f6d66SOleksandr Tymoshenko 		break;
7072b3f6d66SOleksandr Tymoshenko 
7082b3f6d66SOleksandr Tymoshenko 	case MOD_SHUTDOWN:
7092b3f6d66SOleksandr Tymoshenko 		break;
7102b3f6d66SOleksandr Tymoshenko 
7112b3f6d66SOleksandr Tymoshenko 	default:
7122b3f6d66SOleksandr Tymoshenko 		ret = EINVAL;
7132b3f6d66SOleksandr Tymoshenko 		break;
7142b3f6d66SOleksandr Tymoshenko 	}
7152b3f6d66SOleksandr Tymoshenko 
7162b3f6d66SOleksandr Tymoshenko 	return (ret);
7172b3f6d66SOleksandr Tymoshenko }
7182b3f6d66SOleksandr Tymoshenko 
7192b3f6d66SOleksandr Tymoshenko DEV_MODULE(uinput, uinput_modevent, NULL);
720a6b15a34SOleksandr Tymoshenko MODULE_VERSION(uinput, 1);
721a6b15a34SOleksandr Tymoshenko MODULE_DEPEND(uinput, evdev, 1, 1, 1);
722