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