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