xref: /freebsd-13.1/sys/dev/evdev/uinput.c (revision f160aa0d)
12b3f6d66SOleksandr Tymoshenko /*-
22b3f6d66SOleksandr Tymoshenko  * Copyright (c) 2014 Jakub Wojciech Klama <[email protected]>
3e6502802SVladimir Kondratyev  * Copyright (c) 2015-2016 Vladimir Kondratyev <[email protected]>
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
uinput_knllock(void * arg)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
uinput_knlunlock(void * arg)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
uinput_knl_assert_lock(void * arg,int what)148e90afaa0SMateusz Guzik uinput_knl_assert_lock(void *arg, int what)
1492b3f6d66SOleksandr Tymoshenko {
1502b3f6d66SOleksandr Tymoshenko 
151e90afaa0SMateusz Guzik 	if (what == LA_LOCKED)
1522b3f6d66SOleksandr Tymoshenko 		sx_assert((struct sx*)arg, SA_XLOCKED);
153e90afaa0SMateusz Guzik 	else
1542b3f6d66SOleksandr Tymoshenko 		sx_assert((struct sx*)arg, SA_UNLOCKED);
1552b3f6d66SOleksandr Tymoshenko }
1562b3f6d66SOleksandr Tymoshenko 
1572b3f6d66SOleksandr Tymoshenko static void
uinput_ev_event(struct evdev_dev * evdev,uint16_t type,uint16_t code,int32_t value)158911aed94SVladimir Kondratyev uinput_ev_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
159911aed94SVladimir Kondratyev     int32_t value)
1602b3f6d66SOleksandr Tymoshenko {
161911aed94SVladimir Kondratyev 	struct uinput_cdev_state *state = evdev_get_softc(evdev);
1622b3f6d66SOleksandr Tymoshenko 
1632b3f6d66SOleksandr Tymoshenko 	if (type == EV_LED)
1642b3f6d66SOleksandr Tymoshenko 		evdev_push_event(evdev, type, code, value);
1652b3f6d66SOleksandr Tymoshenko 
1662b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
1672b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state == UINPUT_RUNNING) {
1682b3f6d66SOleksandr Tymoshenko 		uinput_enqueue_event(state, type, code, value);
1692b3f6d66SOleksandr Tymoshenko 		uinput_notify(state);
1702b3f6d66SOleksandr Tymoshenko 	}
1712b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
1722b3f6d66SOleksandr Tymoshenko }
1732b3f6d66SOleksandr Tymoshenko 
1742b3f6d66SOleksandr Tymoshenko static void
uinput_enqueue_event(struct uinput_cdev_state * state,uint16_t type,uint16_t code,int32_t value)1752b3f6d66SOleksandr Tymoshenko uinput_enqueue_event(struct uinput_cdev_state *state, uint16_t type,
1762b3f6d66SOleksandr Tymoshenko     uint16_t code, int32_t value)
1772b3f6d66SOleksandr Tymoshenko {
1782b3f6d66SOleksandr Tymoshenko 	size_t head, tail;
1792b3f6d66SOleksandr Tymoshenko 
1802b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
1812b3f6d66SOleksandr Tymoshenko 
1822b3f6d66SOleksandr Tymoshenko 	head = state->ucs_buffer_head;
1832b3f6d66SOleksandr Tymoshenko 	tail = (state->ucs_buffer_tail + 1) % UINPUT_BUFFER_SIZE;
1842b3f6d66SOleksandr Tymoshenko 
1852b3f6d66SOleksandr Tymoshenko 	microtime(&state->ucs_buffer[tail].time);
1862b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].type = type;
1872b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].code = code;
1882b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer[tail].value = value;
1892b3f6d66SOleksandr Tymoshenko 	state->ucs_buffer_tail = tail;
1902b3f6d66SOleksandr Tymoshenko 
1912b3f6d66SOleksandr Tymoshenko 	/* If queue is full remove oldest event */
1922b3f6d66SOleksandr Tymoshenko 	if (tail == head) {
1932b3f6d66SOleksandr Tymoshenko 		debugf(state, "state %p: buffer overflow", state);
1942b3f6d66SOleksandr Tymoshenko 
1952b3f6d66SOleksandr Tymoshenko 		head = (head + 1) % UINPUT_BUFFER_SIZE;
1962b3f6d66SOleksandr Tymoshenko 		state->ucs_buffer_head = head;
1972b3f6d66SOleksandr Tymoshenko 	}
1982b3f6d66SOleksandr Tymoshenko }
1992b3f6d66SOleksandr Tymoshenko 
2002b3f6d66SOleksandr Tymoshenko static int
uinput_open(struct cdev * dev,int oflags,int devtype,struct thread * td)2012b3f6d66SOleksandr Tymoshenko uinput_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
2022b3f6d66SOleksandr Tymoshenko {
2032b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2042b3f6d66SOleksandr Tymoshenko 
2052b3f6d66SOleksandr Tymoshenko 	state = malloc(sizeof(struct uinput_cdev_state), M_EVDEV,
2062b3f6d66SOleksandr Tymoshenko 	    M_WAITOK | M_ZERO);
2072b3f6d66SOleksandr Tymoshenko 	state->ucs_evdev = evdev_alloc();
2082b3f6d66SOleksandr Tymoshenko 
2092b3f6d66SOleksandr Tymoshenko 	sx_init(&state->ucs_lock, "uinput");
2102b3f6d66SOleksandr Tymoshenko 	knlist_init(&state->ucs_selp.si_note, &state->ucs_lock, uinput_knllock,
211e90afaa0SMateusz Guzik 	    uinput_knlunlock, uinput_knl_assert_lock);
2122b3f6d66SOleksandr Tymoshenko 
2132b3f6d66SOleksandr Tymoshenko 	devfs_set_cdevpriv(state, uinput_dtor);
2142b3f6d66SOleksandr Tymoshenko 	return (0);
2152b3f6d66SOleksandr Tymoshenko }
2162b3f6d66SOleksandr Tymoshenko 
2172b3f6d66SOleksandr Tymoshenko static void
uinput_dtor(void * data)2182b3f6d66SOleksandr Tymoshenko uinput_dtor(void *data)
2192b3f6d66SOleksandr Tymoshenko {
2202b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state = (struct uinput_cdev_state *)data;
2212b3f6d66SOleksandr Tymoshenko 
2222b3f6d66SOleksandr Tymoshenko 	evdev_free(state->ucs_evdev);
2232b3f6d66SOleksandr Tymoshenko 
2242b3f6d66SOleksandr Tymoshenko 	knlist_clear(&state->ucs_selp.si_note, 0);
2252b3f6d66SOleksandr Tymoshenko 	seldrain(&state->ucs_selp);
2262b3f6d66SOleksandr Tymoshenko 	knlist_destroy(&state->ucs_selp.si_note);
2272b3f6d66SOleksandr Tymoshenko 	sx_destroy(&state->ucs_lock);
2282b3f6d66SOleksandr Tymoshenko 	free(data, M_EVDEV);
2292b3f6d66SOleksandr Tymoshenko }
2302b3f6d66SOleksandr Tymoshenko 
2312b3f6d66SOleksandr Tymoshenko static int
uinput_read(struct cdev * dev,struct uio * uio,int ioflag)2322b3f6d66SOleksandr Tymoshenko uinput_read(struct cdev *dev, struct uio *uio, int ioflag)
2332b3f6d66SOleksandr Tymoshenko {
2342b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2352b3f6d66SOleksandr Tymoshenko 	struct input_event *event;
2362b3f6d66SOleksandr Tymoshenko 	int remaining, ret;
2372b3f6d66SOleksandr Tymoshenko 
2382b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
2392b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
2402b3f6d66SOleksandr Tymoshenko 		return (ret);
2412b3f6d66SOleksandr Tymoshenko 
2422b3f6d66SOleksandr Tymoshenko 	debugf(state, "read %zd bytes by thread %d", uio->uio_resid,
2432b3f6d66SOleksandr Tymoshenko 	    uio->uio_td->td_tid);
2442b3f6d66SOleksandr Tymoshenko 
2452b3f6d66SOleksandr Tymoshenko 	/* Zero-sized reads are allowed for error checking */
2462b3f6d66SOleksandr Tymoshenko 	if (uio->uio_resid != 0 && uio->uio_resid < sizeof(struct input_event))
2472b3f6d66SOleksandr Tymoshenko 		return (EINVAL);
2482b3f6d66SOleksandr Tymoshenko 
2492b3f6d66SOleksandr Tymoshenko 	remaining = uio->uio_resid / sizeof(struct input_event);
2502b3f6d66SOleksandr Tymoshenko 
2512b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
2522b3f6d66SOleksandr Tymoshenko 
2532b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state != UINPUT_RUNNING)
2542b3f6d66SOleksandr Tymoshenko 		ret = EINVAL;
2552b3f6d66SOleksandr Tymoshenko 
2562b3f6d66SOleksandr Tymoshenko 	if (ret == 0 && UINPUT_EMPTYQ(state)) {
2572b3f6d66SOleksandr Tymoshenko 		if (ioflag & O_NONBLOCK)
2582b3f6d66SOleksandr Tymoshenko 			ret = EWOULDBLOCK;
2592b3f6d66SOleksandr Tymoshenko 		else {
2602b3f6d66SOleksandr Tymoshenko 			if (remaining != 0) {
2612b3f6d66SOleksandr Tymoshenko 				state->ucs_blocked = true;
2622b3f6d66SOleksandr Tymoshenko 				ret = sx_sleep(state, &state->ucs_lock,
2632b3f6d66SOleksandr Tymoshenko 				    PCATCH, "uiread", 0);
2642b3f6d66SOleksandr Tymoshenko 			}
2652b3f6d66SOleksandr Tymoshenko 		}
2662b3f6d66SOleksandr Tymoshenko 	}
2672b3f6d66SOleksandr Tymoshenko 
2682b3f6d66SOleksandr Tymoshenko 	while (ret == 0 && !UINPUT_EMPTYQ(state) && remaining > 0) {
2692b3f6d66SOleksandr Tymoshenko 		event = &state->ucs_buffer[state->ucs_buffer_head];
2702b3f6d66SOleksandr Tymoshenko 		state->ucs_buffer_head = (state->ucs_buffer_head + 1) %
2712b3f6d66SOleksandr Tymoshenko 		    UINPUT_BUFFER_SIZE;
2722b3f6d66SOleksandr Tymoshenko 		remaining--;
2732b3f6d66SOleksandr Tymoshenko 		ret = uiomove(event, sizeof(struct input_event), uio);
2742b3f6d66SOleksandr Tymoshenko 	}
2752b3f6d66SOleksandr Tymoshenko 
2762b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
2772b3f6d66SOleksandr Tymoshenko 
2782b3f6d66SOleksandr Tymoshenko 	return (ret);
2792b3f6d66SOleksandr Tymoshenko }
2802b3f6d66SOleksandr Tymoshenko 
2812b3f6d66SOleksandr Tymoshenko static int
uinput_write(struct cdev * dev,struct uio * uio,int ioflag)2822b3f6d66SOleksandr Tymoshenko uinput_write(struct cdev *dev, struct uio *uio, int ioflag)
2832b3f6d66SOleksandr Tymoshenko {
2842b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
2852b3f6d66SOleksandr Tymoshenko 	struct uinput_user_dev userdev;
2862b3f6d66SOleksandr Tymoshenko 	struct input_event event;
2872b3f6d66SOleksandr Tymoshenko 	int ret = 0;
2882b3f6d66SOleksandr Tymoshenko 
2892b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
2902b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
2912b3f6d66SOleksandr Tymoshenko 		return (ret);
2922b3f6d66SOleksandr Tymoshenko 
2932b3f6d66SOleksandr Tymoshenko 	debugf(state, "write %zd bytes by thread %d", uio->uio_resid,
2942b3f6d66SOleksandr Tymoshenko 	    uio->uio_td->td_tid);
2952b3f6d66SOleksandr Tymoshenko 
2962b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
2972b3f6d66SOleksandr Tymoshenko 
2982b3f6d66SOleksandr Tymoshenko 	if (state->ucs_state != UINPUT_RUNNING) {
2992b3f6d66SOleksandr Tymoshenko 		/* Process written struct uinput_user_dev */
3002b3f6d66SOleksandr Tymoshenko 		if (uio->uio_resid != sizeof(struct uinput_user_dev)) {
3012b3f6d66SOleksandr Tymoshenko 			debugf(state, "write size not multiple of "
3022b3f6d66SOleksandr Tymoshenko 			    "struct uinput_user_dev size");
3032b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
3042b3f6d66SOleksandr Tymoshenko 		} else {
3052b3f6d66SOleksandr Tymoshenko 			ret = uiomove(&userdev, sizeof(struct uinput_user_dev),
3062b3f6d66SOleksandr Tymoshenko 			    uio);
3072b3f6d66SOleksandr Tymoshenko 			if (ret == 0)
3082b3f6d66SOleksandr Tymoshenko 				uinput_setup_provider(state, &userdev);
3092b3f6d66SOleksandr Tymoshenko 		}
3102b3f6d66SOleksandr Tymoshenko 	} else {
3112b3f6d66SOleksandr Tymoshenko 		/* Process written event */
3122b3f6d66SOleksandr Tymoshenko 		if (uio->uio_resid % sizeof(struct input_event) != 0) {
3132b3f6d66SOleksandr Tymoshenko 			debugf(state, "write size not multiple of "
3142b3f6d66SOleksandr Tymoshenko 			    "struct input_event size");
3152b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
3162b3f6d66SOleksandr Tymoshenko 		}
3172b3f6d66SOleksandr Tymoshenko 
3182b3f6d66SOleksandr Tymoshenko 		while (ret == 0 && uio->uio_resid > 0) {
3192b3f6d66SOleksandr Tymoshenko 			uiomove(&event, sizeof(struct input_event), uio);
3202b3f6d66SOleksandr Tymoshenko 			ret = evdev_push_event(state->ucs_evdev, event.type,
3212b3f6d66SOleksandr Tymoshenko 			    event.code, event.value);
3222b3f6d66SOleksandr Tymoshenko 		}
3232b3f6d66SOleksandr Tymoshenko 	}
3242b3f6d66SOleksandr Tymoshenko 
3252b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
3262b3f6d66SOleksandr Tymoshenko 
3272b3f6d66SOleksandr Tymoshenko 	return (ret);
3282b3f6d66SOleksandr Tymoshenko }
3292b3f6d66SOleksandr Tymoshenko 
3302b3f6d66SOleksandr Tymoshenko static int
uinput_setup_dev(struct uinput_cdev_state * state,struct input_id * id,char * name,uint32_t ff_effects_max)3312b3f6d66SOleksandr Tymoshenko uinput_setup_dev(struct uinput_cdev_state *state, struct input_id *id,
3322b3f6d66SOleksandr Tymoshenko     char *name, uint32_t ff_effects_max)
3332b3f6d66SOleksandr Tymoshenko {
3342b3f6d66SOleksandr Tymoshenko 
3352b3f6d66SOleksandr Tymoshenko 	if (name[0] == 0)
3362b3f6d66SOleksandr Tymoshenko 		return (EINVAL);
3372b3f6d66SOleksandr Tymoshenko 
3382b3f6d66SOleksandr Tymoshenko 	evdev_set_name(state->ucs_evdev, name);
3392b3f6d66SOleksandr Tymoshenko 	evdev_set_id(state->ucs_evdev, id->bustype, id->vendor, id->product,
3402b3f6d66SOleksandr Tymoshenko 	    id->version);
3412b3f6d66SOleksandr Tymoshenko 	state->ucs_state = UINPUT_CONFIGURED;
3422b3f6d66SOleksandr Tymoshenko 
3432b3f6d66SOleksandr Tymoshenko 	return (0);
3442b3f6d66SOleksandr Tymoshenko }
3452b3f6d66SOleksandr Tymoshenko 
3462b3f6d66SOleksandr Tymoshenko static int
uinput_setup_provider(struct uinput_cdev_state * state,struct uinput_user_dev * udev)3472b3f6d66SOleksandr Tymoshenko uinput_setup_provider(struct uinput_cdev_state *state,
3482b3f6d66SOleksandr Tymoshenko     struct uinput_user_dev *udev)
3492b3f6d66SOleksandr Tymoshenko {
3502b3f6d66SOleksandr Tymoshenko 	struct input_absinfo absinfo;
3512b3f6d66SOleksandr Tymoshenko 	int i, ret;
3522b3f6d66SOleksandr Tymoshenko 
3532b3f6d66SOleksandr Tymoshenko 	debugf(state, "setup_provider called, udev=%p", udev);
3542b3f6d66SOleksandr Tymoshenko 
3552b3f6d66SOleksandr Tymoshenko 	ret = uinput_setup_dev(state, &udev->id, udev->name,
3562b3f6d66SOleksandr Tymoshenko 	    udev->ff_effects_max);
3572b3f6d66SOleksandr Tymoshenko 	if (ret)
3582b3f6d66SOleksandr Tymoshenko 		return (ret);
3592b3f6d66SOleksandr Tymoshenko 
3602b3f6d66SOleksandr Tymoshenko 	bzero(&absinfo, sizeof(struct input_absinfo));
3612b3f6d66SOleksandr Tymoshenko 	for (i = 0; i < ABS_CNT; i++) {
3622b3f6d66SOleksandr Tymoshenko 		if (!bit_test(state->ucs_evdev->ev_abs_flags, i))
3632b3f6d66SOleksandr Tymoshenko 			continue;
3642b3f6d66SOleksandr Tymoshenko 
3652b3f6d66SOleksandr Tymoshenko 		absinfo.minimum = udev->absmin[i];
3662b3f6d66SOleksandr Tymoshenko 		absinfo.maximum = udev->absmax[i];
3672b3f6d66SOleksandr Tymoshenko 		absinfo.fuzz = udev->absfuzz[i];
3682b3f6d66SOleksandr Tymoshenko 		absinfo.flat = udev->absflat[i];
3692b3f6d66SOleksandr Tymoshenko 		evdev_set_absinfo(state->ucs_evdev, i, &absinfo);
3702b3f6d66SOleksandr Tymoshenko 	}
3712b3f6d66SOleksandr Tymoshenko 
3722b3f6d66SOleksandr Tymoshenko 	return (0);
3732b3f6d66SOleksandr Tymoshenko }
3742b3f6d66SOleksandr Tymoshenko 
3752b3f6d66SOleksandr Tymoshenko static int
uinput_poll(struct cdev * dev,int events,struct thread * td)3762b3f6d66SOleksandr Tymoshenko uinput_poll(struct cdev *dev, int events, struct thread *td)
3772b3f6d66SOleksandr Tymoshenko {
3782b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
3792b3f6d66SOleksandr Tymoshenko 	int revents = 0;
3802b3f6d66SOleksandr Tymoshenko 
3812b3f6d66SOleksandr Tymoshenko 	if (devfs_get_cdevpriv((void **)&state) != 0)
3822b3f6d66SOleksandr Tymoshenko 		return (POLLNVAL);
3832b3f6d66SOleksandr Tymoshenko 
3842b3f6d66SOleksandr Tymoshenko 	debugf(state, "poll by thread %d", td->td_tid);
3852b3f6d66SOleksandr Tymoshenko 
3862b3f6d66SOleksandr Tymoshenko 	/* Always allow write */
3872b3f6d66SOleksandr Tymoshenko 	if (events & (POLLOUT | POLLWRNORM))
3882b3f6d66SOleksandr Tymoshenko 		revents |= (events & (POLLOUT | POLLWRNORM));
3892b3f6d66SOleksandr Tymoshenko 
3902b3f6d66SOleksandr Tymoshenko 	if (events & (POLLIN | POLLRDNORM)) {
3912b3f6d66SOleksandr Tymoshenko 		UINPUT_LOCK(state);
3922b3f6d66SOleksandr Tymoshenko 		if (!UINPUT_EMPTYQ(state))
3932b3f6d66SOleksandr Tymoshenko 			revents = events & (POLLIN | POLLRDNORM);
3942b3f6d66SOleksandr Tymoshenko 		else {
3952b3f6d66SOleksandr Tymoshenko 			state->ucs_selected = true;
3962b3f6d66SOleksandr Tymoshenko 			selrecord(td, &state->ucs_selp);
3972b3f6d66SOleksandr Tymoshenko 		}
3982b3f6d66SOleksandr Tymoshenko 		UINPUT_UNLOCK(state);
3992b3f6d66SOleksandr Tymoshenko 	}
4002b3f6d66SOleksandr Tymoshenko 
4012b3f6d66SOleksandr Tymoshenko 	return (revents);
4022b3f6d66SOleksandr Tymoshenko }
4032b3f6d66SOleksandr Tymoshenko 
4042b3f6d66SOleksandr Tymoshenko static int
uinput_kqfilter(struct cdev * dev,struct knote * kn)4052b3f6d66SOleksandr Tymoshenko uinput_kqfilter(struct cdev *dev, struct knote *kn)
4062b3f6d66SOleksandr Tymoshenko {
4072b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4082b3f6d66SOleksandr Tymoshenko 	int ret;
4092b3f6d66SOleksandr Tymoshenko 
4102b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
4112b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
4122b3f6d66SOleksandr Tymoshenko 		return (ret);
4132b3f6d66SOleksandr Tymoshenko 
4142b3f6d66SOleksandr Tymoshenko 	switch(kn->kn_filter) {
4152b3f6d66SOleksandr Tymoshenko 	case EVFILT_READ:
4162b3f6d66SOleksandr Tymoshenko 		kn->kn_fop = &uinput_filterops;
4172b3f6d66SOleksandr Tymoshenko 		break;
4182b3f6d66SOleksandr Tymoshenko 	default:
4192b3f6d66SOleksandr Tymoshenko 		return(EINVAL);
4202b3f6d66SOleksandr Tymoshenko 	}
4212b3f6d66SOleksandr Tymoshenko 	kn->kn_hook = (caddr_t)state;
4222b3f6d66SOleksandr Tymoshenko 
4232b3f6d66SOleksandr Tymoshenko 	knlist_add(&state->ucs_selp.si_note, kn, 0);
4242b3f6d66SOleksandr Tymoshenko 	return (0);
4252b3f6d66SOleksandr Tymoshenko }
4262b3f6d66SOleksandr Tymoshenko 
4272b3f6d66SOleksandr Tymoshenko static int
uinput_kqread(struct knote * kn,long hint)4282b3f6d66SOleksandr Tymoshenko uinput_kqread(struct knote *kn, long hint)
4292b3f6d66SOleksandr Tymoshenko {
4302b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4312b3f6d66SOleksandr Tymoshenko 	int ret;
4322b3f6d66SOleksandr Tymoshenko 
4332b3f6d66SOleksandr Tymoshenko 	state = (struct uinput_cdev_state *)kn->kn_hook;
4342b3f6d66SOleksandr Tymoshenko 
4352b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4362b3f6d66SOleksandr Tymoshenko 
4372b3f6d66SOleksandr Tymoshenko 	ret = !UINPUT_EMPTYQ(state);
4382b3f6d66SOleksandr Tymoshenko 	return (ret);
4392b3f6d66SOleksandr Tymoshenko }
4402b3f6d66SOleksandr Tymoshenko 
4412b3f6d66SOleksandr Tymoshenko static void
uinput_kqdetach(struct knote * kn)4422b3f6d66SOleksandr Tymoshenko uinput_kqdetach(struct knote *kn)
4432b3f6d66SOleksandr Tymoshenko {
4442b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
4452b3f6d66SOleksandr Tymoshenko 
4462b3f6d66SOleksandr Tymoshenko 	state = (struct uinput_cdev_state *)kn->kn_hook;
4472b3f6d66SOleksandr Tymoshenko 	knlist_remove(&state->ucs_selp.si_note, kn, 0);
4482b3f6d66SOleksandr Tymoshenko }
4492b3f6d66SOleksandr Tymoshenko 
4502b3f6d66SOleksandr Tymoshenko static void
uinput_notify(struct uinput_cdev_state * state)4512b3f6d66SOleksandr Tymoshenko uinput_notify(struct uinput_cdev_state *state)
4522b3f6d66SOleksandr Tymoshenko {
4532b3f6d66SOleksandr Tymoshenko 
4542b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4552b3f6d66SOleksandr Tymoshenko 
4562b3f6d66SOleksandr Tymoshenko 	if (state->ucs_blocked) {
4572b3f6d66SOleksandr Tymoshenko 		state->ucs_blocked = false;
4582b3f6d66SOleksandr Tymoshenko 		wakeup(state);
4592b3f6d66SOleksandr Tymoshenko 	}
4602b3f6d66SOleksandr Tymoshenko 	if (state->ucs_selected) {
4612b3f6d66SOleksandr Tymoshenko 		state->ucs_selected = false;
4622b3f6d66SOleksandr Tymoshenko 		selwakeup(&state->ucs_selp);
4632b3f6d66SOleksandr Tymoshenko 	}
4642b3f6d66SOleksandr Tymoshenko 	KNOTE_LOCKED(&state->ucs_selp.si_note, 0);
4652b3f6d66SOleksandr Tymoshenko }
4662b3f6d66SOleksandr Tymoshenko 
4672b3f6d66SOleksandr Tymoshenko static int
uinput_ioctl_sub(struct uinput_cdev_state * state,u_long cmd,caddr_t data)4682b3f6d66SOleksandr Tymoshenko uinput_ioctl_sub(struct uinput_cdev_state *state, u_long cmd, caddr_t data)
4692b3f6d66SOleksandr Tymoshenko {
4702b3f6d66SOleksandr Tymoshenko 	struct uinput_setup *us;
4712b3f6d66SOleksandr Tymoshenko 	struct uinput_abs_setup *uabs;
4722b3f6d66SOleksandr Tymoshenko 	int ret, len, intdata;
4732b3f6d66SOleksandr Tymoshenko 	char buf[NAMELEN];
4742b3f6d66SOleksandr Tymoshenko 
4752b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK_ASSERT(state);
4762b3f6d66SOleksandr Tymoshenko 
4772b3f6d66SOleksandr Tymoshenko 	len = IOCPARM_LEN(cmd);
4782b3f6d66SOleksandr Tymoshenko 	if ((cmd & IOC_DIRMASK) == IOC_VOID && len == sizeof(int))
4792b3f6d66SOleksandr Tymoshenko 		intdata = *(int *)data;
4802b3f6d66SOleksandr Tymoshenko 
4812b3f6d66SOleksandr Tymoshenko 	switch (IOCBASECMD(cmd)) {
4822b3f6d66SOleksandr Tymoshenko 	case UI_GET_SYSNAME(0):
4832b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_RUNNING)
4842b3f6d66SOleksandr Tymoshenko 			return (ENOENT);
4852b3f6d66SOleksandr Tymoshenko 		if (len == 0)
4862b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
4872b3f6d66SOleksandr Tymoshenko 		snprintf(data, len, "event%d", state->ucs_evdev->ev_unit);
4882b3f6d66SOleksandr Tymoshenko 		return (0);
4892b3f6d66SOleksandr Tymoshenko 	}
4902b3f6d66SOleksandr Tymoshenko 
4912b3f6d66SOleksandr Tymoshenko 	switch (cmd) {
4922b3f6d66SOleksandr Tymoshenko 	case UI_DEV_CREATE:
4932b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_CONFIGURED)
4942b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
4952b3f6d66SOleksandr Tymoshenko 
4962b3f6d66SOleksandr Tymoshenko 		evdev_set_methods(state->ucs_evdev, state, &uinput_ev_methods);
4972b3f6d66SOleksandr Tymoshenko 		evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_SOFTREPEAT);
498*f160aa0dSVladimir Kondratyev 		evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_MT_KEEPID);
499670aa764SOleksandr Tymoshenko 		ret = evdev_register(state->ucs_evdev);
500670aa764SOleksandr Tymoshenko 		if (ret == 0)
5012b3f6d66SOleksandr Tymoshenko 			state->ucs_state = UINPUT_RUNNING;
502670aa764SOleksandr Tymoshenko 		return (ret);
5032b3f6d66SOleksandr Tymoshenko 
5042b3f6d66SOleksandr Tymoshenko 	case UI_DEV_DESTROY:
5052b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state != UINPUT_RUNNING)
5062b3f6d66SOleksandr Tymoshenko 			return (0);
5072b3f6d66SOleksandr Tymoshenko 
5082b3f6d66SOleksandr Tymoshenko 		evdev_unregister(state->ucs_evdev);
5092b3f6d66SOleksandr Tymoshenko 		bzero(state->ucs_evdev, sizeof(struct evdev_dev));
5102b3f6d66SOleksandr Tymoshenko 		state->ucs_state = UINPUT_NEW;
5112b3f6d66SOleksandr Tymoshenko 		return (0);
5122b3f6d66SOleksandr Tymoshenko 
5132b3f6d66SOleksandr Tymoshenko 	case UI_DEV_SETUP:
5142b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5152b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5162b3f6d66SOleksandr Tymoshenko 
5172b3f6d66SOleksandr Tymoshenko 		us = (struct uinput_setup *)data;
5182b3f6d66SOleksandr Tymoshenko 		return (uinput_setup_dev(state, &us->id, us->name,
5192b3f6d66SOleksandr Tymoshenko 		    us->ff_effects_max));
5202b3f6d66SOleksandr Tymoshenko 
5212b3f6d66SOleksandr Tymoshenko 	case UI_ABS_SETUP:
5222b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5232b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5242b3f6d66SOleksandr Tymoshenko 
5252b3f6d66SOleksandr Tymoshenko 		uabs = (struct uinput_abs_setup *)data;
526886f6623SOleksandr Tymoshenko 		if (uabs->code > ABS_MAX)
5272b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5282b3f6d66SOleksandr Tymoshenko 
5295af73ad5SVladimir Kondratyev 		evdev_set_abs_bit(state->ucs_evdev, uabs->code);
5305af73ad5SVladimir Kondratyev 		evdev_set_absinfo(state->ucs_evdev, uabs->code,
5315af73ad5SVladimir Kondratyev 		    &uabs->absinfo);
5322b3f6d66SOleksandr Tymoshenko 		return (0);
5332b3f6d66SOleksandr Tymoshenko 
5342b3f6d66SOleksandr Tymoshenko 	case UI_SET_EVBIT:
5352b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5362b3f6d66SOleksandr Tymoshenko 		    intdata > EV_MAX || intdata < 0)
5372b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5382b3f6d66SOleksandr Tymoshenko 		evdev_support_event(state->ucs_evdev, intdata);
5392b3f6d66SOleksandr Tymoshenko 		return (0);
5402b3f6d66SOleksandr Tymoshenko 
5412b3f6d66SOleksandr Tymoshenko 	case UI_SET_KEYBIT:
5422b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5432b3f6d66SOleksandr Tymoshenko 		    intdata > KEY_MAX || intdata < 0)
5442b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5452b3f6d66SOleksandr Tymoshenko 		evdev_support_key(state->ucs_evdev, intdata);
5462b3f6d66SOleksandr Tymoshenko 		return (0);
5472b3f6d66SOleksandr Tymoshenko 
5482b3f6d66SOleksandr Tymoshenko 	case UI_SET_RELBIT:
5492b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5502b3f6d66SOleksandr Tymoshenko 		    intdata > REL_MAX || intdata < 0)
5512b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5522b3f6d66SOleksandr Tymoshenko 		evdev_support_rel(state->ucs_evdev, intdata);
5532b3f6d66SOleksandr Tymoshenko 		return (0);
5542b3f6d66SOleksandr Tymoshenko 
5552b3f6d66SOleksandr Tymoshenko 	case UI_SET_ABSBIT:
5562b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5572b3f6d66SOleksandr Tymoshenko 		    intdata > ABS_MAX || intdata < 0)
5582b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5592b3f6d66SOleksandr Tymoshenko 		evdev_set_abs_bit(state->ucs_evdev, intdata);
5602b3f6d66SOleksandr Tymoshenko 		return (0);
5612b3f6d66SOleksandr Tymoshenko 
5622b3f6d66SOleksandr Tymoshenko 	case UI_SET_MSCBIT:
5632b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5642b3f6d66SOleksandr Tymoshenko 		    intdata > MSC_MAX || intdata < 0)
5652b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5662b3f6d66SOleksandr Tymoshenko 		evdev_support_msc(state->ucs_evdev, intdata);
5672b3f6d66SOleksandr Tymoshenko 		return (0);
5682b3f6d66SOleksandr Tymoshenko 
5692b3f6d66SOleksandr Tymoshenko 	case UI_SET_LEDBIT:
5702b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5712b3f6d66SOleksandr Tymoshenko 		    intdata > LED_MAX || intdata < 0)
5722b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5732b3f6d66SOleksandr Tymoshenko 		evdev_support_led(state->ucs_evdev, intdata);
5742b3f6d66SOleksandr Tymoshenko 		return (0);
5752b3f6d66SOleksandr Tymoshenko 
5762b3f6d66SOleksandr Tymoshenko 	case UI_SET_SNDBIT:
5772b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5782b3f6d66SOleksandr Tymoshenko 		    intdata > SND_MAX || intdata < 0)
5792b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5802b3f6d66SOleksandr Tymoshenko 		evdev_support_snd(state->ucs_evdev, intdata);
5812b3f6d66SOleksandr Tymoshenko 		return (0);
5822b3f6d66SOleksandr Tymoshenko 
5832b3f6d66SOleksandr Tymoshenko 	case UI_SET_FFBIT:
5842b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
5852b3f6d66SOleksandr Tymoshenko 		    intdata > FF_MAX || intdata < 0)
5862b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5872b3f6d66SOleksandr Tymoshenko 		/* Fake unsupported ioctl */
5882b3f6d66SOleksandr Tymoshenko 		return (0);
5892b3f6d66SOleksandr Tymoshenko 
5902b3f6d66SOleksandr Tymoshenko 	case UI_SET_PHYS:
5912b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
5922b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
5932b3f6d66SOleksandr Tymoshenko 		ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
5942b3f6d66SOleksandr Tymoshenko 		/* Linux returns EINVAL when string does not fit the buffer */
5952b3f6d66SOleksandr Tymoshenko 		if (ret == ENAMETOOLONG)
5962b3f6d66SOleksandr Tymoshenko 			ret = EINVAL;
5972b3f6d66SOleksandr Tymoshenko 		if (ret != 0)
5982b3f6d66SOleksandr Tymoshenko 			return (ret);
5992b3f6d66SOleksandr Tymoshenko 		evdev_set_phys(state->ucs_evdev, buf);
6002b3f6d66SOleksandr Tymoshenko 		return (0);
6012b3f6d66SOleksandr Tymoshenko 
6024b58fa12SVladimir Kondratyev 	case UI_SET_BSDUNIQ:
6034b58fa12SVladimir Kondratyev 		if (state->ucs_state == UINPUT_RUNNING)
6044b58fa12SVladimir Kondratyev 			return (EINVAL);
6054b58fa12SVladimir Kondratyev 		ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
6064b58fa12SVladimir Kondratyev 		if (ret != 0)
6074b58fa12SVladimir Kondratyev 			return (ret);
6084b58fa12SVladimir Kondratyev 		evdev_set_serial(state->ucs_evdev, buf);
6094b58fa12SVladimir Kondratyev 		return (0);
6104b58fa12SVladimir Kondratyev 
6112b3f6d66SOleksandr Tymoshenko 	case UI_SET_SWBIT:
6122b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
6132b3f6d66SOleksandr Tymoshenko 		    intdata > SW_MAX || intdata < 0)
6142b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6152b3f6d66SOleksandr Tymoshenko 		evdev_support_sw(state->ucs_evdev, intdata);
6162b3f6d66SOleksandr Tymoshenko 		return (0);
6172b3f6d66SOleksandr Tymoshenko 
6182b3f6d66SOleksandr Tymoshenko 	case UI_SET_PROPBIT:
6192b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING ||
6202b3f6d66SOleksandr Tymoshenko 		    intdata > INPUT_PROP_MAX || intdata < 0)
6212b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6222b3f6d66SOleksandr Tymoshenko 		evdev_support_prop(state->ucs_evdev, intdata);
6232b3f6d66SOleksandr Tymoshenko 		return (0);
6242b3f6d66SOleksandr Tymoshenko 
6252b3f6d66SOleksandr Tymoshenko 	case UI_BEGIN_FF_UPLOAD:
6262b3f6d66SOleksandr Tymoshenko 	case UI_END_FF_UPLOAD:
6272b3f6d66SOleksandr Tymoshenko 	case UI_BEGIN_FF_ERASE:
6282b3f6d66SOleksandr Tymoshenko 	case UI_END_FF_ERASE:
6292b3f6d66SOleksandr Tymoshenko 		if (state->ucs_state == UINPUT_RUNNING)
6302b3f6d66SOleksandr Tymoshenko 			return (EINVAL);
6312b3f6d66SOleksandr Tymoshenko 		/* Fake unsupported ioctl */
6322b3f6d66SOleksandr Tymoshenko 		return (0);
6332b3f6d66SOleksandr Tymoshenko 
6342b3f6d66SOleksandr Tymoshenko 	case UI_GET_VERSION:
6352b3f6d66SOleksandr Tymoshenko 		*(unsigned int *)data = UINPUT_VERSION;
6362b3f6d66SOleksandr Tymoshenko 		return (0);
6372b3f6d66SOleksandr Tymoshenko 	}
6382b3f6d66SOleksandr Tymoshenko 
6392b3f6d66SOleksandr Tymoshenko 	return (EINVAL);
6402b3f6d66SOleksandr Tymoshenko }
6412b3f6d66SOleksandr Tymoshenko 
6422b3f6d66SOleksandr Tymoshenko static int
uinput_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)6432b3f6d66SOleksandr Tymoshenko uinput_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
6442b3f6d66SOleksandr Tymoshenko     struct thread *td)
6452b3f6d66SOleksandr Tymoshenko {
6462b3f6d66SOleksandr Tymoshenko 	struct uinput_cdev_state *state;
6472b3f6d66SOleksandr Tymoshenko 	int ret;
6482b3f6d66SOleksandr Tymoshenko 
6492b3f6d66SOleksandr Tymoshenko 	ret = devfs_get_cdevpriv((void **)&state);
6502b3f6d66SOleksandr Tymoshenko 	if (ret != 0)
6512b3f6d66SOleksandr Tymoshenko 		return (ret);
6522b3f6d66SOleksandr Tymoshenko 
6532b3f6d66SOleksandr Tymoshenko 	debugf(state, "ioctl called: cmd=0x%08lx, data=%p", cmd, data);
6542b3f6d66SOleksandr Tymoshenko 
6552b3f6d66SOleksandr Tymoshenko 	UINPUT_LOCK(state);
6562b3f6d66SOleksandr Tymoshenko 	ret = uinput_ioctl_sub(state, cmd, data);
6572b3f6d66SOleksandr Tymoshenko 	UINPUT_UNLOCK(state);
6582b3f6d66SOleksandr Tymoshenko 
6592b3f6d66SOleksandr Tymoshenko 	return (ret);
6602b3f6d66SOleksandr Tymoshenko }
6612b3f6d66SOleksandr Tymoshenko 
6622b3f6d66SOleksandr Tymoshenko static int
uinput_cdev_create(void)6632b3f6d66SOleksandr Tymoshenko uinput_cdev_create(void)
6642b3f6d66SOleksandr Tymoshenko {
6652b3f6d66SOleksandr Tymoshenko 	struct make_dev_args mda;
6662b3f6d66SOleksandr Tymoshenko 	int ret;
6672b3f6d66SOleksandr Tymoshenko 
6682b3f6d66SOleksandr Tymoshenko 	make_dev_args_init(&mda);
6692b3f6d66SOleksandr Tymoshenko 	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
6702b3f6d66SOleksandr Tymoshenko 	mda.mda_devsw = &uinput_cdevsw;
6712b3f6d66SOleksandr Tymoshenko 	mda.mda_uid = UID_ROOT;
6722b3f6d66SOleksandr Tymoshenko 	mda.mda_gid = GID_WHEEL;
6732b3f6d66SOleksandr Tymoshenko 	mda.mda_mode = 0600;
6742b3f6d66SOleksandr Tymoshenko 
6752b3f6d66SOleksandr Tymoshenko 	ret = make_dev_s(&mda, &uinput_cdev, "uinput");
6762b3f6d66SOleksandr Tymoshenko 
6772b3f6d66SOleksandr Tymoshenko 	return (ret);
6782b3f6d66SOleksandr Tymoshenko }
6792b3f6d66SOleksandr Tymoshenko 
6802b3f6d66SOleksandr Tymoshenko static int
uinput_cdev_destroy(void)6812b3f6d66SOleksandr Tymoshenko uinput_cdev_destroy(void)
6822b3f6d66SOleksandr Tymoshenko {
6832b3f6d66SOleksandr Tymoshenko 
6842b3f6d66SOleksandr Tymoshenko 	destroy_dev(uinput_cdev);
6852b3f6d66SOleksandr Tymoshenko 
6862b3f6d66SOleksandr Tymoshenko 	return (0);
6872b3f6d66SOleksandr Tymoshenko }
6882b3f6d66SOleksandr Tymoshenko 
6892b3f6d66SOleksandr Tymoshenko static int
uinput_modevent(module_t mod __unused,int cmd,void * data)6902b3f6d66SOleksandr Tymoshenko uinput_modevent(module_t mod __unused, int cmd, void *data)
6912b3f6d66SOleksandr Tymoshenko {
6922b3f6d66SOleksandr Tymoshenko 	int ret = 0;
6932b3f6d66SOleksandr Tymoshenko 
6942b3f6d66SOleksandr Tymoshenko 	switch (cmd) {
6952b3f6d66SOleksandr Tymoshenko 	case MOD_LOAD:
6962b3f6d66SOleksandr Tymoshenko 		ret = uinput_cdev_create();
6972b3f6d66SOleksandr Tymoshenko 		break;
6982b3f6d66SOleksandr Tymoshenko 
6992b3f6d66SOleksandr Tymoshenko 	case MOD_UNLOAD:
7002b3f6d66SOleksandr Tymoshenko 		ret = uinput_cdev_destroy();
7012b3f6d66SOleksandr Tymoshenko 		break;
7022b3f6d66SOleksandr Tymoshenko 
7032b3f6d66SOleksandr Tymoshenko 	case MOD_SHUTDOWN:
7042b3f6d66SOleksandr Tymoshenko 		break;
7052b3f6d66SOleksandr Tymoshenko 
7062b3f6d66SOleksandr Tymoshenko 	default:
7072b3f6d66SOleksandr Tymoshenko 		ret = EINVAL;
7082b3f6d66SOleksandr Tymoshenko 		break;
7092b3f6d66SOleksandr Tymoshenko 	}
7102b3f6d66SOleksandr Tymoshenko 
7112b3f6d66SOleksandr Tymoshenko 	return (ret);
7122b3f6d66SOleksandr Tymoshenko }
7132b3f6d66SOleksandr Tymoshenko 
7142b3f6d66SOleksandr Tymoshenko DEV_MODULE(uinput, uinput_modevent, NULL);
715a6b15a34SOleksandr Tymoshenko MODULE_VERSION(uinput, 1);
716a6b15a34SOleksandr Tymoshenko MODULE_DEPEND(uinput, evdev, 1, 1, 1);
717