diff --git a/sys/dev/evdev/evdev.c b/sys/dev/evdev/evdev.c index 63e04966fec9..4de5dee8db04 100644 --- a/sys/dev/evdev/evdev.c +++ b/sys/dev/evdev/evdev.c @@ -1,949 +1,934 @@ /*- * 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 #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"); #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; 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"); 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_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) { 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: if (bit_test(evdev->ev_snd_states, code) == value) return (EV_SKIP_EVENT); 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; } } int evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { if (evdev->ev_lock_type != EV_LOCK_INTERNAL) EVDEV_LOCK_ASSERT(evdev); if (evdev_check_event(evdev, type, code, value) != 0) return (EINVAL); if (evdev->ev_lock_type == EV_LOCK_INTERNAL) EVDEV_LOCK(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); if (evdev->ev_lock_type == EV_LOCK_INTERNAL) EVDEV_UNLOCK(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, evdev->ev_softc, 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: ret = evdev_push_event(evdev, type, code, value); break; default: ret = EINVAL; } return (ret); } -inline int -evdev_sync(struct evdev_dev *evdev) -{ - - return (evdev_push_event(evdev, EV_SYN, SYN_REPORT, 1)); -} - - -inline int -evdev_mt_sync(struct evdev_dev *evdev) -{ - - return (evdev_push_event(evdev, EV_SYN, SYN_MT_REPORT, 1)); -} - 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, evdev->ev_softc); } 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) evdev->ev_methods->ev_close(evdev, evdev->ev_softc); 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); diff --git a/sys/dev/evdev/evdev.h b/sys/dev/evdev/evdev.h index 7287e739e832..34808b4b563c 100644 --- a/sys/dev/evdev/evdev.h +++ b/sys/dev/evdev/evdev.h @@ -1,132 +1,194 @@ /*- * 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 #define NAMELEN 80 struct evdev_dev; typedef int (evdev_open_t)(struct evdev_dev *, void *); typedef void (evdev_close_t)(struct evdev_dev *, void *); typedef void (evdev_event_t)(struct evdev_dev *, void *, uint16_t, uint16_t, int32_t); typedef void (evdev_keycode_t)(struct evdev_dev *, void *, 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; #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_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; }; /* 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); -int evdev_sync(struct evdev_dev *); -int evdev_mt_sync(struct evdev_dev *); 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, 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); /* Multitouch related functions: */ int32_t evdev_get_mt_slot_by_tracking_id(struct evdev_dev *, int32_t); void evdev_support_nfingers(struct evdev_dev *, int32_t); void evdev_support_mt_compat(struct evdev_dev *); void evdev_push_nfingers(struct evdev_dev *, int32_t); void evdev_push_mt_compat(struct evdev_dev *); /* 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 *); evdev_event_t evdev_ev_kbd_event; +/* 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 != 0)); +} + +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_utils.c b/sys/dev/evdev/evdev_utils.c index 47e5e64a3c42..860634299270 100644 --- a/sys/dev/evdev/evdev_utils.c +++ b/sys/dev/evdev/evdev_utils.c @@ -1,334 +1,333 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama * Copyright (c) 2015-2016 Vladimir Kondratyev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #define NONE KEY_RESERVED static uint16_t evdev_usb_scancodes[256] = { /* 0x00 - 0x27 */ NONE, NONE, NONE, NONE, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, /* 0x28 - 0x3f */ KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, /* 0x40 - 0x5f */ KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ, KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, KEY_SLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, /* 0x60 - 0x7f */ KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE, /* 0x80 - 0x9f */ KEY_VOLUMEUP, KEY_VOLUMEDOWN, NONE, NONE, NONE, KEY_KPCOMMA, NONE, KEY_RO, KEY_KATAKANAHIRAGANA, KEY_YEN,KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, NONE, NONE, NONE, KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0xa0 - 0xbf */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0xc0 - 0xdf */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0xe0 - 0xff */ KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_COFFEE, KEY_REFRESH, KEY_CALC, NONE, NONE, NONE, NONE, }; static uint16_t evdev_at_set1_scancodes[] = { /* 0x00 - 0x1f */ NONE, KEY_ESC, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_ENTER, KEY_LEFTCTRL, KEY_A, KEY_S, /* 0x20 - 0x3f */ KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_LEFTSHIFT, KEY_BACKSLASH, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_RIGHTSHIFT, NONE, KEY_LEFTALT, KEY_SPACE, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, /* 0x40 - 0x5f */ KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KPMINUS, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KPPLUS, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP0, KEY_KPDOT, NONE, NONE, NONE, KEY_F11, KEY_F12, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0x60 - 0x7f */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_KATAKANAHIRAGANA, NONE, NONE, KEY_RO, NONE, NONE, KEY_ZENKAKUHANKAKU, KEY_HIRAGANA, KEY_KATAKANA, KEY_HENKAN, NONE, KEY_MUHENKAN, NONE, KEY_YEN, KEY_KPCOMMA, NONE, /* 0x00 - 0x1f. 0xE0 prefixed */ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_PREVIOUSSONG, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_NEXTSONG, NONE, NONE, NONE, KEY_KPENTER, KEY_RIGHTCTRL, NONE, /* 0x20 - 0x3f. 0xE0 prefixed */ KEY_MUTE, KEY_CALC, KEY_PLAYPAUSE, NONE, KEY_STOPCD, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_VOLUMEDOWN, NONE, KEY_VOLUMEUP, NONE, KEY_HOMEPAGE, NONE, NONE, KEY_KPASTERISK, NONE, KEY_SYSRQ, KEY_RIGHTALT, NONE, NONE, NONE, NONE, NONE, NONE, NONE, /* 0x40 - 0x5f. 0xE0 prefixed */ NONE, NONE, NONE, NONE, NONE, NONE, KEY_PAUSE, KEY_HOME, KEY_UP, KEY_PAGEUP, NONE, KEY_LEFT, NONE, KEY_RIGHT, NONE, KEY_END, KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_MENU, KEY_POWER, KEY_SLEEP, /* 0x60 - 0x7f. 0xE0 prefixed */ NONE, NONE, NONE, KEY_WAKEUP, NONE, KEY_SEARCH, KEY_BOOKMARKS, KEY_REFRESH, KEY_STOP, KEY_FORWARD, KEY_BACK, KEY_COMPUTER, KEY_MAIL, KEY_MEDIA, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, }; static uint16_t evdev_mouse_button_codes[] = { BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, BTN_SIDE, BTN_EXTRA, BTN_FORWARD, BTN_BACK, BTN_TASK, }; static uint16_t evdev_led_codes[] = { LED_CAPSL, /* CLKED */ LED_NUML, /* NLKED */ LED_SCROLLL, /* SLKED */ }; inline uint16_t evdev_hid2key(int scancode) { return evdev_usb_scancodes[scancode]; } inline void evdev_support_all_known_keys(struct evdev_dev *evdev) { size_t i; for (i = KEY_RESERVED; i < nitems(evdev_at_set1_scancodes); i++) if (evdev_at_set1_scancodes[i] != NONE) evdev_support_key(evdev, evdev_at_set1_scancodes[i]); } inline uint16_t evdev_scancode2key(int *state, int scancode) { uint16_t keycode; /* translate the scan code into a keycode */ keycode = evdev_at_set1_scancodes[scancode & 0x7f]; switch (*state) { case 0x00: /* normal scancode */ switch(scancode) { case 0xE0: case 0xE1: *state = scancode; return (NONE); } break; case 0xE0: /* 0xE0 prefix */ *state = 0; keycode = evdev_at_set1_scancodes[0x80 + (scancode & 0x7f)]; break; case 0xE1: /* 0xE1 prefix */ /* * The pause/break key on the 101 keyboard produces: * E1-1D-45 E1-9D-C5 * Ctrl-pause/break produces: * E0-46 E0-C6 (See above.) */ *state = 0; if ((scancode & 0x7f) == 0x1D) *state = 0x1D; return (NONE); /* NOT REACHED */ case 0x1D: /* pause / break */ *state = 0; if (scancode != 0x45) return (NONE); keycode = KEY_PAUSE; break; } return (keycode); } void evdev_push_mouse_btn(struct evdev_dev *evdev, int buttons) { size_t i; for (i = 0; i < nitems(evdev_mouse_button_codes); i++) - evdev_push_event(evdev, EV_KEY, evdev_mouse_button_codes[i], - (buttons & (1 << i)) != 0); + evdev_push_key(evdev, evdev_mouse_button_codes[i], + buttons & (1 << i)); } void evdev_push_leds(struct evdev_dev *evdev, int leds) { size_t i; /* Some drivers initialize leds before evdev */ if (evdev == NULL) return; for (i = 0; i < nitems(evdev_led_codes); i++) - evdev_push_event(evdev, EV_LED, evdev_led_codes[i], - (leds & (1 << i)) != 0); + evdev_push_led(evdev, evdev_led_codes[i], leds & (1 << i)); } void evdev_push_repeats(struct evdev_dev *evdev, keyboard_t *kbd) { /* Some drivers initialize typematics before evdev */ if (evdev == NULL) return; evdev_push_event(evdev, EV_REP, REP_DELAY, kbd->kb_delay1); evdev_push_event(evdev, EV_REP, REP_PERIOD, kbd->kb_delay2); } void evdev_ev_kbd_event(struct evdev_dev *evdev, void *softc, uint16_t type, uint16_t code, int32_t value) { keyboard_t *kbd = (keyboard_t *)softc; int delay[2], leds, oleds; size_t i; if (type == EV_LED) { leds = oleds = KBD_LED_VAL(kbd); for (i = 0; i < nitems(evdev_led_codes); i++) { if (evdev_led_codes[i] == code) { if (value) leds |= 1 << i; else leds &= ~(1 << i); if (leds != oleds) kbdd_ioctl(kbd, KDSETLED, (caddr_t)&leds); break; } } } else if (type == EV_REP && code == REP_DELAY) { delay[0] = value; delay[1] = kbd->kb_delay2; kbdd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); } else if (type == EV_REP && code == REP_PERIOD) { delay[0] = kbd->kb_delay1; delay[1] = value; kbdd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); } }