diff --git a/sys/dev/evdev/evdev.c b/sys/dev/evdev/evdev.c index d6f6cc2e003f..b5eed0e5f02f 100644 --- a/sys/dev/evdev/evdev.c +++ b/sys/dev/evdev/evdev.c @@ -1,1130 +1,1138 @@ /*- * 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(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"); /* adb keyboard driver used on powerpc does not support evdev yet */ #if defined(__powerpc__) && !defined(__powerpc64__) int evdev_rcpt_mask = EVDEV_RCPT_KBDMUX | EVDEV_RCPT_HW_MOUSE; #else int evdev_rcpt_mask = EVDEV_RCPT_HW_MOUSE | EVDEV_RCPT_HW_KBD; #endif int evdev_sysmouse_t_axis = 0; SYSCTL_NODE(_kern, OID_AUTO, evdev, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Evdev args"); #ifdef EVDEV_SUPPORT SYSCTL_INT(_kern_evdev, OID_AUTO, rcpt_mask, CTLFLAG_RWTUN, &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_RWTUN, &evdev_sysmouse_t_axis, 0, "Extract T-axis from 0-none, 1-ums, 2-psm"); #endif SYSCTL_NODE(_kern_evdev, OID_AUTO, input, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Evdev input devices"); 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); 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 void evdev_sysctl_create(struct evdev_dev *evdev) { struct sysctl_oid *ev_sysctl_tree; char ev_unit_str[8]; snprintf(ev_unit_str, sizeof(ev_unit_str), "%d", evdev->ev_unit); sysctl_ctx_init(&evdev->ev_sysctl_ctx); ev_sysctl_tree = SYSCTL_ADD_NODE_WITH_LABEL(&evdev->ev_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_kern_evdev_input), OID_AUTO, ev_unit_str, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "", "device index"); SYSCTL_ADD_STRING(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "name", CTLFLAG_RD, evdev->ev_name, 0, "Input device name"); SYSCTL_ADD_STRUCT(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "id", CTLFLAG_RD, &evdev->ev_id, input_id, "Input device identification"); /* ioctl returns ENOENT if phys is not set. sysctl returns "" here */ SYSCTL_ADD_STRING(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "phys", CTLFLAG_RD, evdev->ev_shortname, 0, "Input device short name"); /* ioctl returns ENOENT if uniq is not set. sysctl returns "" here */ SYSCTL_ADD_STRING(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "uniq", CTLFLAG_RD, evdev->ev_serial, 0, "Input device unique number"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "props", CTLFLAG_RD, evdev->ev_prop_flags, sizeof(evdev->ev_prop_flags), "", "Input device properties"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "type_bits", CTLFLAG_RD, evdev->ev_type_flags, sizeof(evdev->ev_type_flags), "", "Input device supported events types"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "key_bits", CTLFLAG_RD, evdev->ev_key_flags, sizeof(evdev->ev_key_flags), "", "Input device supported keys"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "rel_bits", CTLFLAG_RD, evdev->ev_rel_flags, sizeof(evdev->ev_rel_flags), "", "Input device supported relative events"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "abs_bits", CTLFLAG_RD, evdev->ev_abs_flags, sizeof(evdev->ev_abs_flags), "", "Input device supported absolute events"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "msc_bits", CTLFLAG_RD, evdev->ev_msc_flags, sizeof(evdev->ev_msc_flags), "", "Input device supported miscellaneous events"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "led_bits", CTLFLAG_RD, evdev->ev_led_flags, sizeof(evdev->ev_led_flags), "", "Input device supported LED events"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "snd_bits", CTLFLAG_RD, evdev->ev_snd_flags, sizeof(evdev->ev_snd_flags), "", "Input device supported sound events"); SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx, SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "sw_bits", CTLFLAG_RD, evdev->ev_sw_flags, sizeof(evdev->ev_sw_flags), "", "Input device supported switch events"); } 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 */ CK_SLIST_INIT(&evdev->ev_clients); sx_init(&evdev->ev_list_lock, "evsx"); 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_state_lock, 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_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); if (ret != 0) goto bail_out; /* Create sysctls (for device enumeration without /dev/input access rights) */ evdev_sysctl_create(evdev); bail_out: if (ret != 0) sx_destroy(&evdev->ev_list_lock); return (ret); } int evdev_register(struct evdev_dev *evdev) { int ret; if (bit_test(evdev->ev_flags, EVDEV_FLAG_EXT_EPOCH)) evdev->ev_lock_type = EV_LOCK_EXT_EPOCH; else evdev->ev_lock_type = EV_LOCK_INTERNAL; evdev->ev_state_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_state_lock = mtx; return (evdev_register_common(evdev)); } int evdev_unregister(struct evdev_dev *evdev) { struct evdev_client *client, *tmp; int ret; debugf(evdev, "%s: unregistered evdev provider: %s\n", evdev->ev_shortname, evdev->ev_name); sysctl_ctx_free(&evdev->ev_sysctl_ctx); EVDEV_LIST_LOCK(evdev); evdev->ev_cdev->si_drv1 = NULL; /* Wake up sleepers */ CK_SLIST_FOREACH_SAFE(client, &evdev->ev_clients, ec_link, tmp) { evdev_revoke_client(client); evdev_dispose_client(evdev, client); EVDEV_CLIENT_LOCKQ(client); evdev_notify_event(client); EVDEV_CLIENT_UNLOCKQ(client); } EVDEV_LIST_UNLOCK(evdev); /* release lock to avoid deadlock with evdev_dtor */ ret = evdev_cdev_destroy(evdev); evdev->ev_cdev = NULL; sx_destroy(&evdev->ev_list_lock); if (ret == 0 && evdev->ev_lock_type != EV_LOCK_MTX) 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 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 = 0, .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) { int32_t fuzz, old_value, abs_change; 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 && !CK_SLIST_EMPTY(&evdev->ev_clients)) { if (*value == KEY_EVENT_DOWN) evdev_start_repeat(evdev, code); else evdev_stop_repeat(evdev); } } break; case EV_ABS: - fuzz = evdev->ev_absinfo[code].fuzz; - if (fuzz == 0 || code == ABS_MT_SLOT) + if (code == ABS_MT_SLOT) break; else if (!ABS_IS_MT(code)) old_value = evdev->ev_absinfo[code].value; - else if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) + else if (!bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) + /* Pass MT protocol type A events as is */ + break; + else if (code == ABS_MT_TRACKING_ID) { + *value = evdev_mt_reassign_id(evdev, + evdev_mt_get_last_slot(evdev), *value); + break; + } else old_value = evdev_mt_get_value(evdev, evdev_mt_get_last_slot(evdev), code); - else /* Pass MT protocol type A events as is */ + + fuzz = evdev->ev_absinfo[code].fuzz; + if (fuzz == 0) break; abs_change = abs(*value - old_value); if (abs_change < fuzz / 2) *value = old_value; else if (abs_change < fuzz) *value = (old_value * 3 + *value) / 4; else if (abs_change < fuzz * 2) *value = (old_value + *value) / 2; 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_mt_set_last_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_mt_get_last_slot(evdev); if (evdev_mt_get_value(evdev, last_mt_slot, code) == value) return (EV_SKIP_EVENT); evdev_mt_set_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 epoch_tracker et; 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 */ if (evdev->ev_lock_type == EV_LOCK_INTERNAL) epoch_enter_preempt(INPUT_EPOCH, &et); KASSERT( evdev->ev_lock_type == EV_LOCK_MTX || in_epoch(INPUT_EPOCH) != 0, ("Input epoch has not been entered\n")); CK_SLIST_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); } if (evdev->ev_lock_type == EV_LOCK_INTERNAL) epoch_exit_preempt(INPUT_EPOCH, &et); 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); evdev_modify_event(evdev, type, code, &value); 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 */ bit_foreach(evdev->ev_kdb_led_states, LED_CNT, 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); bit_foreach(evdev->ev_key_states, KEY_CNT, 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); } if (type == EV_SYN && code == SYN_REPORT && bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) evdev_mt_sync_frame(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) { struct epoch_tracker et; 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_MTX) EVDEV_LOCK(evdev); else if (evdev->ev_lock_type == EV_LOCK_EXT_EPOCH) epoch_enter_preempt(INPUT_EPOCH, &et); ret = evdev_push_event(evdev, type, code, value); if (evdev->ev_lock_type == EV_LOCK_MTX) EVDEV_UNLOCK(evdev); else if (evdev->ev_lock_type == EV_LOCK_EXT_EPOCH) epoch_exit_preempt(INPUT_EPOCH, &et); 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_LIST_LOCK_ASSERT(evdev); if (CK_SLIST_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) CK_SLIST_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_LIST_LOCK_ASSERT(evdev); CK_SLIST_REMOVE(&evdev->ev_clients, client, evdev_client, ec_link); if (CK_SLIST_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)) { if (evdev->ev_lock_type != EV_LOCK_MTX) EVDEV_LOCK(evdev); evdev_stop_repeat(evdev); if (evdev->ev_lock_type != EV_LOCK_MTX) EVDEV_UNLOCK(evdev); } } if (evdev->ev_lock_type != EV_LOCK_MTX) EVDEV_LOCK(evdev); evdev_release_client(evdev, client); if (evdev->ev_lock_type != EV_LOCK_MTX) EVDEV_UNLOCK(evdev); } 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 epoch_tracker et; struct evdev_dev *evdev = (struct evdev_dev *)arg; if (evdev->ev_lock_type == EV_LOCK_EXT_EPOCH) epoch_enter_preempt(INPUT_EPOCH, &et); 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_lock_type == EV_LOCK_EXT_EPOCH) epoch_exit_preempt(INPUT_EPOCH, &et); 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); diff --git a/sys/dev/evdev/evdev.h b/sys/dev/evdev/evdev.h index 64bf75f04efd..e1c5aedb029c 100644 --- a/sys/dev/evdev/evdev.h +++ b/sys/dev/evdev/evdev.h @@ -1,242 +1,243 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama * 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_H #define _DEV_EVDEV_EVDEV_H #include #include #include #include #include #define NAMELEN 80 struct evdev_dev; typedef int (evdev_open_t)(struct evdev_dev *); typedef int (evdev_close_t)(struct evdev_dev *); typedef void (evdev_event_t)(struct evdev_dev *, uint16_t, uint16_t, int32_t); typedef void (evdev_keycode_t)(struct evdev_dev *, struct input_keymap_entry *); /* * Keyboard and mouse events recipient mask. * evdev_rcpt_mask variable should be respected by keyboard and mouse drivers * that are able to send events through both evdev and sysmouse/kbdmux * interfaces so user can choose prefered one to not receive one event twice. */ #define EVDEV_RCPT_SYSMOUSE (1<<0) #define EVDEV_RCPT_KBDMUX (1<<1) #define EVDEV_RCPT_HW_MOUSE (1<<2) #define EVDEV_RCPT_HW_KBD (1<<3) extern int evdev_rcpt_mask; /* * Sysmouse protocol does not support horizontal wheel movement reporting. * To overcome this limitation different drivers use different sysmouse proto * extensions. Set kern.evdev.sysmouse_t_axis to tell sysmouse evdev driver * which protocol extension is used. * 0 - do not extract horizontal wheel movement (default). * 1 - ums(4) horizontal wheel encoding. T-axis is mapped to buttons 6 and 7 * 2 - psm(4) wheels encoding: z = 1,-1 - vert. wheel, z = 2,-2 - horiz. wheel */ enum { EVDEV_SYSMOUSE_T_AXIS_NONE = 0, EVDEV_SYSMOUSE_T_AXIS_UMS = 1, EVDEV_SYSMOUSE_T_AXIS_PSM = 2, }; extern int evdev_sysmouse_t_axis; #define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR #define ABS_MT_LAST ABS_MT_TOOL_Y #define ABS_IS_MT(x) ((x) >= ABS_MT_FIRST && (x) <= ABS_MT_LAST) #define ABS_MT_INDEX(x) ((x) - ABS_MT_FIRST) #define MT_CNT (ABS_MT_INDEX(ABS_MT_LAST) + 1) /* Multitouch protocol type A */ #define MAX_MT_REPORTS 5 /* Multitouch protocol type B interface */ #define MAX_MT_SLOTS 16 #define EVDEV_FLAG_SOFTREPEAT 0x00 /* use evdev to repeat keys */ #define EVDEV_FLAG_MT_STCOMPAT 0x01 /* autogenerate ST-compatible events * for MT protocol type B reports */ #define EVDEV_FLAG_MT_AUTOREL 0x02 /* Autorelease MT-slots not listed in * current MT protocol type B report */ #define EVDEV_FLAG_EXT_EPOCH 0x03 /* evdev_push_* is allways called with * input (global) epoch entered */ +#define EVDEV_FLAG_MT_KEEPID 0x04 /* Do not reassign tracking ID */ #define EVDEV_FLAG_MAX 0x1F #define EVDEV_FLAG_CNT (EVDEV_FLAG_MAX + 1) struct evdev_methods { evdev_open_t *ev_open; evdev_close_t *ev_close; evdev_event_t *ev_event; evdev_keycode_t *ev_get_keycode; evdev_keycode_t *ev_set_keycode; }; union evdev_mt_slot { int32_t val[MT_CNT]; struct { int32_t maj; /* ABS_MT_TOUCH_MAJOR */ int32_t min; /* ABS_MT_TOUCH_MINOR */ int32_t w_maj; /* ABS_MT_WIDTH_MAJOR */ int32_t w_min; /* ABS_MT_WIDTH_MINOR */ int32_t ori; /* ABS_MT_ORIENTATION */ int32_t x; /* ABS_MT_POSITION_X */ int32_t y; /* ABS_MT_POSITION_Y */ int32_t type; /* ABS_MT_TOOL_TYPE */ int32_t blob_id; /* ABS_MT_BLOB_ID */ int32_t id; /* ABS_MT_TRACKING_ID */ int32_t p; /* ABS_MT_PRESSURE */ int32_t dist; /* ABS_MT_DISTANCE */ int32_t tool_x; /* ABS_MT_TOOL_X */ int32_t tool_y; /* ABS_MT_TOOL_Y */ }; }; _Static_assert(offsetof(union evdev_mt_slot, tool_y) == offsetof(union evdev_mt_slot, val[ABS_MT_INDEX(ABS_MT_TOOL_Y)]), "evdev_mt_slot array members does not match their structure aliases"); /* Input device interface: */ struct evdev_dev *evdev_alloc(void); void evdev_free(struct evdev_dev *); void evdev_set_name(struct evdev_dev *, const char *); void evdev_set_id(struct evdev_dev *, uint16_t, uint16_t, uint16_t, uint16_t); void evdev_set_phys(struct evdev_dev *, const char *); void evdev_set_serial(struct evdev_dev *, const char *); void evdev_set_methods(struct evdev_dev *, void *, const struct evdev_methods *); int evdev_register(struct evdev_dev *); int evdev_register_mtx(struct evdev_dev *, struct mtx *); int evdev_unregister(struct evdev_dev *); int evdev_push_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); void evdev_support_prop(struct evdev_dev *, uint16_t); void evdev_support_event(struct evdev_dev *, uint16_t); void evdev_support_key(struct evdev_dev *, uint16_t); void evdev_support_rel(struct evdev_dev *, uint16_t); void evdev_support_abs(struct evdev_dev *, uint16_t, int32_t, int32_t, int32_t, int32_t, int32_t); void evdev_support_msc(struct evdev_dev *, uint16_t); void evdev_support_led(struct evdev_dev *, uint16_t); void evdev_support_snd(struct evdev_dev *, uint16_t); void evdev_support_sw(struct evdev_dev *, uint16_t); void evdev_set_repeat_params(struct evdev_dev *, uint16_t, int); int evdev_set_report_size(struct evdev_dev *, size_t); void evdev_set_flag(struct evdev_dev *, uint16_t); void *evdev_get_softc(struct evdev_dev *); /* Multitouch related functions: */ int evdev_get_mt_slot_by_tracking_id(struct evdev_dev *, int32_t); void evdev_support_mt_compat(struct evdev_dev *); void evdev_push_mt_compat(struct evdev_dev *); int evdev_mt_push_slot(struct evdev_dev *, int, union evdev_mt_slot *); void evdev_mt_push_autorel(struct evdev_dev *); static inline int evdev_mt_id_to_slot(struct evdev_dev *evdev, int32_t id) { return (evdev_get_mt_slot_by_tracking_id(evdev, id)); } /* Utility functions: */ uint16_t evdev_hid2key(int); void evdev_support_all_known_keys(struct evdev_dev *); uint16_t evdev_scancode2key(int *, int); void evdev_push_mouse_btn(struct evdev_dev *, int); void evdev_push_leds(struct evdev_dev *, int); void evdev_push_repeats(struct evdev_dev *, keyboard_t *); void evdev_support_nfingers(struct evdev_dev *, int); void evdev_push_nfingers(struct evdev_dev *, int); /* Event reporting shortcuts: */ static __inline int evdev_sync(struct evdev_dev *evdev) { return (evdev_push_event(evdev, EV_SYN, SYN_REPORT, 1)); } static __inline int evdev_mt_sync(struct evdev_dev *evdev) { return (evdev_push_event(evdev, EV_SYN, SYN_MT_REPORT, 1)); } static __inline int evdev_push_key(struct evdev_dev *evdev, uint16_t code, int32_t value) { return (evdev_push_event(evdev, EV_KEY, code, value != 0)); } static __inline int evdev_push_rel(struct evdev_dev *evdev, uint16_t code, int32_t value) { return (evdev_push_event(evdev, EV_REL, code, value)); } static __inline int evdev_push_abs(struct evdev_dev *evdev, uint16_t code, int32_t value) { return (evdev_push_event(evdev, EV_ABS, code, value)); } static __inline int evdev_push_msc(struct evdev_dev *evdev, uint16_t code, int32_t value) { return (evdev_push_event(evdev, EV_MSC, code, value)); } static __inline int evdev_push_led(struct evdev_dev *evdev, uint16_t code, int32_t value) { return (evdev_push_event(evdev, EV_LED, code, value != 0)); } static __inline int evdev_push_snd(struct evdev_dev *evdev, uint16_t code, int32_t value) { return (evdev_push_event(evdev, EV_SND, code, value)); } static __inline int evdev_push_sw(struct evdev_dev *evdev, uint16_t code, int32_t value) { return (evdev_push_event(evdev, EV_SW, code, value != 0)); } #endif /* _DEV_EVDEV_EVDEV_H */ diff --git a/sys/dev/evdev/evdev_mt.c b/sys/dev/evdev/evdev_mt.c index 6f5cce4a008d..1a600fe3480d 100644 --- a/sys/dev/evdev/evdev_mt.c +++ b/sys/dev/evdev/evdev_mt.c @@ -1,328 +1,361 @@ /*- * Copyright (c) 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 #ifdef DEBUG #define debugf(fmt, args...) printf("evdev: " fmt "\n", ##args) #else #define debugf(fmt, args...) #endif typedef u_int slotset_t; _Static_assert(MAX_MT_SLOTS < sizeof(slotset_t) * 8, "MAX_MT_SLOTS too big"); #define FOREACHBIT(v, i) \ for ((i) = ffs(v) - 1; (i) != -1; (i) = ffs((v) & (~1 << (i))) - 1) struct { uint16_t mt; uint16_t st; int32_t max; } static evdev_mtstmap[] = { { ABS_MT_POSITION_X, ABS_X, 0 }, { ABS_MT_POSITION_Y, ABS_Y, 0 }, { ABS_MT_PRESSURE, ABS_PRESSURE, 255 }, { ABS_MT_TOUCH_MAJOR, ABS_TOOL_WIDTH, 15 }, }; struct evdev_mt { int last_reported_slot; + uint16_t tracking_id; + int32_t tracking_ids[MAX_MT_SLOTS]; u_int mtst_events; /* the set of slots with active touches */ slotset_t touches; /* the set of slots with unsynchronized state */ slotset_t frame; union evdev_mt_slot slots[]; }; static void evdev_mt_send_st_compat(struct evdev_dev *); static void evdev_mt_send_autorel(struct evdev_dev *); static inline int ffc_slot(struct evdev_dev *evdev, slotset_t slots) { return (ffs(~slots & (2U << MAXIMAL_MT_SLOT(evdev)) - 1) - 1); } void evdev_mt_init(struct evdev_dev *evdev) { int slot, slots; slots = MAXIMAL_MT_SLOT(evdev) + 1; evdev->ev_mt = malloc(offsetof(struct evdev_mt, slots) + sizeof(union evdev_mt_slot) * slots, M_EVDEV, M_WAITOK | M_ZERO); /* Initialize multitouch protocol type B states */ for (slot = 0; slot < slots; slot++) evdev->ev_mt->slots[slot].id = -1; + if (!bit_test(evdev->ev_flags, EVDEV_FLAG_MT_KEEPID)) + evdev_support_abs(evdev, + ABS_MT_TRACKING_ID, -1, UINT16_MAX, 0, 0, 0); if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT)) evdev_support_mt_compat(evdev); } void evdev_mt_free(struct evdev_dev *evdev) { free(evdev->ev_mt, M_EVDEV); } void evdev_mt_sync_frame(struct evdev_dev *evdev) { if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL)) evdev_mt_send_autorel(evdev); if (evdev->ev_report_opened && bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT)) evdev_mt_send_st_compat(evdev); evdev->ev_mt->frame = 0; } static void evdev_mt_send_slot(struct evdev_dev *evdev, int slot, union evdev_mt_slot *state) { int i; bool type_a = !bit_test(evdev->ev_abs_flags, ABS_MT_SLOT); EVDEV_LOCK_ASSERT(evdev); MPASS(type_a || (slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev))); MPASS(!type_a || state != NULL); if (!type_a) { evdev_send_event(evdev, EV_ABS, ABS_MT_SLOT, slot); if (state == NULL) { evdev_send_event(evdev, EV_ABS, ABS_MT_TRACKING_ID, -1); return; } } bit_foreach_at(evdev->ev_abs_flags, ABS_MT_FIRST, ABS_MT_LAST + 1, i) evdev_send_event(evdev, EV_ABS, i, state->val[ABS_MT_INDEX(i)]); if (type_a) evdev_send_event(evdev, EV_SYN, SYN_MT_REPORT, 1); } int evdev_mt_push_slot(struct evdev_dev *evdev, int slot, union evdev_mt_slot *state) { bool type_a = !bit_test(evdev->ev_abs_flags, ABS_MT_SLOT); if (type_a && state == NULL) return (EINVAL); if (!type_a && (slot < 0 || slot > MAXIMAL_MT_SLOT(evdev))) return (EINVAL); EVDEV_ENTER(evdev); evdev_mt_send_slot(evdev, slot, state); EVDEV_EXIT(evdev); return (0); } int evdev_mt_get_last_slot(struct evdev_dev *evdev) { return (evdev->ev_mt->last_reported_slot); } void evdev_mt_set_last_slot(struct evdev_dev *evdev, int slot) { struct evdev_mt *mt = evdev->ev_mt; MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev)); mt->frame |= 1U << slot; mt->last_reported_slot = slot; } int32_t evdev_mt_get_value(struct evdev_dev *evdev, int slot, int16_t code) { struct evdev_mt *mt = evdev->ev_mt; MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev)); return (mt->slots[slot].val[ABS_MT_INDEX(code)]); } void evdev_mt_set_value(struct evdev_dev *evdev, int slot, int16_t code, int32_t value) { struct evdev_mt *mt = evdev->ev_mt; MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev)); if (code == ABS_MT_TRACKING_ID) { if (value != -1) mt->touches |= 1U << slot; else mt->touches &= ~(1U << slot); } mt->slots[slot].val[ABS_MT_INDEX(code)] = value; } int evdev_get_mt_slot_by_tracking_id(struct evdev_dev *evdev, int32_t tracking_id) { struct evdev_mt *mt = evdev->ev_mt; int slot; FOREACHBIT(mt->touches, slot) - if (mt->slots[slot].id == tracking_id) + if (mt->tracking_ids[slot] == tracking_id) return (slot); /* * Do not allow allocation of new slot in a place of just * released one within the same report. */ return (ffc_slot(evdev, mt->touches | mt->frame)); } +int32_t +evdev_mt_reassign_id(struct evdev_dev *evdev, int slot, int32_t id) +{ + struct evdev_mt *mt = evdev->ev_mt; + int32_t nid; + + if (id == -1 || bit_test(evdev->ev_flags, EVDEV_FLAG_MT_KEEPID)) { + mt->tracking_ids[slot] = id; + return (id); + } + + nid = evdev_mt_get_value(evdev, slot, ABS_MT_TRACKING_ID); + if (nid != -1) { + KASSERT(id == mt->tracking_ids[slot], + ("MT-slot tracking id has changed")); + return (nid); + } + + mt->tracking_ids[slot] = id; +again: + nid = mt->tracking_id++; + FOREACHBIT(mt->touches, slot) + if (evdev_mt_get_value(evdev, slot, ABS_MT_TRACKING_ID) == nid) + goto again; + + return (nid); +} + static inline int32_t evdev_mt_normalize(int32_t value, int32_t mtmin, int32_t mtmax, int32_t stmax) { if (stmax != 0 && mtmax != mtmin) { value = (value - mtmin) * stmax / (mtmax - mtmin); value = MAX(MIN(value, stmax), 0); } return (value); } void evdev_support_mt_compat(struct evdev_dev *evdev) { struct input_absinfo *ai; int i; if (evdev->ev_absinfo == NULL) return; evdev_support_event(evdev, EV_KEY); evdev_support_key(evdev, BTN_TOUCH); /* Touchscreens should not advertise tap tool capabilities */ if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT)) evdev_support_nfingers(evdev, MAXIMAL_MT_SLOT(evdev) + 1); /* Echo 0-th MT-slot as ST-slot */ for (i = 0; i < nitems(evdev_mtstmap); i++) { if (!bit_test(evdev->ev_abs_flags, evdev_mtstmap[i].mt) || bit_test(evdev->ev_abs_flags, evdev_mtstmap[i].st)) continue; ai = evdev->ev_absinfo + evdev_mtstmap[i].mt; evdev->ev_mt->mtst_events |= 1U << i; if (evdev_mtstmap[i].max != 0) evdev_support_abs(evdev, evdev_mtstmap[i].st, 0, evdev_mtstmap[i].max, 0, evdev_mt_normalize( ai->flat, 0, ai->maximum, evdev_mtstmap[i].max), 0); else evdev_support_abs(evdev, evdev_mtstmap[i].st, ai->minimum, ai->maximum, 0, ai->flat, ai->resolution); } } static void evdev_mt_send_st_compat(struct evdev_dev *evdev) { struct evdev_mt *mt = evdev->ev_mt; int nfingers, i, st_slot; EVDEV_LOCK_ASSERT(evdev); nfingers = bitcount(mt->touches); evdev_send_event(evdev, EV_KEY, BTN_TOUCH, nfingers > 0); /* Send first active MT-slot state as single touch report */ st_slot = ffs(mt->touches) - 1; if (st_slot != -1) FOREACHBIT(mt->mtst_events, i) evdev_send_event(evdev, EV_ABS, evdev_mtstmap[i].st, evdev_mt_normalize(evdev_mt_get_value(evdev, st_slot, evdev_mtstmap[i].mt), evdev->ev_absinfo[evdev_mtstmap[i].mt].minimum, evdev->ev_absinfo[evdev_mtstmap[i].mt].maximum, evdev_mtstmap[i].max)); /* Touchscreens should not report tool taps */ if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT)) evdev_send_nfingers(evdev, nfingers); if (nfingers == 0) evdev_send_event(evdev, EV_ABS, ABS_PRESSURE, 0); } void evdev_push_mt_compat(struct evdev_dev *evdev) { EVDEV_ENTER(evdev); evdev_mt_send_st_compat(evdev); EVDEV_EXIT(evdev); } static void evdev_mt_send_autorel(struct evdev_dev *evdev) { struct evdev_mt *mt = evdev->ev_mt; int slot; EVDEV_LOCK_ASSERT(evdev); FOREACHBIT(mt->touches & ~mt->frame, slot) evdev_mt_send_slot(evdev, slot, NULL); } void evdev_mt_push_autorel(struct evdev_dev *evdev) { EVDEV_ENTER(evdev); evdev_mt_send_autorel(evdev); EVDEV_EXIT(evdev); } diff --git a/sys/dev/evdev/evdev_private.h b/sys/dev/evdev/evdev_private.h index fc079a324ba4..3fb2d61d091a 100644 --- a/sys/dev/evdev/evdev_private.h +++ b/sys/dev/evdev/evdev_private.h @@ -1,292 +1,293 @@ /*- * 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 #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 */ }; /* * Locking. * * Internal evdev structures are protected with next locks: * State lock (s) - Internal state. The data it protects is changed * by incoming evdev events and some ioctls. * Client list epoch (l) - Read access to client list. * Client list lock (l) - Write access to client list. * Client queue locks (q) - One lock per client to serialize access to data * available through character device node. * * Depending on evdev_register_() suffix evdev can run in following modes: * 1. Internal epoch. evdev_register(). All locks are internal. * 2. External epoch. Evdev expects to be run under input epoch entered by * parent driver. The mode is enabled with EVDEV_FLAG_EXT_EPOCH flag. * 3. External mutex. evdev_register_mtx(). Evdev uses mutex provided by parent * driver as both "State lock" and "Client list lock". This mode is * deprecated as it causes ev_open and ev_close handlers to be called with * parent driver mutex taken. */ #define INPUT_EPOCH global_epoch_preempt enum evdev_lock_type { EV_LOCK_INTERNAL = 0, /* Internal epoch */ EV_LOCK_MTX, /* Driver`s mutex */ EV_LOCK_EXT_EPOCH, /* External epoch */ }; 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_state_lock; /* State lock */ struct mtx ev_mtx; /* Internal state lock */ struct sx ev_list_lock; /* Client list lock */ struct input_id ev_id; struct evdev_client * ev_grabber; /* (s) */ 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; /* (s) */ bitstr_t bit_decl(ev_flags, EVDEV_FLAG_CNT); /* Repeat parameters & callout: */ int ev_rep[REP_CNT]; /* (s) */ struct callout ev_rep_callout; /* (s) */ uint16_t ev_rep_key; /* (s) */ /* State: */ bitstr_t bit_decl(ev_key_states, KEY_CNT); /* (s) */ bitstr_t bit_decl(ev_led_states, LED_CNT); /* (s) */ bitstr_t bit_decl(ev_snd_states, SND_CNT); /* (s) */ bitstr_t bit_decl(ev_sw_states, SW_CNT); /* (s) */ bool ev_report_opened; /* (s) */ /* 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; /* (s) */ /* Counters: */ uint64_t ev_event_count; /* (s) */ uint64_t ev_report_count; /* (s) */ /* Parent driver callbacks: */ const struct evdev_methods * ev_methods; void * ev_softc; /* Sysctl: */ struct sysctl_ctx_list ev_sysctl_ctx; LIST_ENTRY(evdev_dev) ev_link; CK_SLIST_HEAD(, evdev_client) ev_clients; /* (l) */ }; #define SYSTEM_CONSOLE_LOCK &Giant #define EVDEV_LOCK(evdev) mtx_lock((evdev)->ev_state_lock) #define EVDEV_UNLOCK(evdev) mtx_unlock((evdev)->ev_state_lock) #define EVDEV_LOCK_ASSERT(evdev) do { \ if ((evdev)->ev_state_lock != SYSTEM_CONSOLE_LOCK) \ mtx_assert((evdev)->ev_state_lock, MA_OWNED); \ } while (0) #define EVDEV_ENTER(evdev) do { \ if ((evdev)->ev_lock_type != EV_LOCK_MTX) \ EVDEV_LOCK(evdev); \ else \ EVDEV_LOCK_ASSERT(evdev); \ } while (0) #define EVDEV_EXIT(evdev) do { \ if ((evdev)->ev_lock_type != EV_LOCK_MTX) \ EVDEV_UNLOCK(evdev); \ } while (0) #define EVDEV_LIST_LOCK(evdev) do { \ if ((evdev)->ev_lock_type == EV_LOCK_MTX) \ EVDEV_LOCK(evdev); \ else \ sx_xlock(&(evdev)->ev_list_lock); \ } while (0) #define EVDEV_LIST_UNLOCK(evdev) do { \ if ((evdev)->ev_lock_type == EV_LOCK_MTX) \ EVDEV_UNLOCK(evdev); \ else \ sx_unlock(&(evdev)->ev_list_lock); \ } while (0) #define EVDEV_LIST_LOCK_ASSERT(evdev) do { \ if ((evdev)->ev_lock_type == EV_LOCK_MTX) \ EVDEV_LOCK_ASSERT(evdev); \ else \ sx_assert(&(evdev)->ev_list_lock, MA_OWNED); \ } while (0) static inline int EVDEV_LIST_LOCK_SIG(struct evdev_dev *evdev) { if (evdev->ev_lock_type == EV_LOCK_MTX) { EVDEV_LOCK(evdev); return (0); } return (sx_xlock_sig(&evdev->ev_list_lock)); } struct evdev_client { struct evdev_dev * ec_evdev; struct mtx ec_buffer_mtx; /* Client queue lock */ size_t ec_buffer_size; size_t ec_buffer_head; /* (q) */ size_t ec_buffer_tail; /* (q) */ size_t ec_buffer_ready; /* (q) */ enum evdev_clock_id ec_clock_id; struct selinfo ec_selp; /* (q) */ struct sigio * ec_sigio; bool ec_async; /* (q) */ bool ec_revoked; /* (l) */ bool ec_blocked; /* (q) */ bool ec_selected; /* (q) */ CK_SLIST_ENTRY(evdev_client) ec_link; /* (l) */ struct input_event ec_buffer[]; /* (q) */ }; #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) /* bitstring(3) helper */ static inline void bit_change(bitstr_t *bitstr, int bit, int value) { if (value) bit_set(bitstr, bit); else bit_clear(bitstr, bit); } /* 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 *); void evdev_mt_sync_frame(struct evdev_dev *); int evdev_mt_get_last_slot(struct evdev_dev *); void evdev_mt_set_last_slot(struct evdev_dev *, int); int32_t evdev_mt_get_value(struct evdev_dev *, int, int16_t); void evdev_mt_set_value(struct evdev_dev *, int, int16_t, int32_t); +int32_t evdev_mt_reassign_id(struct evdev_dev *, int, int32_t); /* Utility functions: */ void evdev_client_dumpqueue(struct evdev_client *); void evdev_send_nfingers(struct evdev_dev *, int); #endif /* _DEV_EVDEV_EVDEV_PRIVATE_H */ diff --git a/sys/dev/evdev/uinput.c b/sys/dev/evdev/uinput.c index ceecee652ac3..e7854e89f645 100644 --- a/sys/dev/evdev/uinput.c +++ b/sys/dev/evdev/uinput.c @@ -1,715 +1,716 @@ /*- * 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 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_lock(void *arg, int what) { if (what == LA_LOCKED) sx_assert((struct sx*)arg, SA_XLOCKED); else sx_assert((struct sx*)arg, SA_UNLOCKED); } static void uinput_ev_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { struct uinput_cdev_state *state = evdev_get_softc(evdev); 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_lock); 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_set_flag(state->ucs_evdev, EVDEV_FLAG_MT_KEEPID); 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_set_abs_bit(state->ucs_evdev, uabs->code); evdev_set_absinfo(state->ucs_evdev, uabs->code, &uabs->absinfo); 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_BSDUNIQ: if (state->ucs_state == UINPUT_RUNNING) return (EINVAL); ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL); if (ret != 0) return (ret); evdev_set_serial(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);