xref: /freebsd/sys/dev/evdev/uinput.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
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 
282b3f6d66SOleksandr Tymoshenko #include "opt_evdev.h"
292b3f6d66SOleksandr Tymoshenko 
302b3f6d66SOleksandr Tymoshenko #include <sys/param.h>
31ea2e26b1SVladimir Kondratyev #include <sys/conf.h>
322b3f6d66SOleksandr Tymoshenko #include <sys/fcntl.h>
332b3f6d66SOleksandr Tymoshenko #include <sys/kernel.h>
342b3f6d66SOleksandr Tymoshenko #include <sys/lock.h>
35ea2e26b1SVladimir Kondratyev #include <sys/malloc.h>
36ea2e26b1SVladimir Kondratyev #include <sys/module.h>
37ea2e26b1SVladimir Kondratyev #include <sys/poll.h>
38ea2e26b1SVladimir Kondratyev #include <sys/proc.h>
39ea2e26b1SVladimir Kondratyev #include <sys/selinfo.h>
40ea2e26b1SVladimir Kondratyev #include <sys/systm.h>
412b3f6d66SOleksandr Tymoshenko #include <sys/sx.h>
42ea2e26b1SVladimir Kondratyev #include <sys/uio.h>
432b3f6d66SOleksandr Tymoshenko 
442b3f6d66SOleksandr Tymoshenko #include <dev/evdev/evdev.h>
452b3f6d66SOleksandr Tymoshenko #include <dev/evdev/evdev_private.h>
46ea2e26b1SVladimir Kondratyev #include <dev/evdev/input.h>
47ea2e26b1SVladimir Kondratyev #include <dev/evdev/uinput.h>
482b3f6d66SOleksandr Tymoshenko 
492b3f6d66SOleksandr Tymoshenko #ifdef UINPUT_DEBUG
50e0cfa1bcSOleksandr Tymoshenko #define	debugf(state, fmt, args...)	printf("uinput: " fmt "\n", ##args)
512b3f6d66SOleksandr Tymoshenko #else
522b3f6d66SOleksandr Tymoshenko #define	debugf(state, fmt, args...)
532b3f6d66SOleksandr Tymoshenko #endif
542b3f6d66SOleksandr Tymoshenko 
552b3f6d66SOleksandr Tymoshenko #define	UINPUT_BUFFER_SIZE	16
562b3f6d66SOleksandr Tymoshenko 
572b3f6d66SOleksandr Tymoshenko #define	UINPUT_LOCK(state)		sx_xlock(&(state)->ucs_lock)
582b3f6d66SOleksandr Tymoshenko #define	UINPUT_UNLOCK(state)		sx_unlock(&(state)->ucs_lock)
592b3f6d66SOleksandr Tymoshenko #define	UINPUT_LOCK_ASSERT(state)	sx_assert(&(state)->ucs_lock, SA_LOCKED)
602b3f6d66SOleksandr Tymoshenko #define UINPUT_EMPTYQ(state) \
612b3f6d66SOleksandr Tymoshenko     ((state)->ucs_buffer_head == (state)->ucs_buffer_tail)
622b3f6d66SOleksandr Tymoshenko 
632b3f6d66SOleksandr Tymoshenko enum uinput_state
642b3f6d66SOleksandr Tymoshenko {
652b3f6d66SOleksandr Tymoshenko 	UINPUT_NEW = 0,
662b3f6d66SOleksandr Tymoshenko 	UINPUT_CONFIGURED,
672b3f6d66SOleksandr Tymoshenko 	UINPUT_RUNNING
682b3f6d66SOleksandr Tymoshenko };
692b3f6d66SOleksandr Tymoshenko 
702b3f6d66SOleksandr Tymoshenko static evdev_event_t	uinput_ev_event;
712b3f6d66SOleksandr Tymoshenko 
722b3f6d66SOleksandr Tymoshenko static d_open_t		uinput_open;
732b3f6d66SOleksandr Tymoshenko static d_read_t		uinput_read;
742b3f6d66SOleksandr Tymoshenko static d_write_t	uinput_write;
752b3f6d66SOleksandr Tymoshenko static d_ioctl_t	uinput_ioctl;
762b3f6d66SOleksandr Tymoshenko static d_poll_t		uinput_poll;
772b3f6d66SOleksandr Tymoshenko static d_kqfilter_t	uinput_kqfilter;
782b3f6d66SOleksandr Tymoshenko static void uinput_dtor(void *);
792b3f6d66SOleksandr Tymoshenko 
802b3f6d66SOleksandr Tymoshenko static int uinput_kqread(struct knote *kn, long hint);
812b3f6d66SOleksandr Tymoshenko static void uinput_kqdetach(struct knote *kn);
822b3f6d66SOleksandr Tymoshenko 
832b3f6d66SOleksandr Tymoshenko static struct cdevsw uinput_cdevsw = {
842b3f6d66SOleksandr Tymoshenko 	.d_version = D_VERSION,
852b3f6d66SOleksandr Tymoshenko 	.d_open = uinput_open,
862b3f6d66SOleksandr Tymoshenko 	.d_read = uinput_read,
872b3f6d66SOleksandr Tymoshenko 	.d_write = uinput_write,
882b3f6d66SOleksandr Tymoshenko 	.d_ioctl = uinput_ioctl,
892b3f6d66SOleksandr Tymoshenko 	.d_poll = uinput_poll,
902b3f6d66SOleksandr Tymoshenko 	.d_kqfilter = uinput_kqfilter,
912b3f6d66SOleksandr Tymoshenko 	.d_name = "uinput",
922b3f6d66SOleksandr Tymoshenko };
932b3f6d66SOleksandr Tymoshenko 
942b3f6d66SOleksandr Tymoshenko static struct cdev *uinput_cdev;
952b3f6d66SOleksandr Tymoshenko 
962b3f6d66SOleksandr Tymoshenko static struct evdev_methods uinput_ev_methods = {
972b3f6d66SOleksandr Tymoshenko 	.ev_open = NULL,
982b3f6d66SOleksandr Tymoshenko 	.ev_close = NULL,
992b3f6d66SOleksandr Tymoshenko 	.ev_event = uinput_ev_event,
1002b3f6d66SOleksandr Tymoshenko };
1012b3f6d66SOleksandr Tymoshenko 
1022b3f6d66SOleksandr Tymoshenko static struct filterops uinput_filterops = {
1032b3f6d66SOleksandr Tymoshenko 	.f_isfd = 1,
1042b3f6d66SOleksandr Tymoshenko 	.f_attach = NULL,
1052b3f6d66SOleksandr Tymoshenko 	.f_detach = uinput_kqdetach,
1062b3f6d66SOleksandr Tymoshenko 	.f_event = uinput_kqread,
1072b3f6d66SOleksandr Tymoshenko };
1082b3f6d66SOleksandr Tymoshenko 
1092b3f6d66SOleksandr Tymoshenko struct uinput_cdev_state
1102b3f6d66SOleksandr Tymoshenko {
1112b3f6d66SOleksandr Tymoshenko 	enum uinput_state	ucs_state;
1122b3f6d66SOleksandr Tymoshenko 	struct evdev_dev *	ucs_evdev;
1132b3f6d66SOleksandr Tymoshenko 	struct sx		ucs_lock;
1142b3f6d66SOleksandr Tymoshenko 	size_t			ucs_buffer_head;
1152b3f6d66SOleksandr Tymoshenko 	size_t			ucs_buffer_tail;
1162b3f6d66SOleksandr Tymoshenko 	struct selinfo		ucs_selp;
1172b3f6d66SOleksandr Tymoshenko 	bool			ucs_blocked;
1182b3f6d66SOleksandr Tymoshenko 	bool			ucs_selected;
1192b3f6d66SOleksandr Tymoshenko 	struct input_event      ucs_buffer[UINPUT_BUFFER_SIZE];
1202b3f6d66SOleksandr Tymoshenko };
1212b3f6d66SOleksandr Tymoshenko 
1222b3f6d66SOleksandr Tymoshenko static void uinput_enqueue_event(struct uinput_cdev_state *, uint16_t,
1232b3f6d66SOleksandr Tymoshenko     uint16_t, int32_t);
1242b3f6d66SOleksandr Tymoshenko static int uinput_setup_provider(struct uinput_cdev_state *,
1252b3f6d66SOleksandr Tymoshenko     struct uinput_user_dev *);
1262b3f6d66SOleksandr Tymoshenko static int uinput_cdev_create(void);
1272b3f6d66SOleksandr Tymoshenko static void uinput_notify(struct uinput_cdev_state *);
1282b3f6d66SOleksandr Tymoshenko 
1292b3f6d66SOleksandr Tymoshenko static void
uinput_knllock(void * arg)1302b3f6d66SOleksandr Tymoshenko uinput_knllock(void *arg)
1312b3f6d66SOleksandr Tymoshenko {
1322b3f6d66SOleksandr Tymoshenko 	struct sx *sx = arg;
1332b3f6d66SOleksandr Tymoshenko 
1342b3f6d66SOleksandr Tymoshenko 	sx_xlock(sx);
1352b3f6d66SOleksandr Tymoshenko }
1362b3f6d66SOleksandr Tymoshenko 
1372b3f6d66SOleksandr Tymoshenko static void
uinput_knlunlock(void * arg)1382b3f6d66SOleksandr Tymoshenko uinput_knlunlock(void *arg)
1392b3f6d66SOleksandr Tymoshenko {
1402b3f6d66SOleksandr Tymoshenko 	struct sx *sx = arg;
1412b3f6d66SOleksandr Tymoshenko 
1422b3f6d66SOleksandr Tymoshenko 	sx_unlock(sx);
1432b3f6d66SOleksandr Tymoshenko }
1442b3f6d66SOleksandr Tymoshenko 
1452b3f6d66SOleksandr Tymoshenko static void
uinput_knl_assert_lock(void * arg,int what)146e90afaa0SMateusz Guzik uinput_knl_assert_lock(void *arg, int what)
1472b3f6d66SOleksandr Tymoshenko {
1482b3f6d66SOleksandr Tymoshenko 
149e90afaa0SMateusz Guzik 	if (what == LA_LOCKED)
1502b3f6d66SOleksandr Tymoshenko 		sx_assert((struct sx*)arg, SA_XLOCKED);
151e90afaa0SMateusz Guzik 	else
1522b3f6d66SOleksandr Tymoshenko 		sx_assert((struct sx*)arg, SA_UNLOCKED);
1532b3f6d66SOleksandr Tymoshenko }
1542b3f6d66SOleksandr Tymoshenko 
1552b3f6d66SOleksandr Tymoshenko static void
uinput_ev_event(struct evdev_dev * evdev,uint16_t type,uint16_t code,int32_t value)156911aed94SVladimir Kondratyev uinput_ev_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
157911aed94SVladimir Kondratyev     int32_t value)
1582b3f6d66SOleksandr Tymoshenko {
159911aed94SVladimir Kondratyev 	struct uinput_cdev_state *state = evdev_get_softc(evdev);
1602b3f6d66SOleksandr Tymoshenko 
1612b3f6d66SOleksandr Tymoshenko 	if (type == EV_LED)
1622b3f6d66SOleksandr Tymoshenko 		evdev_push_event(evdev, type, code, value);
1632b3f6d66SOleksandr Tymoshenko 
1642b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
1652b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state == UINPUT_RUNNING) {
1662b3f6d66SOleksandr Tymoshenko 		uinput_enqueue_event(state, type, code, value);
1672b3f6d66SOleksandr Tymoshenko 		uinput_notify(state);
1682b3f6d66SOleksandr Tymoshenko 	}
1692b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
1702b3f6d66SOleksandr Tymoshenko }
1712b3f6d66SOleksandr Tymoshenko 
1722b3f6d66SOleksandr Tymoshenko static void
uinput_enqueue_event(struct uinput_cdev_state * state,uint16_t type,uint16_t code,int32_t value)1732b3f6d66SOleksandr Tymoshenko uinput_enqueue_event(struct uinput_cdev_state *state, uint16_t type,
1742b3f6d66SOleksandr Tymoshenko     uint16_t code, int32_t value)
1752b3f6d66SOleksandr Tymoshenko {
1762b3f6d66SOleksandr Tymoshenko 	size_t head, tail;
1772b3f6d66SOleksandr Tymoshenko 
1782b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
1792b3f6d66SOleksandr Tymoshenko 
1802b3f6d66SOleksandr Tymoshenko 	head = state->ucs_buffer_head;
1812b3f6d66SOleksandr Tymoshenko 	tail = (state->ucs_buffer_tail + 1) % UINPUT_BUFFER_SIZE;
1822b3f6d66SOleksandr Tymoshenko 
1832b3f6d66SOleksandr Tymoshenko 	microtime(&state->ucs_buffer[tail].time);
1842b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].type = type;
1852b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].code = code;
1862b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].value = value;
1872b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer_tail = tail;
1882b3f6d66SOleksandr Tymoshenko 
1892b3f6d66SOleksandr Tymoshenko 	/* If queue is full remove oldest event */
1902b3f6d66SOleksandr Tymoshenko 	if (tail == head) {
1912b3f6d66SOleksandr Tymoshenko 		debugf(state, "state %p: buffer overflow", state);
1922b3f6d66SOleksandr Tymoshenko 
1932b3f6d66SOleksandr Tymoshenko 		head = (head + 1) % UINPUT_BUFFER_SIZE;
1942b3f6d66SOleksandr Tymoshenko 		state->ucs_buffer_head = head;
1952b3f6d66SOleksandr Tymoshenko 	}
1962b3f6d66SOleksandr Tymoshenko }
1972b3f6d66SOleksandr Tymoshenko 
1982b3f6d66SOleksandr Tymoshenko static int
uinput_open(struct cdev * dev,int oflags,int devtype,struct thread * td)1992b3f6d66SOleksandr Tymoshenko uinput_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
2002b3f6d66SOleksandr Tymoshenko {
2012b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2022b3f6d66SOleksandr Tymoshenko 
2032b3f6d66SOleksandr Tymoshenko 	state = malloc(sizeof(struct uinput_cdev_state), M_EVDEV,
2042b3f6d66SOleksandr Tymoshenko 	    M_WAITOK | M_ZERO);
2052b3f6d66SOleksandr Tymoshenko 	state->ucs_evdev = evdev_alloc();
2062b3f6d66SOleksandr Tymoshenko 
2072b3f6d66SOleksandr Tymoshenko 	sx_init(&state->ucs_lock, "uinput");
2082b3f6d66SOleksandr Tymoshenko 	knlist_init(&state->ucs_selp.si_note, &state->ucs_lock, uinput_knllock,
209e90afaa0SMateusz Guzik 	    uinput_knlunlock, uinput_knl_assert_lock);
2102b3f6d66SOleksandr Tymoshenko 
2112b3f6d66SOleksandr Tymoshenko 	devfs_set_cdevpriv(state, uinput_dtor);
2122b3f6d66SOleksandr Tymoshenko 	return (0);
2132b3f6d66SOleksandr Tymoshenko }
2142b3f6d66SOleksandr Tymoshenko 
2152b3f6d66SOleksandr Tymoshenko static void
uinput_dtor(void * data)2162b3f6d66SOleksandr Tymoshenko uinput_dtor(void *data)
2172b3f6d66SOleksandr Tymoshenko {
2182b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state = (struct uinput_cdev_state *)data;
2192b3f6d66SOleksandr Tymoshenko 
2202b3f6d66SOleksandr Tymoshenko 	evdev_free(state->ucs_evdev);
2212b3f6d66SOleksandr Tymoshenko 
2222b3f6d66SOleksandr Tymoshenko 	knlist_clear(&state->ucs_selp.si_note, 0);
2232b3f6d66SOleksandr Tymoshenko 	seldrain(&state->ucs_selp);
2242b3f6d66SOleksandr Tymoshenko 	knlist_destroy(&state->ucs_selp.si_note);
2252b3f6d66SOleksandr Tymoshenko 	sx_destroy(&state->ucs_lock);
2262b3f6d66SOleksandr Tymoshenko 	free(data, M_EVDEV);
2272b3f6d66SOleksandr Tymoshenko }
2282b3f6d66SOleksandr Tymoshenko 
2292b3f6d66SOleksandr Tymoshenko static int
uinput_read(struct cdev * dev,struct uio * uio,int ioflag)2302b3f6d66SOleksandr Tymoshenko uinput_read(struct cdev *dev, struct uio *uio, int ioflag)
2312b3f6d66SOleksandr Tymoshenko {
2322b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2332b3f6d66SOleksandr Tymoshenko 	struct input_event *event;
2342b3f6d66SOleksandr Tymoshenko 	int remaining, ret;
2352b3f6d66SOleksandr Tymoshenko 
2362b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
2372b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
2382b3f6d66SOleksandr Tymoshenko 		return (ret);
2392b3f6d66SOleksandr Tymoshenko 
2402b3f6d66SOleksandr Tymoshenko 	debugf(state, "read %zd bytes by thread %d", uio->uio_resid,
2412b3f6d66SOleksandr Tymoshenko 	    uio->uio_td->td_tid);
2422b3f6d66SOleksandr Tymoshenko 
2432b3f6d66SOleksandr Tymoshenko 	/* Zero-sized reads are allowed for error checking */
2442b3f6d66SOleksandr Tymoshenko 	if (uio->uio_resid != 0 && uio->uio_resid < sizeof(struct input_event))
2452b3f6d66SOleksandr Tymoshenko 		return (EINVAL);
2462b3f6d66SOleksandr Tymoshenko 
2472b3f6d66SOleksandr Tymoshenko 	remaining = uio->uio_resid / sizeof(struct input_event);
2482b3f6d66SOleksandr Tymoshenko 
2492b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
2502b3f6d66SOleksandr Tymoshenko 
2512b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state != UINPUT_RUNNING)
2522b3f6d66SOleksandr Tymoshenko 		ret = EINVAL;
2532b3f6d66SOleksandr Tymoshenko 
2542b3f6d66SOleksandr Tymoshenko 	if (ret == 0 && UINPUT_EMPTYQ(state)) {
2552b3f6d66SOleksandr Tymoshenko 		if (ioflag & O_NONBLOCK)
2562b3f6d66SOleksandr Tymoshenko 			ret = EWOULDBLOCK;
2572b3f6d66SOleksandr Tymoshenko 		else {
2582b3f6d66SOleksandr Tymoshenko 			if (remaining != 0) {
2592b3f6d66SOleksandr Tymoshenko 				state->ucs_blocked = true;
2602b3f6d66SOleksandr Tymoshenko 				ret = sx_sleep(state, &state->ucs_lock,
2612b3f6d66SOleksandr Tymoshenko 				    PCATCH, "uiread", 0);
2622b3f6d66SOleksandr Tymoshenko 			}
2632b3f6d66SOleksandr Tymoshenko 		}
2642b3f6d66SOleksandr Tymoshenko 	}
2652b3f6d66SOleksandr Tymoshenko 
2662b3f6d66SOleksandr Tymoshenko 	while (ret == 0 && !UINPUT_EMPTYQ(state) && remaining > 0) {
2672b3f6d66SOleksandr Tymoshenko 		event = &state->ucs_buffer[state->ucs_buffer_head];
2682b3f6d66SOleksandr Tymoshenko 		state->ucs_buffer_head = (state->ucs_buffer_head + 1) %
2692b3f6d66SOleksandr Tymoshenko 		    UINPUT_BUFFER_SIZE;
2702b3f6d66SOleksandr Tymoshenko 		remaining--;
2712b3f6d66SOleksandr Tymoshenko 		ret = uiomove(event, sizeof(struct input_event), uio);
2722b3f6d66SOleksandr Tymoshenko 	}
2732b3f6d66SOleksandr Tymoshenko 
2742b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
2752b3f6d66SOleksandr Tymoshenko 
2762b3f6d66SOleksandr Tymoshenko 	return (ret);
2772b3f6d66SOleksandr Tymoshenko }
2782b3f6d66SOleksandr Tymoshenko 
2792b3f6d66SOleksandr Tymoshenko static int
uinput_write(struct cdev * dev,struct uio * uio,int ioflag)2802b3f6d66SOleksandr Tymoshenko uinput_write(struct cdev *dev, struct uio *uio, int ioflag)
2812b3f6d66SOleksandr Tymoshenko {
2822b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2832b3f6d66SOleksandr Tymoshenko 	struct uinput_user_dev userdev;
2842b3f6d66SOleksandr Tymoshenko 	struct input_event event;
2852b3f6d66SOleksandr Tymoshenko 	int ret = 0;
2862b3f6d66SOleksandr Tymoshenko 
2872b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
2882b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
2892b3f6d66SOleksandr Tymoshenko 		return (ret);
2902b3f6d66SOleksandr Tymoshenko 
2912b3f6d66SOleksandr Tymoshenko 	debugf(state, "write %zd bytes by thread %d", uio->uio_resid,
2922b3f6d66SOleksandr Tymoshenko 	    uio->uio_td->td_tid);
2932b3f6d66SOleksandr Tymoshenko 
2942b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
2952b3f6d66SOleksandr Tymoshenko 
2962b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state != UINPUT_RUNNING) {
2972b3f6d66SOleksandr Tymoshenko 		/* Process written struct uinput_user_dev */
2982b3f6d66SOleksandr Tymoshenko 		if (uio->uio_resid != sizeof(struct uinput_user_dev)) {
2992b3f6d66SOleksandr Tymoshenko 			debugf(state, "write size not multiple of "
3002b3f6d66SOleksandr Tymoshenko 			    "struct uinput_user_dev size");
3012b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
3022b3f6d66SOleksandr Tymoshenko 		} else {
3032b3f6d66SOleksandr Tymoshenko 			ret = uiomove(&userdev, sizeof(struct uinput_user_dev),
3042b3f6d66SOleksandr Tymoshenko 			    uio);
3052b3f6d66SOleksandr Tymoshenko 			if (ret == 0)
3062b3f6d66SOleksandr Tymoshenko 				uinput_setup_provider(state, &userdev);
3072b3f6d66SOleksandr Tymoshenko 		}
3082b3f6d66SOleksandr Tymoshenko 	} else {
3092b3f6d66SOleksandr Tymoshenko 		/* Process written event */
3102b3f6d66SOleksandr Tymoshenko 		if (uio->uio_resid % sizeof(struct input_event) != 0) {
3112b3f6d66SOleksandr Tymoshenko 			debugf(state, "write size not multiple of "
3122b3f6d66SOleksandr Tymoshenko 			    "struct input_event size");
3132b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
3142b3f6d66SOleksandr Tymoshenko 		}
3152b3f6d66SOleksandr Tymoshenko 
3162b3f6d66SOleksandr Tymoshenko 		while (ret == 0 && uio->uio_resid > 0) {
3172b3f6d66SOleksandr Tymoshenko 			uiomove(&event, sizeof(struct input_event), uio);
3182b3f6d66SOleksandr Tymoshenko 			ret = evdev_push_event(state->ucs_evdev, event.type,
3192b3f6d66SOleksandr Tymoshenko 			    event.code, event.value);
3202b3f6d66SOleksandr Tymoshenko 		}
3212b3f6d66SOleksandr Tymoshenko 	}
3222b3f6d66SOleksandr Tymoshenko 
3232b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
3242b3f6d66SOleksandr Tymoshenko 
3252b3f6d66SOleksandr Tymoshenko 	return (ret);
3262b3f6d66SOleksandr Tymoshenko }
3272b3f6d66SOleksandr Tymoshenko 
3282b3f6d66SOleksandr Tymoshenko static int
uinput_setup_dev(struct uinput_cdev_state * state,struct input_id * id,char * name,uint32_t ff_effects_max)3292b3f6d66SOleksandr Tymoshenko uinput_setup_dev(struct uinput_cdev_state *state, struct input_id *id,
3302b3f6d66SOleksandr Tymoshenko     char *name, uint32_t ff_effects_max)
3312b3f6d66SOleksandr Tymoshenko {
3322b3f6d66SOleksandr Tymoshenko 
3332b3f6d66SOleksandr Tymoshenko 	if (name[0] == 0)
3342b3f6d66SOleksandr Tymoshenko 		return (EINVAL);
3352b3f6d66SOleksandr Tymoshenko 
3362b3f6d66SOleksandr Tymoshenko 	evdev_set_name(state->ucs_evdev, name);
3372b3f6d66SOleksandr Tymoshenko 	evdev_set_id(state->ucs_evdev, id->bustype, id->vendor, id->product,
3382b3f6d66SOleksandr Tymoshenko 	    id->version);
3392b3f6d66SOleksandr Tymoshenko 	state->ucs_state = UINPUT_CONFIGURED;
3402b3f6d66SOleksandr Tymoshenko 
3412b3f6d66SOleksandr Tymoshenko 	return (0);
3422b3f6d66SOleksandr Tymoshenko }
3432b3f6d66SOleksandr Tymoshenko 
3442b3f6d66SOleksandr Tymoshenko static int
uinput_setup_provider(struct uinput_cdev_state * state,struct uinput_user_dev * udev)3452b3f6d66SOleksandr Tymoshenko uinput_setup_provider(struct uinput_cdev_state *state,
3462b3f6d66SOleksandr Tymoshenko     struct uinput_user_dev *udev)
3472b3f6d66SOleksandr Tymoshenko {
3482b3f6d66SOleksandr Tymoshenko 	struct input_absinfo absinfo;
3492b3f6d66SOleksandr Tymoshenko 	int i, ret;
3502b3f6d66SOleksandr Tymoshenko 
3512b3f6d66SOleksandr Tymoshenko 	debugf(state, "setup_provider called, udev=%p", udev);
3522b3f6d66SOleksandr Tymoshenko 
3532b3f6d66SOleksandr Tymoshenko 	ret = uinput_setup_dev(state, &udev->id, udev->name,
3542b3f6d66SOleksandr Tymoshenko 	    udev->ff_effects_max);
3552b3f6d66SOleksandr Tymoshenko 	if (ret)
3562b3f6d66SOleksandr Tymoshenko 		return (ret);
3572b3f6d66SOleksandr Tymoshenko 
3582b3f6d66SOleksandr Tymoshenko 	bzero(&absinfo, sizeof(struct input_absinfo));
3592b3f6d66SOleksandr Tymoshenko 	for (i = 0; i < ABS_CNT; i++) {
3602b3f6d66SOleksandr Tymoshenko 		if (!bit_test(state->ucs_evdev->ev_abs_flags, i))
3612b3f6d66SOleksandr Tymoshenko 			continue;
3622b3f6d66SOleksandr Tymoshenko 
3632b3f6d66SOleksandr Tymoshenko 		absinfo.minimum = udev->absmin[i];
3642b3f6d66SOleksandr Tymoshenko 		absinfo.maximum = udev->absmax[i];
3652b3f6d66SOleksandr Tymoshenko 		absinfo.fuzz = udev->absfuzz[i];
3662b3f6d66SOleksandr Tymoshenko 		absinfo.flat = udev->absflat[i];
3672b3f6d66SOleksandr Tymoshenko 		evdev_set_absinfo(state->ucs_evdev, i, &absinfo);
3682b3f6d66SOleksandr Tymoshenko 	}
3692b3f6d66SOleksandr Tymoshenko 
3702b3f6d66SOleksandr Tymoshenko 	return (0);
3712b3f6d66SOleksandr Tymoshenko }
3722b3f6d66SOleksandr Tymoshenko 
3732b3f6d66SOleksandr Tymoshenko static int
uinput_poll(struct cdev * dev,int events,struct thread * td)3742b3f6d66SOleksandr Tymoshenko uinput_poll(struct cdev *dev, int events, struct thread *td)
3752b3f6d66SOleksandr Tymoshenko {
3762b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
3772b3f6d66SOleksandr Tymoshenko 	int revents = 0;
3782b3f6d66SOleksandr Tymoshenko 
3792b3f6d66SOleksandr Tymoshenko 	if (devfs_get_cdevpriv((void **)&state) != 0)
3802b3f6d66SOleksandr Tymoshenko 		return (POLLNVAL);
3812b3f6d66SOleksandr Tymoshenko 
3822b3f6d66SOleksandr Tymoshenko 	debugf(state, "poll by thread %d", td->td_tid);
3832b3f6d66SOleksandr Tymoshenko 
3842b3f6d66SOleksandr Tymoshenko 	/* Always allow write */
3852b3f6d66SOleksandr Tymoshenko 	if (events & (POLLOUT | POLLWRNORM))
3862b3f6d66SOleksandr Tymoshenko 		revents |= (events & (POLLOUT | POLLWRNORM));
3872b3f6d66SOleksandr Tymoshenko 
3882b3f6d66SOleksandr Tymoshenko 	if (events & (POLLIN | POLLRDNORM)) {
3892b3f6d66SOleksandr Tymoshenko 		UINPUT_LOCK(state);
3902b3f6d66SOleksandr Tymoshenko 		if (!UINPUT_EMPTYQ(state))
3912b3f6d66SOleksandr Tymoshenko 			revents = events & (POLLIN | POLLRDNORM);
3922b3f6d66SOleksandr Tymoshenko 		else {
3932b3f6d66SOleksandr Tymoshenko 			state->ucs_selected = true;
3942b3f6d66SOleksandr Tymoshenko 			selrecord(td, &state->ucs_selp);
3952b3f6d66SOleksandr Tymoshenko 		}
3962b3f6d66SOleksandr Tymoshenko 		UINPUT_UNLOCK(state);
3972b3f6d66SOleksandr Tymoshenko 	}
3982b3f6d66SOleksandr Tymoshenko 
3992b3f6d66SOleksandr Tymoshenko 	return (revents);
4002b3f6d66SOleksandr Tymoshenko }
4012b3f6d66SOleksandr Tymoshenko 
4022b3f6d66SOleksandr Tymoshenko static int
uinput_kqfilter(struct cdev * dev,struct knote * kn)4032b3f6d66SOleksandr Tymoshenko uinput_kqfilter(struct cdev *dev, struct knote *kn)
4042b3f6d66SOleksandr Tymoshenko {
4052b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4062b3f6d66SOleksandr Tymoshenko 	int ret;
4072b3f6d66SOleksandr Tymoshenko 
4082b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
4092b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
4102b3f6d66SOleksandr Tymoshenko 		return (ret);
4112b3f6d66SOleksandr Tymoshenko 
4122b3f6d66SOleksandr Tymoshenko 	switch(kn->kn_filter) {
4132b3f6d66SOleksandr Tymoshenko 	case EVFILT_READ:
4142b3f6d66SOleksandr Tymoshenko 		kn->kn_fop = &uinput_filterops;
4152b3f6d66SOleksandr Tymoshenko 		break;
4162b3f6d66SOleksandr Tymoshenko 	default:
4172b3f6d66SOleksandr Tymoshenko 		return(EINVAL);
4182b3f6d66SOleksandr Tymoshenko 	}
4192b3f6d66SOleksandr Tymoshenko 	kn->kn_hook = (caddr_t)state;
4202b3f6d66SOleksandr Tymoshenko 
4212b3f6d66SOleksandr Tymoshenko 	knlist_add(&state->ucs_selp.si_note, kn, 0);
4222b3f6d66SOleksandr Tymoshenko 	return (0);
4232b3f6d66SOleksandr Tymoshenko }
4242b3f6d66SOleksandr Tymoshenko 
4252b3f6d66SOleksandr Tymoshenko static int
uinput_kqread(struct knote * kn,long hint)4262b3f6d66SOleksandr Tymoshenko uinput_kqread(struct knote *kn, long hint)
4272b3f6d66SOleksandr Tymoshenko {
4282b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4292b3f6d66SOleksandr Tymoshenko 	int ret;
4302b3f6d66SOleksandr Tymoshenko 
4312b3f6d66SOleksandr Tymoshenko 	state = (struct uinput_cdev_state *)kn->kn_hook;
4322b3f6d66SOleksandr Tymoshenko 
4332b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4342b3f6d66SOleksandr Tymoshenko 
4352b3f6d66SOleksandr Tymoshenko 	ret = !UINPUT_EMPTYQ(state);
4362b3f6d66SOleksandr Tymoshenko 	return (ret);
4372b3f6d66SOleksandr Tymoshenko }
4382b3f6d66SOleksandr Tymoshenko 
4392b3f6d66SOleksandr Tymoshenko static void
uinput_kqdetach(struct knote * kn)4402b3f6d66SOleksandr Tymoshenko uinput_kqdetach(struct knote *kn)
4412b3f6d66SOleksandr Tymoshenko {
4422b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4432b3f6d66SOleksandr Tymoshenko 
4442b3f6d66SOleksandr Tymoshenko 	state = (struct uinput_cdev_state *)kn->kn_hook;
4452b3f6d66SOleksandr Tymoshenko 	knlist_remove(&state->ucs_selp.si_note, kn, 0);
4462b3f6d66SOleksandr Tymoshenko }
4472b3f6d66SOleksandr Tymoshenko 
4482b3f6d66SOleksandr Tymoshenko static void
uinput_notify(struct uinput_cdev_state * state)4492b3f6d66SOleksandr Tymoshenko uinput_notify(struct uinput_cdev_state *state)
4502b3f6d66SOleksandr Tymoshenko {
4512b3f6d66SOleksandr Tymoshenko 
4522b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4532b3f6d66SOleksandr Tymoshenko 
4542b3f6d66SOleksandr Tymoshenko 	if (state->ucs_blocked) {
4552b3f6d66SOleksandr Tymoshenko 		state->ucs_blocked = false;
4562b3f6d66SOleksandr Tymoshenko 		wakeup(state);
4572b3f6d66SOleksandr Tymoshenko 	}
4582b3f6d66SOleksandr Tymoshenko 	if (state->ucs_selected) {
4592b3f6d66SOleksandr Tymoshenko 		state->ucs_selected = false;
4602b3f6d66SOleksandr Tymoshenko 		selwakeup(&state->ucs_selp);
4612b3f6d66SOleksandr Tymoshenko 	}
4622b3f6d66SOleksandr Tymoshenko 	KNOTE_LOCKED(&state->ucs_selp.si_note, 0);
4632b3f6d66SOleksandr Tymoshenko }
4642b3f6d66SOleksandr Tymoshenko 
4652b3f6d66SOleksandr Tymoshenko static int
uinput_ioctl_sub(struct uinput_cdev_state * state,u_long cmd,caddr_t data)4662b3f6d66SOleksandr Tymoshenko uinput_ioctl_sub(struct uinput_cdev_state *state, u_long cmd, caddr_t data)
4672b3f6d66SOleksandr Tymoshenko {
4682b3f6d66SOleksandr Tymoshenko 	struct uinput_setup *us;
4692b3f6d66SOleksandr Tymoshenko 	struct uinput_abs_setup *uabs;
4702b3f6d66SOleksandr Tymoshenko 	int ret, len, intdata;
4712b3f6d66SOleksandr Tymoshenko 	char buf[NAMELEN];
4722b3f6d66SOleksandr Tymoshenko 
4732b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4742b3f6d66SOleksandr Tymoshenko 
4752b3f6d66SOleksandr Tymoshenko 	len = IOCPARM_LEN(cmd);
4762b3f6d66SOleksandr Tymoshenko 	if ((cmd & IOC_DIRMASK) == IOC_VOID && len == sizeof(int))
4772b3f6d66SOleksandr Tymoshenko 		intdata = *(int *)data;
4782b3f6d66SOleksandr Tymoshenko 
4792b3f6d66SOleksandr Tymoshenko 	switch (IOCBASECMD(cmd)) {
4802b3f6d66SOleksandr Tymoshenko 	case UI_GET_SYSNAME(0):
4812b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_RUNNING)
4822b3f6d66SOleksandr Tymoshenko 			return (ENOENT);
4832b3f6d66SOleksandr Tymoshenko 		if (len == 0)
4842b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
4852b3f6d66SOleksandr Tymoshenko 		snprintf(data, len, "event%d", state->ucs_evdev->ev_unit);
4862b3f6d66SOleksandr Tymoshenko 		return (0);
4872b3f6d66SOleksandr Tymoshenko 	}
4882b3f6d66SOleksandr Tymoshenko 
4892b3f6d66SOleksandr Tymoshenko 	switch (cmd) {
4902b3f6d66SOleksandr Tymoshenko 	case UI_DEV_CREATE:
4912b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_CONFIGURED)
4922b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
4932b3f6d66SOleksandr Tymoshenko 
4942b3f6d66SOleksandr Tymoshenko 		evdev_set_methods(state->ucs_evdev, state, &uinput_ev_methods);
4952b3f6d66SOleksandr Tymoshenko 		evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_SOFTREPEAT);
496*66bd52f5SVladimir Kondratyev 		evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_MT_KEEPID);
497670aa764SOleksandr Tymoshenko 		ret = evdev_register(state->ucs_evdev);
498670aa764SOleksandr Tymoshenko 		if (ret == 0)
4992b3f6d66SOleksandr Tymoshenko 			state->ucs_state = UINPUT_RUNNING;
500670aa764SOleksandr Tymoshenko 		return (ret);
5012b3f6d66SOleksandr Tymoshenko 
5022b3f6d66SOleksandr Tymoshenko 	case UI_DEV_DESTROY:
5032b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_RUNNING)
5042b3f6d66SOleksandr Tymoshenko 			return (0);
5052b3f6d66SOleksandr Tymoshenko 
5062b3f6d66SOleksandr Tymoshenko 		evdev_unregister(state->ucs_evdev);
5072b3f6d66SOleksandr Tymoshenko 		bzero(state->ucs_evdev, sizeof(struct evdev_dev));
5082b3f6d66SOleksandr Tymoshenko 		state->ucs_state = UINPUT_NEW;
5092b3f6d66SOleksandr Tymoshenko 		return (0);
5102b3f6d66SOleksandr Tymoshenko 
5112b3f6d66SOleksandr Tymoshenko 	case UI_DEV_SETUP:
5122b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5132b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5142b3f6d66SOleksandr Tymoshenko 
5152b3f6d66SOleksandr Tymoshenko 		us = (struct uinput_setup *)data;
5162b3f6d66SOleksandr Tymoshenko 		return (uinput_setup_dev(state, &us->id, us->name,
5172b3f6d66SOleksandr Tymoshenko 		    us->ff_effects_max));
5182b3f6d66SOleksandr Tymoshenko 
5192b3f6d66SOleksandr Tymoshenko 	case UI_ABS_SETUP:
5202b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5212b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5222b3f6d66SOleksandr Tymoshenko 
5232b3f6d66SOleksandr Tymoshenko 		uabs = (struct uinput_abs_setup *)data;
524886f6623SOleksandr Tymoshenko 		if (uabs->code > ABS_MAX)
5252b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5262b3f6d66SOleksandr Tymoshenko 
5275af73ad5SVladimir Kondratyev 		evdev_set_abs_bit(state->ucs_evdev, uabs->code);
5285af73ad5SVladimir Kondratyev 		evdev_set_absinfo(state->ucs_evdev, uabs->code,
5295af73ad5SVladimir Kondratyev 		    &uabs->absinfo);
5302b3f6d66SOleksandr Tymoshenko 		return (0);
5312b3f6d66SOleksandr Tymoshenko 
5322b3f6d66SOleksandr Tymoshenko 	case UI_SET_EVBIT:
5332b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5342b3f6d66SOleksandr Tymoshenko 		    intdata > EV_MAX || intdata < 0)
5352b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5362b3f6d66SOleksandr Tymoshenko 		evdev_support_event(state->ucs_evdev, intdata);
5372b3f6d66SOleksandr Tymoshenko 		return (0);
5382b3f6d66SOleksandr Tymoshenko 
5392b3f6d66SOleksandr Tymoshenko 	case UI_SET_KEYBIT:
5402b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5412b3f6d66SOleksandr Tymoshenko 		    intdata > KEY_MAX || intdata < 0)
5422b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5432b3f6d66SOleksandr Tymoshenko 		evdev_support_key(state->ucs_evdev, intdata);
5442b3f6d66SOleksandr Tymoshenko 		return (0);
5452b3f6d66SOleksandr Tymoshenko 
5462b3f6d66SOleksandr Tymoshenko 	case UI_SET_RELBIT:
5472b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5482b3f6d66SOleksandr Tymoshenko 		    intdata > REL_MAX || intdata < 0)
5492b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5502b3f6d66SOleksandr Tymoshenko 		evdev_support_rel(state->ucs_evdev, intdata);
5512b3f6d66SOleksandr Tymoshenko 		return (0);
5522b3f6d66SOleksandr Tymoshenko 
5532b3f6d66SOleksandr Tymoshenko 	case UI_SET_ABSBIT:
5542b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5552b3f6d66SOleksandr Tymoshenko 		    intdata > ABS_MAX || intdata < 0)
5562b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5572b3f6d66SOleksandr Tymoshenko 		evdev_set_abs_bit(state->ucs_evdev, intdata);
5582b3f6d66SOleksandr Tymoshenko 		return (0);
5592b3f6d66SOleksandr Tymoshenko 
5602b3f6d66SOleksandr Tymoshenko 	case UI_SET_MSCBIT:
5612b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5622b3f6d66SOleksandr Tymoshenko 		    intdata > MSC_MAX || intdata < 0)
5632b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5642b3f6d66SOleksandr Tymoshenko 		evdev_support_msc(state->ucs_evdev, intdata);
5652b3f6d66SOleksandr Tymoshenko 		return (0);
5662b3f6d66SOleksandr Tymoshenko 
5672b3f6d66SOleksandr Tymoshenko 	case UI_SET_LEDBIT:
5682b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5692b3f6d66SOleksandr Tymoshenko 		    intdata > LED_MAX || intdata < 0)
5702b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5712b3f6d66SOleksandr Tymoshenko 		evdev_support_led(state->ucs_evdev, intdata);
5722b3f6d66SOleksandr Tymoshenko 		return (0);
5732b3f6d66SOleksandr Tymoshenko 
5742b3f6d66SOleksandr Tymoshenko 	case UI_SET_SNDBIT:
5752b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5762b3f6d66SOleksandr Tymoshenko 		    intdata > SND_MAX || intdata < 0)
5772b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5782b3f6d66SOleksandr Tymoshenko 		evdev_support_snd(state->ucs_evdev, intdata);
5792b3f6d66SOleksandr Tymoshenko 		return (0);
5802b3f6d66SOleksandr Tymoshenko 
5812b3f6d66SOleksandr Tymoshenko 	case UI_SET_FFBIT:
5822b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5832b3f6d66SOleksandr Tymoshenko 		    intdata > FF_MAX || intdata < 0)
5842b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5852b3f6d66SOleksandr Tymoshenko 		/* Fake unsupported ioctl */
5862b3f6d66SOleksandr Tymoshenko 		return (0);
5872b3f6d66SOleksandr Tymoshenko 
5882b3f6d66SOleksandr Tymoshenko 	case UI_SET_PHYS:
5892b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5902b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5912b3f6d66SOleksandr Tymoshenko 		ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
5922b3f6d66SOleksandr Tymoshenko 		/* Linux returns EINVAL when string does not fit the buffer */
5932b3f6d66SOleksandr Tymoshenko 		if (ret == ENAMETOOLONG)
5942b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
5952b3f6d66SOleksandr Tymoshenko 		if (ret != 0)
5962b3f6d66SOleksandr Tymoshenko 			return (ret);
5972b3f6d66SOleksandr Tymoshenko 		evdev_set_phys(state->ucs_evdev, buf);
5982b3f6d66SOleksandr Tymoshenko 		return (0);
5992b3f6d66SOleksandr Tymoshenko 
6004b58fa12SVladimir Kondratyev 	case UI_SET_BSDUNIQ:
6014b58fa12SVladimir Kondratyev 		if (state->ucs_state == UINPUT_RUNNING)
6024b58fa12SVladimir Kondratyev 			return (EINVAL);
6034b58fa12SVladimir Kondratyev 		ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
6044b58fa12SVladimir Kondratyev 		if (ret != 0)
6054b58fa12SVladimir Kondratyev 			return (ret);
6064b58fa12SVladimir Kondratyev 		evdev_set_serial(state->ucs_evdev, buf);
6074b58fa12SVladimir Kondratyev 		return (0);
6084b58fa12SVladimir Kondratyev 
6092b3f6d66SOleksandr Tymoshenko 	case UI_SET_SWBIT:
6102b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
6112b3f6d66SOleksandr Tymoshenko 		    intdata > SW_MAX || intdata < 0)
6122b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6132b3f6d66SOleksandr Tymoshenko 		evdev_support_sw(state->ucs_evdev, intdata);
6142b3f6d66SOleksandr Tymoshenko 		return (0);
6152b3f6d66SOleksandr Tymoshenko 
6162b3f6d66SOleksandr Tymoshenko 	case UI_SET_PROPBIT:
6172b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
6182b3f6d66SOleksandr Tymoshenko 		    intdata > INPUT_PROP_MAX || intdata < 0)
6192b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6202b3f6d66SOleksandr Tymoshenko 		evdev_support_prop(state->ucs_evdev, intdata);
6212b3f6d66SOleksandr Tymoshenko 		return (0);
6222b3f6d66SOleksandr Tymoshenko 
6232b3f6d66SOleksandr Tymoshenko 	case UI_BEGIN_FF_UPLOAD:
6242b3f6d66SOleksandr Tymoshenko 	case UI_END_FF_UPLOAD:
6252b3f6d66SOleksandr Tymoshenko 	case UI_BEGIN_FF_ERASE:
6262b3f6d66SOleksandr Tymoshenko 	case UI_END_FF_ERASE:
6272b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
6282b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6292b3f6d66SOleksandr Tymoshenko 		/* Fake unsupported ioctl */
6302b3f6d66SOleksandr Tymoshenko 		return (0);
6312b3f6d66SOleksandr Tymoshenko 
6322b3f6d66SOleksandr Tymoshenko 	case UI_GET_VERSION:
6332b3f6d66SOleksandr Tymoshenko 		*(unsigned int *)data = UINPUT_VERSION;
6342b3f6d66SOleksandr Tymoshenko 		return (0);
6352b3f6d66SOleksandr Tymoshenko 	}
6362b3f6d66SOleksandr Tymoshenko 
6372b3f6d66SOleksandr Tymoshenko 	return (EINVAL);
6382b3f6d66SOleksandr Tymoshenko }
6392b3f6d66SOleksandr Tymoshenko 
6402b3f6d66SOleksandr Tymoshenko static int
uinput_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)6412b3f6d66SOleksandr Tymoshenko uinput_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
6422b3f6d66SOleksandr Tymoshenko     struct thread *td)
6432b3f6d66SOleksandr Tymoshenko {
6442b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
6452b3f6d66SOleksandr Tymoshenko 	int ret;
6462b3f6d66SOleksandr Tymoshenko 
6472b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
6482b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
6492b3f6d66SOleksandr Tymoshenko 		return (ret);
6502b3f6d66SOleksandr Tymoshenko 
6512b3f6d66SOleksandr Tymoshenko 	debugf(state, "ioctl called: cmd=0x%08lx, data=%p", cmd, data);
6522b3f6d66SOleksandr Tymoshenko 
6532b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
6542b3f6d66SOleksandr Tymoshenko 	ret = uinput_ioctl_sub(state, cmd, data);
6552b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
6562b3f6d66SOleksandr Tymoshenko 
6572b3f6d66SOleksandr Tymoshenko 	return (ret);
6582b3f6d66SOleksandr Tymoshenko }
6592b3f6d66SOleksandr Tymoshenko 
6602b3f6d66SOleksandr Tymoshenko static int
uinput_cdev_create(void)6612b3f6d66SOleksandr Tymoshenko uinput_cdev_create(void)
6622b3f6d66SOleksandr Tymoshenko {
6632b3f6d66SOleksandr Tymoshenko 	struct make_dev_args mda;
6642b3f6d66SOleksandr Tymoshenko 	int ret;
6652b3f6d66SOleksandr Tymoshenko 
6662b3f6d66SOleksandr Tymoshenko 	make_dev_args_init(&mda);
6672b3f6d66SOleksandr Tymoshenko 	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
6682b3f6d66SOleksandr Tymoshenko 	mda.mda_devsw = &uinput_cdevsw;
6692b3f6d66SOleksandr Tymoshenko 	mda.mda_uid = UID_ROOT;
6702b3f6d66SOleksandr Tymoshenko 	mda.mda_gid = GID_WHEEL;
6712b3f6d66SOleksandr Tymoshenko 	mda.mda_mode = 0600;
6722b3f6d66SOleksandr Tymoshenko 
6732b3f6d66SOleksandr Tymoshenko 	ret = make_dev_s(&mda, &uinput_cdev, "uinput");
6742b3f6d66SOleksandr Tymoshenko 
6752b3f6d66SOleksandr Tymoshenko 	return (ret);
6762b3f6d66SOleksandr Tymoshenko }
6772b3f6d66SOleksandr Tymoshenko 
6782b3f6d66SOleksandr Tymoshenko static int
uinput_cdev_destroy(void)6792b3f6d66SOleksandr Tymoshenko uinput_cdev_destroy(void)
6802b3f6d66SOleksandr Tymoshenko {
6812b3f6d66SOleksandr Tymoshenko 
6822b3f6d66SOleksandr Tymoshenko 	destroy_dev(uinput_cdev);
6832b3f6d66SOleksandr Tymoshenko 
6842b3f6d66SOleksandr Tymoshenko 	return (0);
6852b3f6d66SOleksandr Tymoshenko }
6862b3f6d66SOleksandr Tymoshenko 
6872b3f6d66SOleksandr Tymoshenko static int
uinput_modevent(module_t mod __unused,int cmd,void * data)6882b3f6d66SOleksandr Tymoshenko uinput_modevent(module_t mod __unused, int cmd, void *data)
6892b3f6d66SOleksandr Tymoshenko {
6902b3f6d66SOleksandr Tymoshenko 	int ret = 0;
6912b3f6d66SOleksandr Tymoshenko 
6922b3f6d66SOleksandr Tymoshenko 	switch (cmd) {
6932b3f6d66SOleksandr Tymoshenko 	case MOD_LOAD:
6942b3f6d66SOleksandr Tymoshenko 		ret = uinput_cdev_create();
6952b3f6d66SOleksandr Tymoshenko 		break;
6962b3f6d66SOleksandr Tymoshenko 
6972b3f6d66SOleksandr Tymoshenko 	case MOD_UNLOAD:
6982b3f6d66SOleksandr Tymoshenko 		ret = uinput_cdev_destroy();
6992b3f6d66SOleksandr Tymoshenko 		break;
7002b3f6d66SOleksandr Tymoshenko 
7012b3f6d66SOleksandr Tymoshenko 	case MOD_SHUTDOWN:
7022b3f6d66SOleksandr Tymoshenko 		break;
7032b3f6d66SOleksandr Tymoshenko 
7042b3f6d66SOleksandr Tymoshenko 	default:
7052b3f6d66SOleksandr Tymoshenko 		ret = EINVAL;
7062b3f6d66SOleksandr Tymoshenko 		break;
7072b3f6d66SOleksandr Tymoshenko 	}
7082b3f6d66SOleksandr Tymoshenko 
7092b3f6d66SOleksandr Tymoshenko 	return (ret);
7102b3f6d66SOleksandr Tymoshenko }
7112b3f6d66SOleksandr Tymoshenko 
7122b3f6d66SOleksandr Tymoshenko DEV_MODULE(uinput, uinput_modevent, NULL);
713a6b15a34SOleksandr Tymoshenko MODULE_VERSION(uinput, 1);
714a6b15a34SOleksandr Tymoshenko MODULE_DEPEND(uinput, evdev, 1, 1, 1);
715