Index: head/sys/dev/evdev/cdev.c =================================================================== --- head/sys/dev/evdev/cdev.c (revision 339823) +++ head/sys/dev/evdev/cdev.c (revision 339824) @@ -1,860 +1,873 @@ /*- * 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 #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; 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) { 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); 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); + /* + * Fix evdev state corrupted with discarding of kdb events. + * EVIOCGKEY and EVIOCGLED ioctls can suffer from this. + */ + if (evdev->ev_kdb_active) { + EVDEV_LOCK(evdev); + if (evdev->ev_kdb_active) { + evdev->ev_kdb_active = false; + evdev_restore_after_kdb(evdev); + } + EVDEV_UNLOCK(evdev); + } + /* 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, 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, 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: head/sys/dev/evdev/evdev.c =================================================================== --- head/sys/dev/evdev/evdev.c (revision 339823) +++ head/sys/dev/evdev/evdev.c (revision 339824) @@ -1,947 +1,991 @@ /*- * 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 #ifdef EVDEV_DEBUG #define debugf(evdev, fmt, args...) printf("evdev: " fmt "\n", ##args) #else #define debugf(evdev, fmt, args...) #endif #ifdef FEATURE FEATURE(evdev, "Input event devices support"); #ifdef EVDEV_SUPPORT FEATURE(evdev_support, "Evdev support in hybrid drivers"); #endif #endif enum evdev_sparse_result { EV_SKIP_EVENT, /* Event value not changed */ EV_REPORT_EVENT, /* Event value changed */ EV_REPORT_MT_SLOT, /* Event value and MT slot number changed */ }; MALLOC_DEFINE(M_EVDEV, "evdev", "evdev memory"); int evdev_rcpt_mask = EVDEV_RCPT_SYSMOUSE | EVDEV_RCPT_KBDMUX; int evdev_sysmouse_t_axis = 0; #ifdef EVDEV_SUPPORT SYSCTL_NODE(_kern, OID_AUTO, evdev, CTLFLAG_RW, 0, "Evdev args"); SYSCTL_INT(_kern_evdev, OID_AUTO, rcpt_mask, CTLFLAG_RW, &evdev_rcpt_mask, 0, "Who is receiving events: bit0 - sysmouse, bit1 - kbdmux, " "bit2 - mouse hardware, bit3 - keyboard hardware"); SYSCTL_INT(_kern_evdev, OID_AUTO, sysmouse_t_axis, CTLFLAG_RW, &evdev_sysmouse_t_axis, 0, "Extract T-axis from 0-none, 1-ums, 2-psm"); #endif static void evdev_start_repeat(struct evdev_dev *, uint16_t); static void evdev_stop_repeat(struct evdev_dev *); static int evdev_check_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); static inline void bit_change(bitstr_t *bitstr, int bit, int value) { if (value) bit_set(bitstr, bit); else bit_clear(bitstr, bit); } struct evdev_dev * evdev_alloc(void) { return malloc(sizeof(struct evdev_dev), M_EVDEV, M_WAITOK | M_ZERO); } void evdev_free(struct evdev_dev *evdev) { if (evdev != NULL && evdev->ev_cdev != NULL && evdev->ev_cdev->si_drv1 != NULL) evdev_unregister(evdev); free(evdev, M_EVDEV); } static struct input_absinfo * evdev_alloc_absinfo(void) { return (malloc(sizeof(struct input_absinfo) * ABS_CNT, M_EVDEV, M_WAITOK | M_ZERO)); } static void evdev_free_absinfo(struct input_absinfo *absinfo) { free(absinfo, M_EVDEV); } int evdev_set_report_size(struct evdev_dev *evdev, size_t report_size) { if (report_size > KEY_CNT + REL_CNT + ABS_CNT + MAX_MT_SLOTS * MT_CNT + MSC_CNT + LED_CNT + SND_CNT + SW_CNT + FF_CNT) return (EINVAL); evdev->ev_report_size = report_size; return (0); } static size_t evdev_estimate_report_size(struct evdev_dev *evdev) { size_t size = 0; int res; /* * Keyboards generate one event per report but other devices with * buttons like mouses can report events simultaneously */ bit_ffs_at(evdev->ev_key_flags, KEY_OK, KEY_CNT - KEY_OK, &res); if (res == -1) bit_ffs(evdev->ev_key_flags, BTN_MISC, &res); size += (res != -1); bit_count(evdev->ev_key_flags, BTN_MISC, KEY_OK - BTN_MISC, &res); size += res; /* All relative axes can be reported simultaneously */ bit_count(evdev->ev_rel_flags, 0, REL_CNT, &res); size += res; /* * All absolute axes can be reported simultaneously. * Multitouch axes can be reported ABS_MT_SLOT times */ if (evdev->ev_absinfo != NULL) { bit_count(evdev->ev_abs_flags, 0, ABS_CNT, &res); size += res; bit_count(evdev->ev_abs_flags, ABS_MT_FIRST, MT_CNT, &res); if (res > 0) { res++; /* ABS_MT_SLOT or SYN_MT_REPORT */ if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) /* MT type B */ size += res * MAXIMAL_MT_SLOT(evdev); else /* MT type A */ size += res * (MAX_MT_REPORTS - 1); } } /* All misc events can be reported simultaneously */ bit_count(evdev->ev_msc_flags, 0, MSC_CNT, &res); size += res; /* All leds can be reported simultaneously */ bit_count(evdev->ev_led_flags, 0, LED_CNT, &res); size += res; /* Assume other events are generated once per report */ bit_ffs(evdev->ev_snd_flags, SND_CNT, &res); size += (res != -1); bit_ffs(evdev->ev_sw_flags, SW_CNT, &res); size += (res != -1); /* XXX: FF part is not implemented yet */ size++; /* SYN_REPORT */ return (size); } static int evdev_register_common(struct evdev_dev *evdev) { int ret; debugf(evdev, "%s: registered evdev provider: %s <%s>\n", evdev->ev_shortname, evdev->ev_name, evdev->ev_serial); /* Initialize internal structures */ LIST_INIT(&evdev->ev_clients); if (evdev_event_supported(evdev, EV_REP) && bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) { /* Initialize callout */ callout_init_mtx(&evdev->ev_rep_callout, &evdev->ev_mtx, 0); if (evdev->ev_rep[REP_DELAY] == 0 && evdev->ev_rep[REP_PERIOD] == 0) { /* Supply default values */ evdev->ev_rep[REP_DELAY] = 250; evdev->ev_rep[REP_PERIOD] = 33; } } /* Initialize multitouch protocol type B states */ if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT) && evdev->ev_absinfo != NULL && MAXIMAL_MT_SLOT(evdev) > 0) evdev_mt_init(evdev); /* Estimate maximum report size */ if (evdev->ev_report_size == 0) { ret = evdev_set_report_size(evdev, evdev_estimate_report_size(evdev)); if (ret != 0) goto bail_out; } /* Create char device node */ ret = evdev_cdev_create(evdev); bail_out: return (ret); } int evdev_register(struct evdev_dev *evdev) { int ret; evdev->ev_lock_type = EV_LOCK_INTERNAL; evdev->ev_lock = &evdev->ev_mtx; mtx_init(&evdev->ev_mtx, "evmtx", NULL, MTX_DEF); ret = evdev_register_common(evdev); if (ret != 0) mtx_destroy(&evdev->ev_mtx); return (ret); } int evdev_register_mtx(struct evdev_dev *evdev, struct mtx *mtx) { evdev->ev_lock_type = EV_LOCK_MTX; evdev->ev_lock = mtx; return (evdev_register_common(evdev)); } int evdev_unregister(struct evdev_dev *evdev) { struct evdev_client *client; int ret; debugf(evdev, "%s: unregistered evdev provider: %s\n", evdev->ev_shortname, evdev->ev_name); EVDEV_LOCK(evdev); evdev->ev_cdev->si_drv1 = NULL; /* Wake up sleepers */ LIST_FOREACH(client, &evdev->ev_clients, ec_link) { evdev_revoke_client(client); evdev_dispose_client(evdev, client); EVDEV_CLIENT_LOCKQ(client); evdev_notify_event(client); EVDEV_CLIENT_UNLOCKQ(client); } EVDEV_UNLOCK(evdev); /* destroy_dev can sleep so release lock */ ret = evdev_cdev_destroy(evdev); evdev->ev_cdev = NULL; if (ret == 0 && evdev->ev_lock_type == EV_LOCK_INTERNAL) mtx_destroy(&evdev->ev_mtx); evdev_free_absinfo(evdev->ev_absinfo); evdev_mt_free(evdev); return (ret); } inline void evdev_set_name(struct evdev_dev *evdev, const char *name) { snprintf(evdev->ev_name, NAMELEN, "%s", name); } inline void evdev_set_id(struct evdev_dev *evdev, uint16_t bustype, uint16_t vendor, uint16_t product, uint16_t version) { evdev->ev_id = (struct input_id) { .bustype = bustype, .vendor = vendor, .product = product, .version = version }; } inline void evdev_set_phys(struct evdev_dev *evdev, const char *name) { snprintf(evdev->ev_shortname, NAMELEN, "%s", name); } inline void evdev_set_serial(struct evdev_dev *evdev, const char *serial) { snprintf(evdev->ev_serial, NAMELEN, "%s", serial); } inline void evdev_set_methods(struct evdev_dev *evdev, void *softc, const struct evdev_methods *methods) { evdev->ev_methods = methods; evdev->ev_softc = softc; } inline void * evdev_get_softc(struct evdev_dev *evdev) { return (evdev->ev_softc); } inline void evdev_support_prop(struct evdev_dev *evdev, uint16_t prop) { KASSERT(prop < INPUT_PROP_CNT, ("invalid evdev input property")); bit_set(evdev->ev_prop_flags, prop); } inline void evdev_support_event(struct evdev_dev *evdev, uint16_t type) { KASSERT(type < EV_CNT, ("invalid evdev event property")); bit_set(evdev->ev_type_flags, type); } inline void evdev_support_key(struct evdev_dev *evdev, uint16_t code) { KASSERT(code < KEY_CNT, ("invalid evdev key property")); bit_set(evdev->ev_key_flags, code); } inline void evdev_support_rel(struct evdev_dev *evdev, uint16_t code) { KASSERT(code < REL_CNT, ("invalid evdev rel property")); bit_set(evdev->ev_rel_flags, code); } inline void evdev_support_abs(struct evdev_dev *evdev, uint16_t code, int32_t value, int32_t minimum, int32_t maximum, int32_t fuzz, int32_t flat, int32_t resolution) { struct input_absinfo absinfo; KASSERT(code < ABS_CNT, ("invalid evdev abs property")); absinfo = (struct input_absinfo) { .value = value, .minimum = minimum, .maximum = maximum, .fuzz = fuzz, .flat = flat, .resolution = resolution, }; evdev_set_abs_bit(evdev, code); evdev_set_absinfo(evdev, code, &absinfo); } inline void evdev_set_abs_bit(struct evdev_dev *evdev, uint16_t code) { KASSERT(code < ABS_CNT, ("invalid evdev abs property")); if (evdev->ev_absinfo == NULL) evdev->ev_absinfo = evdev_alloc_absinfo(); bit_set(evdev->ev_abs_flags, code); } inline void evdev_support_msc(struct evdev_dev *evdev, uint16_t code) { KASSERT(code < MSC_CNT, ("invalid evdev msc property")); bit_set(evdev->ev_msc_flags, code); } inline void evdev_support_led(struct evdev_dev *evdev, uint16_t code) { KASSERT(code < LED_CNT, ("invalid evdev led property")); bit_set(evdev->ev_led_flags, code); } inline void evdev_support_snd(struct evdev_dev *evdev, uint16_t code) { KASSERT(code < SND_CNT, ("invalid evdev snd property")); bit_set(evdev->ev_snd_flags, code); } inline void evdev_support_sw(struct evdev_dev *evdev, uint16_t code) { KASSERT(code < SW_CNT, ("invalid evdev sw property")); bit_set(evdev->ev_sw_flags, code); } bool evdev_event_supported(struct evdev_dev *evdev, uint16_t type) { KASSERT(type < EV_CNT, ("invalid evdev event property")); return (bit_test(evdev->ev_type_flags, type)); } inline void evdev_set_absinfo(struct evdev_dev *evdev, uint16_t axis, struct input_absinfo *absinfo) { KASSERT(axis < ABS_CNT, ("invalid evdev abs property")); if (axis == ABS_MT_SLOT && (absinfo->maximum < 1 || absinfo->maximum >= MAX_MT_SLOTS)) return; if (evdev->ev_absinfo == NULL) evdev->ev_absinfo = evdev_alloc_absinfo(); if (axis == ABS_MT_SLOT) evdev->ev_absinfo[ABS_MT_SLOT].maximum = absinfo->maximum; else memcpy(&evdev->ev_absinfo[axis], absinfo, sizeof(struct input_absinfo)); } inline void evdev_set_repeat_params(struct evdev_dev *evdev, uint16_t property, int value) { KASSERT(property < REP_CNT, ("invalid evdev repeat property")); evdev->ev_rep[property] = value; } inline void evdev_set_flag(struct evdev_dev *evdev, uint16_t flag) { KASSERT(flag < EVDEV_FLAG_CNT, ("invalid evdev flag property")); bit_set(evdev->ev_flags, flag); } static int evdev_check_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { if (type >= EV_CNT) return (EINVAL); /* Allow SYN events implicitly */ if (type != EV_SYN && !evdev_event_supported(evdev, type)) return (EINVAL); switch (type) { case EV_SYN: if (code >= SYN_CNT) return (EINVAL); break; case EV_KEY: if (code >= KEY_CNT) return (EINVAL); if (!bit_test(evdev->ev_key_flags, code)) return (EINVAL); break; case EV_REL: if (code >= REL_CNT) return (EINVAL); if (!bit_test(evdev->ev_rel_flags, code)) return (EINVAL); break; case EV_ABS: if (code >= ABS_CNT) return (EINVAL); if (!bit_test(evdev->ev_abs_flags, code)) return (EINVAL); if (code == ABS_MT_SLOT && (value < 0 || value > MAXIMAL_MT_SLOT(evdev))) return (EINVAL); if (ABS_IS_MT(code) && evdev->ev_mt == NULL && bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) return (EINVAL); break; case EV_MSC: if (code >= MSC_CNT) return (EINVAL); if (!bit_test(evdev->ev_msc_flags, code)) return (EINVAL); break; case EV_LED: if (code >= LED_CNT) return (EINVAL); if (!bit_test(evdev->ev_led_flags, code)) return (EINVAL); break; case EV_SND: if (code >= SND_CNT) return (EINVAL); if (!bit_test(evdev->ev_snd_flags, code)) return (EINVAL); break; case EV_SW: if (code >= SW_CNT) return (EINVAL); if (!bit_test(evdev->ev_sw_flags, code)) return (EINVAL); break; case EV_REP: if (code >= REP_CNT) return (EINVAL); break; default: return (EINVAL); } return (0); } static void evdev_modify_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t *value) { EVDEV_LOCK_ASSERT(evdev); switch (type) { case EV_KEY: if (!evdev_event_supported(evdev, EV_REP)) break; if (!bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) { /* Detect driver key repeats. */ if (bit_test(evdev->ev_key_states, code) && *value == KEY_EVENT_DOWN) *value = KEY_EVENT_REPEAT; } else { /* Start/stop callout for evdev repeats */ if (bit_test(evdev->ev_key_states, code) == !*value && !LIST_EMPTY(&evdev->ev_clients)) { if (*value == KEY_EVENT_DOWN) evdev_start_repeat(evdev, code); else evdev_stop_repeat(evdev); } } break; case EV_ABS: /* TBD: implement fuzz */ break; } } static enum evdev_sparse_result evdev_sparse_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { int32_t last_mt_slot; EVDEV_LOCK_ASSERT(evdev); /* * For certain event types, update device state bits * and convert level reporting to edge reporting */ switch (type) { case EV_KEY: switch (value) { case KEY_EVENT_UP: case KEY_EVENT_DOWN: if (bit_test(evdev->ev_key_states, code) == value) return (EV_SKIP_EVENT); bit_change(evdev->ev_key_states, code, value); break; case KEY_EVENT_REPEAT: if (bit_test(evdev->ev_key_states, code) == 0 || !evdev_event_supported(evdev, EV_REP)) return (EV_SKIP_EVENT); break; default: return (EV_SKIP_EVENT); } break; case EV_LED: if (bit_test(evdev->ev_led_states, code) == value) return (EV_SKIP_EVENT); bit_change(evdev->ev_led_states, code, value); break; case EV_SND: bit_change(evdev->ev_snd_states, code, value); break; case EV_SW: if (bit_test(evdev->ev_sw_states, code) == value) return (EV_SKIP_EVENT); bit_change(evdev->ev_sw_states, code, value); break; case EV_REP: if (evdev->ev_rep[code] == value) return (EV_SKIP_EVENT); evdev_set_repeat_params(evdev, code, value); break; case EV_REL: if (value == 0) return (EV_SKIP_EVENT); break; /* For EV_ABS, save last value in absinfo and ev_mt_states */ case EV_ABS: switch (code) { case ABS_MT_SLOT: /* Postpone ABS_MT_SLOT till next event */ evdev_set_last_mt_slot(evdev, value); return (EV_SKIP_EVENT); case ABS_MT_FIRST ... ABS_MT_LAST: /* Pass MT protocol type A events as is */ if (!bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) break; /* Don`t repeat MT protocol type B events */ last_mt_slot = evdev_get_last_mt_slot(evdev); if (evdev_get_mt_value(evdev, last_mt_slot, code) == value) return (EV_SKIP_EVENT); evdev_set_mt_value(evdev, last_mt_slot, code, value); if (last_mt_slot != CURRENT_MT_SLOT(evdev)) { CURRENT_MT_SLOT(evdev) = last_mt_slot; evdev->ev_report_opened = true; return (EV_REPORT_MT_SLOT); } break; default: if (evdev->ev_absinfo[code].value == value) return (EV_SKIP_EVENT); evdev->ev_absinfo[code].value = value; } break; case EV_SYN: if (code == SYN_REPORT) { /* Count empty reports as well as non empty */ evdev->ev_report_count++; /* Skip empty reports */ if (!evdev->ev_report_opened) return (EV_SKIP_EVENT); evdev->ev_report_opened = false; return (EV_REPORT_EVENT); } break; } evdev->ev_report_opened = true; return (EV_REPORT_EVENT); } static void evdev_propagate_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { struct evdev_client *client; debugf(evdev, "%s pushed event %d/%d/%d", evdev->ev_shortname, type, code, value); EVDEV_LOCK_ASSERT(evdev); /* Propagate event through all clients */ LIST_FOREACH(client, &evdev->ev_clients, ec_link) { if (evdev->ev_grabber != NULL && evdev->ev_grabber != client) continue; EVDEV_CLIENT_LOCKQ(client); evdev_client_push(client, type, code, value); if (type == EV_SYN && code == SYN_REPORT) evdev_notify_event(client); EVDEV_CLIENT_UNLOCKQ(client); } evdev->ev_event_count++; } void evdev_send_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { enum evdev_sparse_result sparse; EVDEV_LOCK_ASSERT(evdev); sparse = evdev_sparse_event(evdev, type, code, value); switch (sparse) { case EV_REPORT_MT_SLOT: /* report postponed ABS_MT_SLOT */ evdev_propagate_event(evdev, EV_ABS, ABS_MT_SLOT, CURRENT_MT_SLOT(evdev)); /* FALLTHROUGH */ case EV_REPORT_EVENT: evdev_propagate_event(evdev, type, code, value); /* FALLTHROUGH */ case EV_SKIP_EVENT: break; } } +void +evdev_restore_after_kdb(struct evdev_dev *evdev) +{ + int code; + + EVDEV_LOCK_ASSERT(evdev); + + /* Report postponed leds */ + for (code = 0; code < LED_CNT; code++) + if (bit_test(evdev->ev_kdb_led_states, code)) + evdev_send_event(evdev, EV_LED, code, + !bit_test(evdev->ev_led_states, code)); + bit_nclear(evdev->ev_kdb_led_states, 0, LED_MAX); + + /* Release stuck keys (CTRL + ALT + ESC) */ + evdev_stop_repeat(evdev); + for (code = 0; code < KEY_CNT; code++) { + if (bit_test(evdev->ev_key_states, code)) { + evdev_send_event(evdev, EV_KEY, code, KEY_EVENT_UP); + evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1); + } + } +} + int evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { if (evdev_check_event(evdev, type, code, value) != 0) return (EINVAL); + /* + * Discard all but LEDs kdb events as unrelated to userspace. + * Aggregate LED updates and postpone reporting until kdb deactivation. + */ + if (kdb_active || SCHEDULER_STOPPED()) { + evdev->ev_kdb_active = true; + if (type == EV_LED) + bit_set(evdev->ev_kdb_led_states, + bit_test(evdev->ev_led_states, code) != value); + return (0); + } + EVDEV_ENTER(evdev); + + /* Fix evdev state corrupted with discarding of kdb events */ + if (evdev->ev_kdb_active) { + evdev->ev_kdb_active = false; + evdev_restore_after_kdb(evdev); + } evdev_modify_event(evdev, type, code, &value); if (type == EV_SYN && code == SYN_REPORT && bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL)) evdev_send_mt_autorel(evdev); if (type == EV_SYN && code == SYN_REPORT && evdev->ev_report_opened && bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT)) evdev_send_mt_compat(evdev); evdev_send_event(evdev, type, code, value); EVDEV_EXIT(evdev); return (0); } int evdev_inject_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { int ret = 0; switch (type) { case EV_REP: /* evdev repeats should not be processed by hardware driver */ if (bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) goto push; /* FALLTHROUGH */ case EV_LED: case EV_MSC: case EV_SND: case EV_FF: if (evdev->ev_methods != NULL && evdev->ev_methods->ev_event != NULL) evdev->ev_methods->ev_event(evdev, type, code, value); /* * Leds and driver repeats should be reported in ev_event * method body to interoperate with kbdmux states and rates * propagation so both ways (ioctl and evdev) of changing it * will produce only one evdev event report to client. */ if (type == EV_LED || type == EV_REP) break; /* FALLTHROUGH */ case EV_SYN: case EV_KEY: case EV_REL: case EV_ABS: case EV_SW: push: if (evdev->ev_lock_type != EV_LOCK_INTERNAL) EVDEV_LOCK(evdev); ret = evdev_push_event(evdev, type, code, value); if (evdev->ev_lock_type != EV_LOCK_INTERNAL) EVDEV_UNLOCK(evdev); break; default: ret = EINVAL; } return (ret); } int evdev_register_client(struct evdev_dev *evdev, struct evdev_client *client) { int ret = 0; debugf(evdev, "adding new client for device %s", evdev->ev_shortname); EVDEV_LOCK_ASSERT(evdev); if (LIST_EMPTY(&evdev->ev_clients) && evdev->ev_methods != NULL && evdev->ev_methods->ev_open != NULL) { debugf(evdev, "calling ev_open() on device %s", evdev->ev_shortname); ret = evdev->ev_methods->ev_open(evdev); } if (ret == 0) LIST_INSERT_HEAD(&evdev->ev_clients, client, ec_link); return (ret); } void evdev_dispose_client(struct evdev_dev *evdev, struct evdev_client *client) { debugf(evdev, "removing client for device %s", evdev->ev_shortname); EVDEV_LOCK_ASSERT(evdev); LIST_REMOVE(client, ec_link); if (LIST_EMPTY(&evdev->ev_clients)) { if (evdev->ev_methods != NULL && evdev->ev_methods->ev_close != NULL) (void)evdev->ev_methods->ev_close(evdev); if (evdev_event_supported(evdev, EV_REP) && bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) evdev_stop_repeat(evdev); } evdev_release_client(evdev, client); } int evdev_grab_client(struct evdev_dev *evdev, struct evdev_client *client) { EVDEV_LOCK_ASSERT(evdev); if (evdev->ev_grabber != NULL) return (EBUSY); evdev->ev_grabber = client; return (0); } int evdev_release_client(struct evdev_dev *evdev, struct evdev_client *client) { EVDEV_LOCK_ASSERT(evdev); if (evdev->ev_grabber != client) return (EINVAL); evdev->ev_grabber = NULL; return (0); } static void evdev_repeat_callout(void *arg) { struct evdev_dev *evdev = (struct evdev_dev *)arg; evdev_send_event(evdev, EV_KEY, evdev->ev_rep_key, KEY_EVENT_REPEAT); evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1); if (evdev->ev_rep[REP_PERIOD]) callout_reset(&evdev->ev_rep_callout, evdev->ev_rep[REP_PERIOD] * hz / 1000, evdev_repeat_callout, evdev); else evdev->ev_rep_key = KEY_RESERVED; } static void evdev_start_repeat(struct evdev_dev *evdev, uint16_t key) { EVDEV_LOCK_ASSERT(evdev); if (evdev->ev_rep[REP_DELAY]) { evdev->ev_rep_key = key; callout_reset(&evdev->ev_rep_callout, evdev->ev_rep[REP_DELAY] * hz / 1000, evdev_repeat_callout, evdev); } } static void evdev_stop_repeat(struct evdev_dev *evdev) { EVDEV_LOCK_ASSERT(evdev); if (evdev->ev_rep_key != KEY_RESERVED) { callout_stop(&evdev->ev_rep_callout); evdev->ev_rep_key = KEY_RESERVED; } } MODULE_VERSION(evdev, 1); Index: head/sys/dev/evdev/evdev_private.h =================================================================== --- head/sys/dev/evdev/evdev_private.h (revision 339823) +++ head/sys/dev/evdev/evdev_private.h (revision 339824) @@ -1,216 +1,221 @@ /*- * 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$ */ #ifndef _DEV_EVDEV_EVDEV_PRIVATE_H #define _DEV_EVDEV_EVDEV_PRIVATE_H #include #include #include #include #include #include #include #include #define NAMELEN 80 /* * bitstr_t implementation must be identical to one found in EVIOCG* * libevdev ioctls. Our bitstring(3) API is compatible since r299090. */ _Static_assert(sizeof(bitstr_t) == sizeof(unsigned long), "bitstr_t size mismatch"); MALLOC_DECLARE(M_EVDEV); struct evdev_client; struct evdev_mt; #define CURRENT_MT_SLOT(evdev) ((evdev)->ev_absinfo[ABS_MT_SLOT].value) #define MAXIMAL_MT_SLOT(evdev) ((evdev)->ev_absinfo[ABS_MT_SLOT].maximum) enum evdev_key_events { KEY_EVENT_UP, KEY_EVENT_DOWN, KEY_EVENT_REPEAT }; /* evdev clock IDs in Linux semantic */ enum evdev_clock_id { EV_CLOCK_REALTIME = 0, /* UTC clock */ EV_CLOCK_MONOTONIC, /* monotonic, stops on suspend */ EV_CLOCK_BOOTTIME /* monotonic, suspend-awared */ }; enum evdev_lock_type { EV_LOCK_INTERNAL = 0, /* Internal evdev mutex */ EV_LOCK_MTX, /* Driver`s mutex */ }; struct evdev_dev { char ev_name[NAMELEN]; char ev_shortname[NAMELEN]; char ev_serial[NAMELEN]; struct cdev * ev_cdev; int ev_unit; enum evdev_lock_type ev_lock_type; struct mtx * ev_lock; struct mtx ev_mtx; struct input_id ev_id; struct evdev_client * ev_grabber; size_t ev_report_size; /* Supported features: */ bitstr_t bit_decl(ev_prop_flags, INPUT_PROP_CNT); bitstr_t bit_decl(ev_type_flags, EV_CNT); bitstr_t bit_decl(ev_key_flags, KEY_CNT); bitstr_t bit_decl(ev_rel_flags, REL_CNT); bitstr_t bit_decl(ev_abs_flags, ABS_CNT); bitstr_t bit_decl(ev_msc_flags, MSC_CNT); bitstr_t bit_decl(ev_led_flags, LED_CNT); bitstr_t bit_decl(ev_snd_flags, SND_CNT); bitstr_t bit_decl(ev_sw_flags, SW_CNT); struct input_absinfo * ev_absinfo; bitstr_t bit_decl(ev_flags, EVDEV_FLAG_CNT); /* Repeat parameters & callout: */ int ev_rep[REP_CNT]; struct callout ev_rep_callout; uint16_t ev_rep_key; /* State: */ bitstr_t bit_decl(ev_key_states, KEY_CNT); bitstr_t bit_decl(ev_led_states, LED_CNT); bitstr_t bit_decl(ev_snd_states, SND_CNT); bitstr_t bit_decl(ev_sw_states, SW_CNT); bool ev_report_opened; + /* KDB state: */ + bool ev_kdb_active; + bitstr_t bit_decl(ev_kdb_led_states, LED_CNT); + /* Multitouch protocol type B state: */ struct evdev_mt * ev_mt; /* Counters: */ uint64_t ev_event_count; uint64_t ev_report_count; /* Parent driver callbacks: */ const struct evdev_methods * ev_methods; void * ev_softc; LIST_ENTRY(evdev_dev) ev_link; LIST_HEAD(, evdev_client) ev_clients; }; #define SYSTEM_CONSOLE_LOCK &Giant #define EVDEV_LOCK(evdev) mtx_lock((evdev)->ev_lock) #define EVDEV_UNLOCK(evdev) mtx_unlock((evdev)->ev_lock) #define EVDEV_LOCK_ASSERT(evdev) do { \ if ((evdev)->ev_lock != SYSTEM_CONSOLE_LOCK) \ mtx_assert((evdev)->ev_lock, MA_OWNED); \ } while (0) #define EVDEV_ENTER(evdev) do { \ if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL) \ EVDEV_LOCK(evdev); \ else \ EVDEV_LOCK_ASSERT(evdev); \ } while (0) #define EVDEV_EXIT(evdev) do { \ if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL) \ EVDEV_UNLOCK(evdev); \ } while (0) struct evdev_client { struct evdev_dev * ec_evdev; struct mtx ec_buffer_mtx; size_t ec_buffer_size; size_t ec_buffer_head; size_t ec_buffer_tail; size_t ec_buffer_ready; enum evdev_clock_id ec_clock_id; struct selinfo ec_selp; struct sigio * ec_sigio; bool ec_async; bool ec_revoked; bool ec_blocked; bool ec_selected; LIST_ENTRY(evdev_client) ec_link; struct input_event ec_buffer[]; }; #define EVDEV_CLIENT_LOCKQ(client) mtx_lock(&(client)->ec_buffer_mtx) #define EVDEV_CLIENT_UNLOCKQ(client) mtx_unlock(&(client)->ec_buffer_mtx) #define EVDEV_CLIENT_LOCKQ_ASSERT(client) \ mtx_assert(&(client)->ec_buffer_mtx, MA_OWNED) #define EVDEV_CLIENT_EMPTYQ(client) \ ((client)->ec_buffer_head == (client)->ec_buffer_ready) #define EVDEV_CLIENT_SIZEQ(client) \ (((client)->ec_buffer_ready + (client)->ec_buffer_size - \ (client)->ec_buffer_head) % (client)->ec_buffer_size) /* Input device interface: */ void evdev_send_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); int evdev_inject_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); int evdev_cdev_create(struct evdev_dev *); int evdev_cdev_destroy(struct evdev_dev *); bool evdev_event_supported(struct evdev_dev *, uint16_t); void evdev_set_abs_bit(struct evdev_dev *, uint16_t); void evdev_set_absinfo(struct evdev_dev *, uint16_t, struct input_absinfo *); +void evdev_restore_after_kdb(struct evdev_dev *); /* Client interface: */ int evdev_register_client(struct evdev_dev *, struct evdev_client *); void evdev_dispose_client(struct evdev_dev *, struct evdev_client *); int evdev_grab_client(struct evdev_dev *, struct evdev_client *); int evdev_release_client(struct evdev_dev *, struct evdev_client *); void evdev_client_push(struct evdev_client *, uint16_t, uint16_t, int32_t); void evdev_notify_event(struct evdev_client *); void evdev_revoke_client(struct evdev_client *); /* Multitouch related functions: */ void evdev_mt_init(struct evdev_dev *); void evdev_mt_free(struct evdev_dev *); int32_t evdev_get_last_mt_slot(struct evdev_dev *); void evdev_set_last_mt_slot(struct evdev_dev *, int32_t); int32_t evdev_get_mt_value(struct evdev_dev *, int32_t, int16_t); void evdev_set_mt_value(struct evdev_dev *, int32_t, int16_t, int32_t); void evdev_send_mt_compat(struct evdev_dev *); void evdev_send_mt_autorel(struct evdev_dev *); /* Utility functions: */ void evdev_client_dumpqueue(struct evdev_client *); #endif /* _DEV_EVDEV_EVDEV_PRIVATE_H */