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