Index: stable/11/sys/dev/evdev/cdev.c =================================================================== --- stable/11/sys/dev/evdev/cdev.c (revision 317988) +++ stable/11/sys/dev/evdev/cdev.c (revision 317989) @@ -1,860 +1,861 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama * Copyright (c) 2015-2016 Vladimir Kondratyev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_evdev.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EVDEV_DEBUG #define debugf(client, fmt, args...) printf("evdev cdev: "fmt"\n", ##args) #else #define debugf(client, fmt, args...) #endif #define DEF_RING_REPORTS 8 static d_open_t evdev_open; static d_read_t evdev_read; static d_write_t evdev_write; static d_ioctl_t evdev_ioctl; static d_poll_t evdev_poll; static d_kqfilter_t evdev_kqfilter; static int evdev_kqread(struct knote *kn, long hint); static void evdev_kqdetach(struct knote *kn); static void evdev_dtor(void *); static int evdev_ioctl_eviocgbit(struct evdev_dev *, int, int, caddr_t); static void evdev_client_filter_queue(struct evdev_client *, uint16_t); static struct cdevsw evdev_cdevsw = { .d_version = D_VERSION, .d_open = evdev_open, .d_read = evdev_read, .d_write = evdev_write, .d_ioctl = evdev_ioctl, .d_poll = evdev_poll, .d_kqfilter = evdev_kqfilter, .d_name = "evdev", }; static struct filterops evdev_cdev_filterops = { .f_isfd = 1, .f_attach = NULL, .f_detach = evdev_kqdetach, .f_event = evdev_kqread, }; static int evdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct evdev_dev *evdev = dev->si_drv1; struct evdev_client *client; size_t buffer_size; int ret; if (evdev == NULL) return (ENODEV); /* Initialize client structure */ buffer_size = evdev->ev_report_size * DEF_RING_REPORTS; client = malloc(offsetof(struct evdev_client, ec_buffer) + sizeof(struct input_event) * buffer_size, M_EVDEV, M_WAITOK | M_ZERO); /* Initialize ring buffer */ client->ec_buffer_size = buffer_size; client->ec_buffer_head = 0; client->ec_buffer_tail = 0; client->ec_buffer_ready = 0; client->ec_evdev = evdev; mtx_init(&client->ec_buffer_mtx, "evclient", "evdev", MTX_DEF); knlist_init_mtx(&client->ec_selp.si_note, &client->ec_buffer_mtx); /* Avoid race with evdev_unregister */ EVDEV_LOCK(evdev); if (dev->si_drv1 == NULL) ret = ENODEV; else ret = evdev_register_client(evdev, client); if (ret != 0) evdev_revoke_client(client); /* * Unlock evdev here because non-sleepable lock held * while calling devfs_set_cdevpriv upsets WITNESS */ EVDEV_UNLOCK(evdev); if (!ret) ret = devfs_set_cdevpriv(client, evdev_dtor); if (ret != 0) { debugf(client, "cannot register evdev client"); evdev_dtor(client); } return (ret); } static void evdev_dtor(void *data) { struct evdev_client *client = (struct evdev_client *)data; EVDEV_LOCK(client->ec_evdev); if (!client->ec_revoked) evdev_dispose_client(client->ec_evdev, client); EVDEV_UNLOCK(client->ec_evdev); knlist_clear(&client->ec_selp.si_note, 0); seldrain(&client->ec_selp); knlist_destroy(&client->ec_selp.si_note); funsetown(&client->ec_sigio); mtx_destroy(&client->ec_buffer_mtx); free(client, M_EVDEV); } static int evdev_read(struct cdev *dev, struct uio *uio, int ioflag) { struct evdev_client *client; - struct input_event *event; + struct input_event event; int ret = 0; int remaining; ret = devfs_get_cdevpriv((void **)&client); if (ret != 0) return (ret); debugf(client, "read %zd bytes by thread %d", uio->uio_resid, uio->uio_td->td_tid); if (client->ec_revoked) return (ENODEV); /* Zero-sized reads are allowed for error checking */ if (uio->uio_resid != 0 && uio->uio_resid < sizeof(struct input_event)) return (EINVAL); remaining = uio->uio_resid / sizeof(struct input_event); EVDEV_CLIENT_LOCKQ(client); if (EVDEV_CLIENT_EMPTYQ(client)) { if (ioflag & O_NONBLOCK) ret = EWOULDBLOCK; else { if (remaining != 0) { client->ec_blocked = true; ret = mtx_sleep(client, &client->ec_buffer_mtx, PCATCH, "evread", 0); } } } while (ret == 0 && !EVDEV_CLIENT_EMPTYQ(client) && remaining > 0) { - event = &client->ec_buffer[client->ec_buffer_head]; + memcpy(&event, &client->ec_buffer[client->ec_buffer_head], + sizeof(struct input_event)); client->ec_buffer_head = (client->ec_buffer_head + 1) % client->ec_buffer_size; remaining--; EVDEV_CLIENT_UNLOCKQ(client); - ret = uiomove(event, sizeof(struct input_event), uio); + ret = uiomove(&event, sizeof(struct input_event), uio); EVDEV_CLIENT_LOCKQ(client); } EVDEV_CLIENT_UNLOCKQ(client); return (ret); } static int evdev_write(struct cdev *dev, struct uio *uio, int ioflag) { struct evdev_dev *evdev = dev->si_drv1; struct evdev_client *client; struct input_event event; int ret = 0; ret = devfs_get_cdevpriv((void **)&client); if (ret != 0) return (ret); debugf(client, "write %zd bytes by thread %d", uio->uio_resid, uio->uio_td->td_tid); if (client->ec_revoked || evdev == NULL) return (ENODEV); if (uio->uio_resid % sizeof(struct input_event) != 0) { debugf(client, "write size not multiple of input_event size"); return (EINVAL); } while (uio->uio_resid > 0 && ret == 0) { ret = uiomove(&event, sizeof(struct input_event), uio); if (ret == 0) ret = evdev_inject_event(evdev, event.type, event.code, event.value); } return (ret); } static int evdev_poll(struct cdev *dev, int events, struct thread *td) { struct evdev_client *client; int ret; int revents = 0; ret = devfs_get_cdevpriv((void **)&client); if (ret != 0) return (POLLNVAL); debugf(client, "poll by thread %d", td->td_tid); if (client->ec_revoked) return (POLLHUP); if (events & (POLLIN | POLLRDNORM)) { EVDEV_CLIENT_LOCKQ(client); if (!EVDEV_CLIENT_EMPTYQ(client)) revents = events & (POLLIN | POLLRDNORM); else { client->ec_selected = true; selrecord(td, &client->ec_selp); } EVDEV_CLIENT_UNLOCKQ(client); } return (revents); } static int evdev_kqfilter(struct cdev *dev, struct knote *kn) { struct evdev_client *client; int ret; ret = devfs_get_cdevpriv((void **)&client); if (ret != 0) return (ret); if (client->ec_revoked) return (ENODEV); switch(kn->kn_filter) { case EVFILT_READ: kn->kn_fop = &evdev_cdev_filterops; break; default: return(EINVAL); } kn->kn_hook = (caddr_t)client; knlist_add(&client->ec_selp.si_note, kn, 0); return (0); } static int evdev_kqread(struct knote *kn, long hint) { struct evdev_client *client; int ret; client = (struct evdev_client *)kn->kn_hook; EVDEV_CLIENT_LOCKQ_ASSERT(client); if (client->ec_revoked) { kn->kn_flags |= EV_EOF; ret = 1; } else { kn->kn_data = EVDEV_CLIENT_SIZEQ(client) * sizeof(struct input_event); ret = !EVDEV_CLIENT_EMPTYQ(client); } return (ret); } static void evdev_kqdetach(struct knote *kn) { struct evdev_client *client; client = (struct evdev_client *)kn->kn_hook; knlist_remove(&client->ec_selp.si_note, kn, 0); } static int evdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct evdev_dev *evdev = dev->si_drv1; struct evdev_client *client; struct input_keymap_entry *ke; int ret, len, limit, type_num; uint32_t code; size_t nvalues; ret = devfs_get_cdevpriv((void **)&client); if (ret != 0) return (ret); if (client->ec_revoked || evdev == NULL) return (ENODEV); /* file I/O ioctl handling */ switch (cmd) { case FIOSETOWN: return (fsetown(*(int *)data, &client->ec_sigio)); case FIOGETOWN: *(int *)data = fgetown(&client->ec_sigio); return (0); case FIONBIO: return (0); case FIOASYNC: if (*(int *)data) client->ec_async = true; else client->ec_async = false; return (0); case FIONREAD: EVDEV_CLIENT_LOCKQ(client); *(int *)data = EVDEV_CLIENT_SIZEQ(client) * sizeof(struct input_event); EVDEV_CLIENT_UNLOCKQ(client); return (0); } len = IOCPARM_LEN(cmd); debugf(client, "ioctl called: cmd=0x%08lx, data=%p", cmd, data); /* evdev fixed-length ioctls handling */ switch (cmd) { case EVIOCGVERSION: *(int *)data = EV_VERSION; return (0); case EVIOCGID: debugf(client, "EVIOCGID: bus=%d vendor=0x%04x product=0x%04x", evdev->ev_id.bustype, evdev->ev_id.vendor, evdev->ev_id.product); memcpy(data, &evdev->ev_id, sizeof(struct input_id)); return (0); case EVIOCGREP: if (!evdev_event_supported(evdev, EV_REP)) return (ENOTSUP); memcpy(data, evdev->ev_rep, sizeof(evdev->ev_rep)); return (0); case EVIOCSREP: if (!evdev_event_supported(evdev, EV_REP)) return (ENOTSUP); evdev_inject_event(evdev, EV_REP, REP_DELAY, ((int *)data)[0]); evdev_inject_event(evdev, EV_REP, REP_PERIOD, ((int *)data)[1]); return (0); case EVIOCGKEYCODE: /* Fake unsupported ioctl */ return (0); case EVIOCGKEYCODE_V2: if (evdev->ev_methods == NULL || evdev->ev_methods->ev_get_keycode == NULL) return (ENOTSUP); ke = (struct input_keymap_entry *)data; evdev->ev_methods->ev_get_keycode(evdev, evdev->ev_softc, ke); return (0); case EVIOCSKEYCODE: /* Fake unsupported ioctl */ return (0); case EVIOCSKEYCODE_V2: if (evdev->ev_methods == NULL || evdev->ev_methods->ev_set_keycode == NULL) return (ENOTSUP); ke = (struct input_keymap_entry *)data; evdev->ev_methods->ev_set_keycode(evdev, evdev->ev_softc, ke); return (0); case EVIOCGABS(0) ... EVIOCGABS(ABS_MAX): if (evdev->ev_absinfo == NULL) return (EINVAL); memcpy(data, &evdev->ev_absinfo[cmd - EVIOCGABS(0)], sizeof(struct input_absinfo)); return (0); case EVIOCSABS(0) ... EVIOCSABS(ABS_MAX): if (evdev->ev_absinfo == NULL) return (EINVAL); code = cmd - EVIOCSABS(0); /* mt-slot number can not be changed */ if (code == ABS_MT_SLOT) return (EINVAL); EVDEV_LOCK(evdev); evdev_set_absinfo(evdev, code, (struct input_absinfo *)data); EVDEV_UNLOCK(evdev); return (0); case EVIOCSFF: case EVIOCRMFF: case EVIOCGEFFECTS: /* Fake unsupported ioctls */ return (0); case EVIOCGRAB: EVDEV_LOCK(evdev); if (*(int *)data) ret = evdev_grab_client(evdev, client); else ret = evdev_release_client(evdev, client); EVDEV_UNLOCK(evdev); return (ret); case EVIOCREVOKE: if (*(int *)data != 0) return (EINVAL); EVDEV_LOCK(evdev); if (dev->si_drv1 != NULL && !client->ec_revoked) { evdev_dispose_client(evdev, client); evdev_revoke_client(client); } EVDEV_UNLOCK(evdev); return (0); case EVIOCSCLOCKID: switch (*(int *)data) { case CLOCK_REALTIME: client->ec_clock_id = EV_CLOCK_REALTIME; return (0); case CLOCK_MONOTONIC: client->ec_clock_id = EV_CLOCK_MONOTONIC; return (0); default: return (EINVAL); } } /* evdev variable-length ioctls handling */ switch (IOCBASECMD(cmd)) { case EVIOCGNAME(0): strlcpy(data, evdev->ev_name, len); return (0); case EVIOCGPHYS(0): if (evdev->ev_shortname[0] == 0) return (ENOENT); strlcpy(data, evdev->ev_shortname, len); return (0); case EVIOCGUNIQ(0): if (evdev->ev_serial[0] == 0) return (ENOENT); strlcpy(data, evdev->ev_serial, len); return (0); case EVIOCGPROP(0): limit = MIN(len, bitstr_size(INPUT_PROP_CNT)); memcpy(data, evdev->ev_prop_flags, limit); return (0); case EVIOCGMTSLOTS(0): if (evdev->ev_mt == NULL) return (EINVAL); if (len < sizeof(uint32_t)) return (EINVAL); code = *(uint32_t *)data; if (!ABS_IS_MT(code)) return (EINVAL); nvalues = MIN(len / sizeof(int32_t) - 1, MAXIMAL_MT_SLOT(evdev) + 1); for (int i = 0; i < nvalues; i++) ((int32_t *)data)[i + 1] = evdev_get_mt_value(evdev, i, code); return (0); case EVIOCGKEY(0): limit = MIN(len, bitstr_size(KEY_CNT)); EVDEV_LOCK(evdev); evdev_client_filter_queue(client, EV_KEY); memcpy(data, evdev->ev_key_states, limit); EVDEV_UNLOCK(evdev); return (0); case EVIOCGLED(0): limit = MIN(len, bitstr_size(LED_CNT)); EVDEV_LOCK(evdev); evdev_client_filter_queue(client, EV_LED); memcpy(data, evdev->ev_led_states, limit); EVDEV_UNLOCK(evdev); return (0); case EVIOCGSND(0): limit = MIN(len, bitstr_size(SND_CNT)); EVDEV_LOCK(evdev); evdev_client_filter_queue(client, EV_SND); memcpy(data, evdev->ev_snd_states, limit); EVDEV_UNLOCK(evdev); return (0); case EVIOCGSW(0): limit = MIN(len, bitstr_size(SW_CNT)); EVDEV_LOCK(evdev); evdev_client_filter_queue(client, EV_SW); memcpy(data, evdev->ev_sw_states, limit); EVDEV_UNLOCK(evdev); return (0); case EVIOCGBIT(0, 0) ... EVIOCGBIT(EV_MAX, 0): type_num = IOCBASECMD(cmd) - EVIOCGBIT(0, 0); debugf(client, "EVIOCGBIT(%d): data=%p, len=%d", type_num, data, len); return (evdev_ioctl_eviocgbit(evdev, type_num, len, data)); } return (EINVAL); } static int evdev_ioctl_eviocgbit(struct evdev_dev *evdev, int type, int len, caddr_t data) { unsigned long *bitmap; int limit; switch (type) { case 0: bitmap = evdev->ev_type_flags; limit = EV_CNT; break; case EV_KEY: bitmap = evdev->ev_key_flags; limit = KEY_CNT; break; case EV_REL: bitmap = evdev->ev_rel_flags; limit = REL_CNT; break; case EV_ABS: bitmap = evdev->ev_abs_flags; limit = ABS_CNT; break; case EV_MSC: bitmap = evdev->ev_msc_flags; limit = MSC_CNT; break; case EV_LED: bitmap = evdev->ev_led_flags; limit = LED_CNT; break; case EV_SND: bitmap = evdev->ev_snd_flags; limit = SND_CNT; break; case EV_SW: bitmap = evdev->ev_sw_flags; limit = SW_CNT; break; case EV_FF: /* * We don't support EV_FF now, so let's * just fake it returning only zeros. */ bzero(data, len); return (0); default: return (ENOTTY); } /* * Clear ioctl data buffer in case it's bigger than * bitmap size */ bzero(data, len); limit = bitstr_size(limit); len = MIN(limit, len); memcpy(data, bitmap, len); return (0); } void evdev_revoke_client(struct evdev_client *client) { EVDEV_LOCK_ASSERT(client->ec_evdev); client->ec_revoked = true; } void evdev_notify_event(struct evdev_client *client) { EVDEV_CLIENT_LOCKQ_ASSERT(client); if (client->ec_blocked) { client->ec_blocked = false; wakeup(client); } if (client->ec_selected) { client->ec_selected = false; selwakeup(&client->ec_selp); } KNOTE_LOCKED(&client->ec_selp.si_note, 0); if (client->ec_async && client->ec_sigio != NULL) pgsigio(&client->ec_sigio, SIGIO, 0); } int evdev_cdev_create(struct evdev_dev *evdev) { struct make_dev_args mda; int ret, unit = 0; make_dev_args_init(&mda); mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; mda.mda_devsw = &evdev_cdevsw; mda.mda_uid = UID_ROOT; mda.mda_gid = GID_WHEEL; mda.mda_mode = 0600; mda.mda_si_drv1 = evdev; /* Try to coexist with cuse-backed input/event devices */ while ((ret = make_dev_s(&mda, &evdev->ev_cdev, "input/event%d", unit)) == EEXIST) unit++; if (ret == 0) evdev->ev_unit = unit; return (ret); } int evdev_cdev_destroy(struct evdev_dev *evdev) { destroy_dev(evdev->ev_cdev); return (0); } static void evdev_client_gettime(struct evdev_client *client, struct timeval *tv) { switch (client->ec_clock_id) { case EV_CLOCK_BOOTTIME: /* * XXX: FreeBSD does not support true POSIX monotonic clock. * So aliase EV_CLOCK_BOOTTIME to EV_CLOCK_MONOTONIC. */ case EV_CLOCK_MONOTONIC: microuptime(tv); break; case EV_CLOCK_REALTIME: default: microtime(tv); break; } } void evdev_client_push(struct evdev_client *client, uint16_t type, uint16_t code, int32_t value) { struct timeval time; size_t count, head, tail, ready; EVDEV_CLIENT_LOCKQ_ASSERT(client); head = client->ec_buffer_head; tail = client->ec_buffer_tail; ready = client->ec_buffer_ready; count = client->ec_buffer_size; /* If queue is full drop its content and place SYN_DROPPED event */ if ((tail + 1) % count == head) { debugf(client, "client %p: buffer overflow", client); head = (tail + count - 1) % count; client->ec_buffer[head] = (struct input_event) { .type = EV_SYN, .code = SYN_DROPPED, .value = 0 }; /* * XXX: Here is a small race window from now till the end of * report. The queue is empty but client has been already * notified of data readyness. Can be fixed in two ways: * 1. Implement bulk insert so queue lock would not be dropped * till the SYN_REPORT event. * 2. Insert SYN_REPORT just now and skip remaining events */ client->ec_buffer_head = head; client->ec_buffer_ready = head; } client->ec_buffer[tail].type = type; client->ec_buffer[tail].code = code; client->ec_buffer[tail].value = value; client->ec_buffer_tail = (tail + 1) % count; /* Allow users to read events only after report has been completed */ if (type == EV_SYN && code == SYN_REPORT) { evdev_client_gettime(client, &time); for (; ready != client->ec_buffer_tail; ready = (ready + 1) % count) client->ec_buffer[ready].time = time; client->ec_buffer_ready = client->ec_buffer_tail; } } void evdev_client_dumpqueue(struct evdev_client *client) { struct input_event *event; size_t i, head, tail, ready, size; head = client->ec_buffer_head; tail = client->ec_buffer_tail; ready = client->ec_buffer_ready; size = client->ec_buffer_size; printf("evdev client: %p\n", client); printf("event queue: head=%zu ready=%zu tail=%zu size=%zu\n", head, ready, tail, size); printf("queue contents:\n"); for (i = 0; i < size; i++) { event = &client->ec_buffer[i]; printf("%zu: ", i); if (i < head || i > tail) printf("unused\n"); else printf("type=%d code=%d value=%d ", event->type, event->code, event->value); if (i == head) printf("<- head\n"); else if (i == tail) printf("<- tail\n"); else if (i == ready) printf("<- ready\n"); else printf("\n"); } } static void evdev_client_filter_queue(struct evdev_client *client, uint16_t type) { struct input_event *event; size_t head, tail, count, i; bool last_was_syn = false; EVDEV_CLIENT_LOCKQ(client); i = head = client->ec_buffer_head; tail = client->ec_buffer_tail; count = client->ec_buffer_size; client->ec_buffer_ready = client->ec_buffer_tail; while (i != client->ec_buffer_tail) { event = &client->ec_buffer[i]; i = (i + 1) % count; /* Skip event of given type */ if (event->type == type) continue; /* Remove empty SYN_REPORT events */ if (event->type == EV_SYN && event->code == SYN_REPORT) { if (last_was_syn) continue; else client->ec_buffer_ready = (tail + 1) % count; } /* Rewrite entry */ memcpy(&client->ec_buffer[tail], event, sizeof(struct input_event)); last_was_syn = (event->type == EV_SYN && event->code == SYN_REPORT); tail = (tail + 1) % count; } client->ec_buffer_head = i; client->ec_buffer_tail = tail; EVDEV_CLIENT_UNLOCKQ(client); } Index: stable/11/sys/dev/evdev/evdev_utils.c =================================================================== --- stable/11/sys/dev/evdev/evdev_utils.c (revision 317988) +++ stable/11/sys/dev/evdev/evdev_utils.c (revision 317989) @@ -1,333 +1,333 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama * Copyright (c) 2015-2016 Vladimir Kondratyev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #define NONE KEY_RESERVED static uint16_t evdev_usb_scancodes[256] = { /* 0x00 - 0x27 */ NONE, NONE, NONE, NONE, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, /* 0x28 - 0x3f */ KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, /* 0x40 - 0x5f */ KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ, KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, KEY_SLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, /* 0x60 - 0x7f */ KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE, /* 0x80 - 0x9f */ KEY_VOLUMEUP, KEY_VOLUMEDOWN, NONE, NONE, NONE, KEY_KPCOMMA, NONE, KEY_RO, KEY_KATAKANAHIRAGANA, KEY_YEN,KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, NONE, NONE, NONE, KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0xa0 - 0xbf */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0xc0 - 0xdf */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0xe0 - 0xff */ KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_COFFEE, KEY_REFRESH, KEY_CALC, NONE, NONE, NONE, NONE, }; static uint16_t evdev_at_set1_scancodes[] = { /* 0x00 - 0x1f */ NONE, KEY_ESC, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_ENTER, KEY_LEFTCTRL, KEY_A, KEY_S, /* 0x20 - 0x3f */ KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_LEFTSHIFT, KEY_BACKSLASH, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_RIGHTSHIFT, NONE, KEY_LEFTALT, KEY_SPACE, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, /* 0x40 - 0x5f */ KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KPMINUS, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KPPLUS, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP0, KEY_KPDOT, NONE, NONE, NONE, KEY_F11, KEY_F12, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0x60 - 0x7f */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_KATAKANAHIRAGANA, NONE, NONE, KEY_RO, NONE, NONE, KEY_ZENKAKUHANKAKU, KEY_HIRAGANA, KEY_KATAKANA, KEY_HENKAN, NONE, KEY_MUHENKAN, NONE, KEY_YEN, KEY_KPCOMMA, NONE, /* 0x00 - 0x1f. 0xE0 prefixed */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_PREVIOUSSONG, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_NEXTSONG, NONE, NONE, - NONE, KEY_KPENTER, KEY_RIGHTCTRL, NONE, + KEY_KPENTER, KEY_RIGHTCTRL, NONE, NONE, /* 0x20 - 0x3f. 0xE0 prefixed */ KEY_MUTE, KEY_CALC, KEY_PLAYPAUSE, NONE, KEY_STOPCD, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_VOLUMEDOWN, NONE, KEY_VOLUMEUP, NONE, KEY_HOMEPAGE, NONE, NONE, KEY_KPASTERISK, NONE, KEY_SYSRQ, KEY_RIGHTALT, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0x40 - 0x5f. 0xE0 prefixed */ NONE, NONE, NONE, NONE, NONE, NONE, KEY_PAUSE, KEY_HOME, KEY_UP, KEY_PAGEUP, NONE, KEY_LEFT, NONE, KEY_RIGHT, NONE, KEY_END, KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_MENU, KEY_POWER, KEY_SLEEP, /* 0x60 - 0x7f. 0xE0 prefixed */ NONE, NONE, NONE, KEY_WAKEUP, NONE, KEY_SEARCH, KEY_BOOKMARKS, KEY_REFRESH, KEY_STOP, KEY_FORWARD, KEY_BACK, KEY_COMPUTER, KEY_MAIL, KEY_MEDIA, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, }; static uint16_t evdev_mouse_button_codes[] = { BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, BTN_SIDE, BTN_EXTRA, BTN_FORWARD, BTN_BACK, BTN_TASK, }; static uint16_t evdev_led_codes[] = { LED_CAPSL, /* CLKED */ LED_NUML, /* NLKED */ LED_SCROLLL, /* SLKED */ }; uint16_t evdev_hid2key(int scancode) { return evdev_usb_scancodes[scancode]; } void evdev_support_all_known_keys(struct evdev_dev *evdev) { size_t i; for (i = KEY_RESERVED; i < nitems(evdev_at_set1_scancodes); i++) if (evdev_at_set1_scancodes[i] != NONE) evdev_support_key(evdev, evdev_at_set1_scancodes[i]); } uint16_t evdev_scancode2key(int *state, int scancode) { uint16_t keycode; /* translate the scan code into a keycode */ keycode = evdev_at_set1_scancodes[scancode & 0x7f]; switch (*state) { case 0x00: /* normal scancode */ switch(scancode) { case 0xE0: case 0xE1: *state = scancode; return (NONE); } break; case 0xE0: /* 0xE0 prefix */ *state = 0; keycode = evdev_at_set1_scancodes[0x80 + (scancode & 0x7f)]; break; case 0xE1: /* 0xE1 prefix */ /* * The pause/break key on the 101 keyboard produces: * E1-1D-45 E1-9D-C5 * Ctrl-pause/break produces: * E0-46 E0-C6 (See above.) */ *state = 0; if ((scancode & 0x7f) == 0x1D) *state = 0x1D; return (NONE); /* NOT REACHED */ case 0x1D: /* pause / break */ *state = 0; if (scancode != 0x45) return (NONE); keycode = KEY_PAUSE; break; } return (keycode); } void evdev_push_mouse_btn(struct evdev_dev *evdev, int buttons) { size_t i; for (i = 0; i < nitems(evdev_mouse_button_codes); i++) evdev_push_key(evdev, evdev_mouse_button_codes[i], buttons & (1 << i)); } void evdev_push_leds(struct evdev_dev *evdev, int leds) { size_t i; /* Some drivers initialize leds before evdev */ if (evdev == NULL) return; for (i = 0; i < nitems(evdev_led_codes); i++) evdev_push_led(evdev, evdev_led_codes[i], leds & (1 << i)); } void evdev_push_repeats(struct evdev_dev *evdev, keyboard_t *kbd) { /* Some drivers initialize typematics before evdev */ if (evdev == NULL) return; evdev_push_event(evdev, EV_REP, REP_DELAY, kbd->kb_delay1); evdev_push_event(evdev, EV_REP, REP_PERIOD, kbd->kb_delay2); } void evdev_ev_kbd_event(struct evdev_dev *evdev, void *softc, uint16_t type, uint16_t code, int32_t value) { keyboard_t *kbd = (keyboard_t *)softc; int delay[2], leds, oleds; size_t i; if (type == EV_LED) { leds = oleds = KBD_LED_VAL(kbd); for (i = 0; i < nitems(evdev_led_codes); i++) { if (evdev_led_codes[i] == code) { if (value) leds |= 1 << i; else leds &= ~(1 << i); if (leds != oleds) kbdd_ioctl(kbd, KDSETLED, (caddr_t)&leds); break; } } } else if (type == EV_REP && code == REP_DELAY) { delay[0] = value; delay[1] = kbd->kb_delay2; kbdd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); } else if (type == EV_REP && code == REP_PERIOD) { delay[0] = kbd->kb_delay1; delay[1] = value; kbdd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); } } Index: stable/11/sys/dev/evdev/uinput.c =================================================================== --- stable/11/sys/dev/evdev/uinput.c (revision 317988) +++ stable/11/sys/dev/evdev/uinput.c (revision 317989) @@ -1,712 +1,713 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama * Copyright (c) 2015-2016 Vladimir Kondratyev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_evdev.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UINPUT_DEBUG #define debugf(state, fmt, args...) printf("uinput: " fmt "\n", ##args) #else #define debugf(state, fmt, args...) #endif #define UINPUT_BUFFER_SIZE 16 #define UINPUT_LOCK(state) sx_xlock(&(state)->ucs_lock) #define UINPUT_UNLOCK(state) sx_unlock(&(state)->ucs_lock) #define UINPUT_LOCK_ASSERT(state) sx_assert(&(state)->ucs_lock, SA_LOCKED) #define UINPUT_EMPTYQ(state) \ ((state)->ucs_buffer_head == (state)->ucs_buffer_tail) enum uinput_state { UINPUT_NEW = 0, UINPUT_CONFIGURED, UINPUT_RUNNING }; static evdev_event_t uinput_ev_event; static d_open_t uinput_open; static d_read_t uinput_read; static d_write_t uinput_write; static d_ioctl_t uinput_ioctl; static d_poll_t uinput_poll; static d_kqfilter_t uinput_kqfilter; static void uinput_dtor(void *); static int uinput_kqread(struct knote *kn, long hint); static void uinput_kqdetach(struct knote *kn); static struct cdevsw uinput_cdevsw = { .d_version = D_VERSION, .d_open = uinput_open, .d_read = uinput_read, .d_write = uinput_write, .d_ioctl = uinput_ioctl, .d_poll = uinput_poll, .d_kqfilter = uinput_kqfilter, .d_name = "uinput", }; static struct cdev *uinput_cdev; static struct evdev_methods uinput_ev_methods = { .ev_open = NULL, .ev_close = NULL, .ev_event = uinput_ev_event, }; static struct filterops uinput_filterops = { .f_isfd = 1, .f_attach = NULL, .f_detach = uinput_kqdetach, .f_event = uinput_kqread, }; struct uinput_cdev_state { enum uinput_state ucs_state; struct evdev_dev * ucs_evdev; struct sx ucs_lock; size_t ucs_buffer_head; size_t ucs_buffer_tail; struct selinfo ucs_selp; bool ucs_blocked; bool ucs_selected; struct input_event ucs_buffer[UINPUT_BUFFER_SIZE]; }; static void uinput_enqueue_event(struct uinput_cdev_state *, uint16_t, uint16_t, int32_t); static int uinput_setup_provider(struct uinput_cdev_state *, struct uinput_user_dev *); static int uinput_cdev_create(void); static void uinput_notify(struct uinput_cdev_state *); static void uinput_knllock(void *arg) { struct sx *sx = arg; sx_xlock(sx); } static void uinput_knlunlock(void *arg) { struct sx *sx = arg; sx_unlock(sx); } static void uinput_knl_assert_locked(void *arg) { sx_assert((struct sx*)arg, SA_XLOCKED); } static void uinput_knl_assert_unlocked(void *arg) { sx_assert((struct sx*)arg, SA_UNLOCKED); } static void uinput_ev_event(struct evdev_dev *evdev, void *softc, uint16_t type, uint16_t code, int32_t value) { struct uinput_cdev_state *state = softc; if (type == EV_LED) evdev_push_event(evdev, type, code, value); UINPUT_LOCK(state); if (state->ucs_state == UINPUT_RUNNING) { uinput_enqueue_event(state, type, code, value); uinput_notify(state); } UINPUT_UNLOCK(state); } static void uinput_enqueue_event(struct uinput_cdev_state *state, uint16_t type, uint16_t code, int32_t value) { size_t head, tail; UINPUT_LOCK_ASSERT(state); head = state->ucs_buffer_head; tail = (state->ucs_buffer_tail + 1) % UINPUT_BUFFER_SIZE; microtime(&state->ucs_buffer[tail].time); state->ucs_buffer[tail].type = type; state->ucs_buffer[tail].code = code; state->ucs_buffer[tail].value = value; state->ucs_buffer_tail = tail; /* If queue is full remove oldest event */ if (tail == head) { debugf(state, "state %p: buffer overflow", state); head = (head + 1) % UINPUT_BUFFER_SIZE; state->ucs_buffer_head = head; } } static int uinput_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct uinput_cdev_state *state; state = malloc(sizeof(struct uinput_cdev_state), M_EVDEV, M_WAITOK | M_ZERO); state->ucs_evdev = evdev_alloc(); sx_init(&state->ucs_lock, "uinput"); knlist_init(&state->ucs_selp.si_note, &state->ucs_lock, uinput_knllock, uinput_knlunlock, uinput_knl_assert_locked, uinput_knl_assert_unlocked); devfs_set_cdevpriv(state, uinput_dtor); return (0); } static void uinput_dtor(void *data) { struct uinput_cdev_state *state = (struct uinput_cdev_state *)data; evdev_free(state->ucs_evdev); knlist_clear(&state->ucs_selp.si_note, 0); seldrain(&state->ucs_selp); knlist_destroy(&state->ucs_selp.si_note); sx_destroy(&state->ucs_lock); free(data, M_EVDEV); } static int uinput_read(struct cdev *dev, struct uio *uio, int ioflag) { struct uinput_cdev_state *state; struct input_event *event; int remaining, ret; ret = devfs_get_cdevpriv((void **)&state); if (ret != 0) return (ret); debugf(state, "read %zd bytes by thread %d", uio->uio_resid, uio->uio_td->td_tid); /* Zero-sized reads are allowed for error checking */ if (uio->uio_resid != 0 && uio->uio_resid < sizeof(struct input_event)) return (EINVAL); remaining = uio->uio_resid / sizeof(struct input_event); UINPUT_LOCK(state); if (state->ucs_state != UINPUT_RUNNING) ret = EINVAL; if (ret == 0 && UINPUT_EMPTYQ(state)) { if (ioflag & O_NONBLOCK) ret = EWOULDBLOCK; else { if (remaining != 0) { state->ucs_blocked = true; ret = sx_sleep(state, &state->ucs_lock, PCATCH, "uiread", 0); } } } while (ret == 0 && !UINPUT_EMPTYQ(state) && remaining > 0) { event = &state->ucs_buffer[state->ucs_buffer_head]; state->ucs_buffer_head = (state->ucs_buffer_head + 1) % UINPUT_BUFFER_SIZE; remaining--; ret = uiomove(event, sizeof(struct input_event), uio); } UINPUT_UNLOCK(state); return (ret); } static int uinput_write(struct cdev *dev, struct uio *uio, int ioflag) { struct uinput_cdev_state *state; struct uinput_user_dev userdev; struct input_event event; int ret = 0; ret = devfs_get_cdevpriv((void **)&state); if (ret != 0) return (ret); debugf(state, "write %zd bytes by thread %d", uio->uio_resid, uio->uio_td->td_tid); UINPUT_LOCK(state); if (state->ucs_state != UINPUT_RUNNING) { /* Process written struct uinput_user_dev */ if (uio->uio_resid != sizeof(struct uinput_user_dev)) { debugf(state, "write size not multiple of " "struct uinput_user_dev size"); ret = EINVAL; } else { ret = uiomove(&userdev, sizeof(struct uinput_user_dev), uio); if (ret == 0) uinput_setup_provider(state, &userdev); } } else { /* Process written event */ if (uio->uio_resid % sizeof(struct input_event) != 0) { debugf(state, "write size not multiple of " "struct input_event size"); ret = EINVAL; } while (ret == 0 && uio->uio_resid > 0) { uiomove(&event, sizeof(struct input_event), uio); ret = evdev_push_event(state->ucs_evdev, event.type, event.code, event.value); } } UINPUT_UNLOCK(state); return (ret); } static int uinput_setup_dev(struct uinput_cdev_state *state, struct input_id *id, char *name, uint32_t ff_effects_max) { if (name[0] == 0) return (EINVAL); evdev_set_name(state->ucs_evdev, name); evdev_set_id(state->ucs_evdev, id->bustype, id->vendor, id->product, id->version); state->ucs_state = UINPUT_CONFIGURED; return (0); } static int uinput_setup_provider(struct uinput_cdev_state *state, struct uinput_user_dev *udev) { struct input_absinfo absinfo; int i, ret; debugf(state, "setup_provider called, udev=%p", udev); ret = uinput_setup_dev(state, &udev->id, udev->name, udev->ff_effects_max); if (ret) return (ret); bzero(&absinfo, sizeof(struct input_absinfo)); for (i = 0; i < ABS_CNT; i++) { if (!bit_test(state->ucs_evdev->ev_abs_flags, i)) continue; absinfo.minimum = udev->absmin[i]; absinfo.maximum = udev->absmax[i]; absinfo.fuzz = udev->absfuzz[i]; absinfo.flat = udev->absflat[i]; evdev_set_absinfo(state->ucs_evdev, i, &absinfo); } return (0); } static int uinput_poll(struct cdev *dev, int events, struct thread *td) { struct uinput_cdev_state *state; int revents = 0; if (devfs_get_cdevpriv((void **)&state) != 0) return (POLLNVAL); debugf(state, "poll by thread %d", td->td_tid); /* Always allow write */ if (events & (POLLOUT | POLLWRNORM)) revents |= (events & (POLLOUT | POLLWRNORM)); if (events & (POLLIN | POLLRDNORM)) { UINPUT_LOCK(state); if (!UINPUT_EMPTYQ(state)) revents = events & (POLLIN | POLLRDNORM); else { state->ucs_selected = true; selrecord(td, &state->ucs_selp); } UINPUT_UNLOCK(state); } return (revents); } static int uinput_kqfilter(struct cdev *dev, struct knote *kn) { struct uinput_cdev_state *state; int ret; ret = devfs_get_cdevpriv((void **)&state); if (ret != 0) return (ret); switch(kn->kn_filter) { case EVFILT_READ: kn->kn_fop = &uinput_filterops; break; default: return(EINVAL); } kn->kn_hook = (caddr_t)state; knlist_add(&state->ucs_selp.si_note, kn, 0); return (0); } static int uinput_kqread(struct knote *kn, long hint) { struct uinput_cdev_state *state; int ret; state = (struct uinput_cdev_state *)kn->kn_hook; UINPUT_LOCK_ASSERT(state); ret = !UINPUT_EMPTYQ(state); return (ret); } static void uinput_kqdetach(struct knote *kn) { struct uinput_cdev_state *state; state = (struct uinput_cdev_state *)kn->kn_hook; knlist_remove(&state->ucs_selp.si_note, kn, 0); } static void uinput_notify(struct uinput_cdev_state *state) { UINPUT_LOCK_ASSERT(state); if (state->ucs_blocked) { state->ucs_blocked = false; wakeup(state); } if (state->ucs_selected) { state->ucs_selected = false; selwakeup(&state->ucs_selp); } KNOTE_LOCKED(&state->ucs_selp.si_note, 0); } static int uinput_ioctl_sub(struct uinput_cdev_state *state, u_long cmd, caddr_t data) { struct uinput_setup *us; struct uinput_abs_setup *uabs; int ret, len, intdata; char buf[NAMELEN]; UINPUT_LOCK_ASSERT(state); len = IOCPARM_LEN(cmd); if ((cmd & IOC_DIRMASK) == IOC_VOID && len == sizeof(int)) intdata = *(int *)data; switch (IOCBASECMD(cmd)) { case UI_GET_SYSNAME(0): if (state->ucs_state != UINPUT_RUNNING) return (ENOENT); if (len == 0) return (EINVAL); snprintf(data, len, "event%d", state->ucs_evdev->ev_unit); return (0); } switch (cmd) { case UI_DEV_CREATE: if (state->ucs_state != UINPUT_CONFIGURED) return (EINVAL); evdev_set_methods(state->ucs_evdev, state, &uinput_ev_methods); evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_SOFTREPEAT); - evdev_register(state->ucs_evdev); - state->ucs_state = UINPUT_RUNNING; - return (0); + ret = evdev_register(state->ucs_evdev); + if (ret == 0) + state->ucs_state = UINPUT_RUNNING; + return (ret); case UI_DEV_DESTROY: if (state->ucs_state != UINPUT_RUNNING) return (0); evdev_unregister(state->ucs_evdev); bzero(state->ucs_evdev, sizeof(struct evdev_dev)); state->ucs_state = UINPUT_NEW; return (0); case UI_DEV_SETUP: if (state->ucs_state == UINPUT_RUNNING) return (EINVAL); us = (struct uinput_setup *)data; return (uinput_setup_dev(state, &us->id, us->name, us->ff_effects_max)); case UI_ABS_SETUP: if (state->ucs_state == UINPUT_RUNNING) return (EINVAL); uabs = (struct uinput_abs_setup *)data; if (uabs->code > ABS_MAX) return (EINVAL); evdev_support_abs(state->ucs_evdev, uabs->code, uabs->absinfo.value, uabs->absinfo.minimum, uabs->absinfo.maximum, uabs->absinfo.fuzz, uabs->absinfo.flat, uabs->absinfo.resolution); return (0); case UI_SET_EVBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > EV_MAX || intdata < 0) return (EINVAL); evdev_support_event(state->ucs_evdev, intdata); return (0); case UI_SET_KEYBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > KEY_MAX || intdata < 0) return (EINVAL); evdev_support_key(state->ucs_evdev, intdata); return (0); case UI_SET_RELBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > REL_MAX || intdata < 0) return (EINVAL); evdev_support_rel(state->ucs_evdev, intdata); return (0); case UI_SET_ABSBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > ABS_MAX || intdata < 0) return (EINVAL); evdev_set_abs_bit(state->ucs_evdev, intdata); return (0); case UI_SET_MSCBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > MSC_MAX || intdata < 0) return (EINVAL); evdev_support_msc(state->ucs_evdev, intdata); return (0); case UI_SET_LEDBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > LED_MAX || intdata < 0) return (EINVAL); evdev_support_led(state->ucs_evdev, intdata); return (0); case UI_SET_SNDBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > SND_MAX || intdata < 0) return (EINVAL); evdev_support_snd(state->ucs_evdev, intdata); return (0); case UI_SET_FFBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > FF_MAX || intdata < 0) return (EINVAL); /* Fake unsupported ioctl */ return (0); case UI_SET_PHYS: if (state->ucs_state == UINPUT_RUNNING) return (EINVAL); ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL); /* Linux returns EINVAL when string does not fit the buffer */ if (ret == ENAMETOOLONG) ret = EINVAL; if (ret != 0) return (ret); evdev_set_phys(state->ucs_evdev, buf); return (0); case UI_SET_SWBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > SW_MAX || intdata < 0) return (EINVAL); evdev_support_sw(state->ucs_evdev, intdata); return (0); case UI_SET_PROPBIT: if (state->ucs_state == UINPUT_RUNNING || intdata > INPUT_PROP_MAX || intdata < 0) return (EINVAL); evdev_support_prop(state->ucs_evdev, intdata); return (0); case UI_BEGIN_FF_UPLOAD: case UI_END_FF_UPLOAD: case UI_BEGIN_FF_ERASE: case UI_END_FF_ERASE: if (state->ucs_state == UINPUT_RUNNING) return (EINVAL); /* Fake unsupported ioctl */ return (0); case UI_GET_VERSION: *(unsigned int *)data = UINPUT_VERSION; return (0); } return (EINVAL); } static int uinput_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct uinput_cdev_state *state; int ret; ret = devfs_get_cdevpriv((void **)&state); if (ret != 0) return (ret); debugf(state, "ioctl called: cmd=0x%08lx, data=%p", cmd, data); UINPUT_LOCK(state); ret = uinput_ioctl_sub(state, cmd, data); UINPUT_UNLOCK(state); return (ret); } static int uinput_cdev_create(void) { struct make_dev_args mda; int ret; make_dev_args_init(&mda); mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; mda.mda_devsw = &uinput_cdevsw; mda.mda_uid = UID_ROOT; mda.mda_gid = GID_WHEEL; mda.mda_mode = 0600; ret = make_dev_s(&mda, &uinput_cdev, "uinput"); return (ret); } static int uinput_cdev_destroy(void) { destroy_dev(uinput_cdev); return (0); } static int uinput_modevent(module_t mod __unused, int cmd, void *data) { int ret = 0; switch (cmd) { case MOD_LOAD: ret = uinput_cdev_create(); break; case MOD_UNLOAD: ret = uinput_cdev_destroy(); break; case MOD_SHUTDOWN: break; default: ret = EINVAL; break; } return (ret); } DEV_MODULE(uinput, uinput_modevent, NULL); MODULE_VERSION(uinput, 1); MODULE_DEPEND(uinput, evdev, 1, 1, 1); Index: stable/11 =================================================================== --- stable/11 (revision 317988) +++ stable/11 (revision 317989) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r315176-315178