Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/evdev/evdev.c
Show All 25 Lines | |||||
* | * | ||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include "opt_evdev.h" | #include "opt_evdev.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/bitstring.h> | #include <sys/bitstring.h> | ||||
#include <sys/ck.h> | |||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/epoch.h> | |||||
#include <sys/kdb.h> | #include <sys/kdb.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/sx.h> | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <dev/evdev/evdev.h> | #include <dev/evdev/evdev.h> | ||||
#include <dev/evdev/evdev_private.h> | #include <dev/evdev/evdev_private.h> | ||||
#include <dev/evdev/input.h> | #include <dev/evdev/input.h> | ||||
#ifdef EVDEV_DEBUG | #ifdef EVDEV_DEBUG | ||||
▲ Show 20 Lines • Show All 242 Lines • ▼ Show 20 Lines | |||||
evdev_register_common(struct evdev_dev *evdev) | evdev_register_common(struct evdev_dev *evdev) | ||||
{ | { | ||||
int ret; | int ret; | ||||
debugf(evdev, "%s: registered evdev provider: %s <%s>\n", | debugf(evdev, "%s: registered evdev provider: %s <%s>\n", | ||||
evdev->ev_shortname, evdev->ev_name, evdev->ev_serial); | evdev->ev_shortname, evdev->ev_name, evdev->ev_serial); | ||||
/* Initialize internal structures */ | /* Initialize internal structures */ | ||||
LIST_INIT(&evdev->ev_clients); | CK_SLIST_INIT(&evdev->ev_clients); | ||||
sx_init(&evdev->ev_list_lock, "evsx"); | |||||
if (evdev_event_supported(evdev, EV_REP) && | if (evdev_event_supported(evdev, EV_REP) && | ||||
bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) { | bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) { | ||||
/* Initialize callout */ | /* Initialize callout */ | ||||
callout_init_mtx(&evdev->ev_rep_callout, evdev->ev_lock, 0); | callout_init_mtx(&evdev->ev_rep_callout, | ||||
evdev->ev_state_lock, 0); | |||||
if (evdev->ev_rep[REP_DELAY] == 0 && | if (evdev->ev_rep[REP_DELAY] == 0 && | ||||
evdev->ev_rep[REP_PERIOD] == 0) { | evdev->ev_rep[REP_PERIOD] == 0) { | ||||
/* Supply default values */ | /* Supply default values */ | ||||
evdev->ev_rep[REP_DELAY] = 250; | evdev->ev_rep[REP_DELAY] = 250; | ||||
evdev->ev_rep[REP_PERIOD] = 33; | evdev->ev_rep[REP_PERIOD] = 33; | ||||
} | } | ||||
} | } | ||||
Show All 15 Lines | evdev_register_common(struct evdev_dev *evdev) | ||||
ret = evdev_cdev_create(evdev); | ret = evdev_cdev_create(evdev); | ||||
if (ret != 0) | if (ret != 0) | ||||
goto bail_out; | goto bail_out; | ||||
/* Create sysctls (for device enumeration without /dev/input access rights) */ | /* Create sysctls (for device enumeration without /dev/input access rights) */ | ||||
evdev_sysctl_create(evdev); | evdev_sysctl_create(evdev); | ||||
bail_out: | bail_out: | ||||
if (ret != 0) | |||||
sx_destroy(&evdev->ev_list_lock); | |||||
return (ret); | return (ret); | ||||
} | } | ||||
int | int | ||||
evdev_register(struct evdev_dev *evdev) | evdev_register(struct evdev_dev *evdev) | ||||
{ | { | ||||
int ret; | 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_lock_type = EV_LOCK_INTERNAL; | ||||
evdev->ev_lock = &evdev->ev_mtx; | evdev->ev_state_lock = &evdev->ev_mtx; | ||||
mtx_init(&evdev->ev_mtx, "evmtx", NULL, MTX_DEF); | mtx_init(&evdev->ev_mtx, "evmtx", NULL, MTX_DEF); | ||||
ret = evdev_register_common(evdev); | ret = evdev_register_common(evdev); | ||||
if (ret != 0) | if (ret != 0) | ||||
mtx_destroy(&evdev->ev_mtx); | mtx_destroy(&evdev->ev_mtx); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
int | int | ||||
evdev_register_mtx(struct evdev_dev *evdev, struct mtx *mtx) | evdev_register_mtx(struct evdev_dev *evdev, struct mtx *mtx) | ||||
{ | { | ||||
evdev->ev_lock_type = EV_LOCK_MTX; | evdev->ev_lock_type = EV_LOCK_MTX; | ||||
evdev->ev_lock = mtx; | evdev->ev_state_lock = mtx; | ||||
return (evdev_register_common(evdev)); | return (evdev_register_common(evdev)); | ||||
} | } | ||||
int | int | ||||
evdev_unregister(struct evdev_dev *evdev) | evdev_unregister(struct evdev_dev *evdev) | ||||
{ | { | ||||
struct evdev_client *client, *tmp; | struct evdev_client *client, *tmp; | ||||
int ret; | int ret; | ||||
debugf(evdev, "%s: unregistered evdev provider: %s\n", | debugf(evdev, "%s: unregistered evdev provider: %s\n", | ||||
evdev->ev_shortname, evdev->ev_name); | evdev->ev_shortname, evdev->ev_name); | ||||
sysctl_ctx_free(&evdev->ev_sysctl_ctx); | sysctl_ctx_free(&evdev->ev_sysctl_ctx); | ||||
EVDEV_LOCK(evdev); | EVDEV_LIST_LOCK(evdev); | ||||
evdev->ev_cdev->si_drv1 = NULL; | evdev->ev_cdev->si_drv1 = NULL; | ||||
/* Wake up sleepers */ | /* Wake up sleepers */ | ||||
LIST_FOREACH_SAFE(client, &evdev->ev_clients, ec_link, tmp) { | CK_SLIST_FOREACH_SAFE(client, &evdev->ev_clients, ec_link, tmp) { | ||||
evdev_revoke_client(client); | evdev_revoke_client(client); | ||||
evdev_dispose_client(evdev, client); | evdev_dispose_client(evdev, client); | ||||
EVDEV_CLIENT_LOCKQ(client); | EVDEV_CLIENT_LOCKQ(client); | ||||
evdev_notify_event(client); | evdev_notify_event(client); | ||||
EVDEV_CLIENT_UNLOCKQ(client); | EVDEV_CLIENT_UNLOCKQ(client); | ||||
} | } | ||||
EVDEV_UNLOCK(evdev); | EVDEV_LIST_UNLOCK(evdev); | ||||
/* destroy_dev can sleep so release lock */ | /* release lock to avoid deadlock with evdev_dtor */ | ||||
ret = evdev_cdev_destroy(evdev); | ret = evdev_cdev_destroy(evdev); | ||||
evdev->ev_cdev = NULL; | evdev->ev_cdev = NULL; | ||||
if (ret == 0 && evdev->ev_lock_type == EV_LOCK_INTERNAL) | sx_destroy(&evdev->ev_list_lock); | ||||
if (ret == 0 && evdev->ev_lock_type != EV_LOCK_MTX) | |||||
mtx_destroy(&evdev->ev_mtx); | mtx_destroy(&evdev->ev_mtx); | ||||
evdev_free_absinfo(evdev->ev_absinfo); | evdev_free_absinfo(evdev->ev_absinfo); | ||||
evdev_mt_free(evdev); | evdev_mt_free(evdev); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 287 Lines • ▼ Show 20 Lines | case EV_KEY: | ||||
if (!bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) { | if (!bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) { | ||||
/* Detect driver key repeats. */ | /* Detect driver key repeats. */ | ||||
if (bit_test(evdev->ev_key_states, code) && | if (bit_test(evdev->ev_key_states, code) && | ||||
*value == KEY_EVENT_DOWN) | *value == KEY_EVENT_DOWN) | ||||
*value = KEY_EVENT_REPEAT; | *value = KEY_EVENT_REPEAT; | ||||
} else { | } else { | ||||
/* Start/stop callout for evdev repeats */ | /* Start/stop callout for evdev repeats */ | ||||
if (bit_test(evdev->ev_key_states, code) == !*value && | if (bit_test(evdev->ev_key_states, code) == !*value && | ||||
!LIST_EMPTY(&evdev->ev_clients)) { | !CK_SLIST_EMPTY(&evdev->ev_clients)) { | ||||
if (*value == KEY_EVENT_DOWN) | if (*value == KEY_EVENT_DOWN) | ||||
evdev_start_repeat(evdev, code); | evdev_start_repeat(evdev, code); | ||||
else | else | ||||
evdev_stop_repeat(evdev); | evdev_stop_repeat(evdev); | ||||
} | } | ||||
} | } | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | evdev_sparse_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | ||||
evdev->ev_report_opened = true; | evdev->ev_report_opened = true; | ||||
return (EV_REPORT_EVENT); | return (EV_REPORT_EVENT); | ||||
} | } | ||||
static void | static void | ||||
evdev_propagate_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | evdev_propagate_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | ||||
int32_t value) | int32_t value) | ||||
{ | { | ||||
struct epoch_tracker et; | |||||
struct evdev_client *client; | struct evdev_client *client; | ||||
debugf(evdev, "%s pushed event %d/%d/%d", | debugf(evdev, "%s pushed event %d/%d/%d", | ||||
evdev->ev_shortname, type, code, value); | evdev->ev_shortname, type, code, value); | ||||
EVDEV_LOCK_ASSERT(evdev); | EVDEV_LOCK_ASSERT(evdev); | ||||
/* Propagate event through all clients */ | /* Propagate event through all clients */ | ||||
LIST_FOREACH(client, &evdev->ev_clients, ec_link) { | 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) | if (evdev->ev_grabber != NULL && evdev->ev_grabber != client) | ||||
continue; | continue; | ||||
EVDEV_CLIENT_LOCKQ(client); | EVDEV_CLIENT_LOCKQ(client); | ||||
evdev_client_push(client, type, code, value); | evdev_client_push(client, type, code, value); | ||||
if (type == EV_SYN && code == SYN_REPORT) | if (type == EV_SYN && code == SYN_REPORT) | ||||
evdev_notify_event(client); | evdev_notify_event(client); | ||||
EVDEV_CLIENT_UNLOCKQ(client); | EVDEV_CLIENT_UNLOCKQ(client); | ||||
} | } | ||||
if (evdev->ev_lock_type == EV_LOCK_INTERNAL) | |||||
epoch_exit_preempt(INPUT_EPOCH, &et); | |||||
evdev->ev_event_count++; | evdev->ev_event_count++; | ||||
} | } | ||||
void | void | ||||
evdev_send_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | evdev_send_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | ||||
int32_t value) | int32_t value) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
evdev_inject_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | evdev_inject_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, | ||||
int32_t value) | int32_t value) | ||||
{ | { | ||||
struct epoch_tracker et; | |||||
int ret = 0; | int ret = 0; | ||||
switch (type) { | switch (type) { | ||||
case EV_REP: | case EV_REP: | ||||
/* evdev repeats should not be processed by hardware driver */ | /* evdev repeats should not be processed by hardware driver */ | ||||
if (bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) | if (bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) | ||||
goto push; | goto push; | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
Show All 14 Lines | if (type == EV_LED || type == EV_REP) | ||||
break; | break; | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case EV_SYN: | case EV_SYN: | ||||
case EV_KEY: | case EV_KEY: | ||||
case EV_REL: | case EV_REL: | ||||
case EV_ABS: | case EV_ABS: | ||||
case EV_SW: | case EV_SW: | ||||
push: | push: | ||||
if (evdev->ev_lock_type != EV_LOCK_INTERNAL) | if (evdev->ev_lock_type == EV_LOCK_MTX) | ||||
EVDEV_LOCK(evdev); | 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); | ret = evdev_push_event(evdev, type, code, value); | ||||
if (evdev->ev_lock_type != EV_LOCK_INTERNAL) | if (evdev->ev_lock_type == EV_LOCK_MTX) | ||||
EVDEV_UNLOCK(evdev); | EVDEV_UNLOCK(evdev); | ||||
else if (evdev->ev_lock_type == EV_LOCK_EXT_EPOCH) | |||||
epoch_exit_preempt(INPUT_EPOCH, &et); | |||||
break; | break; | ||||
default: | default: | ||||
ret = EINVAL; | ret = EINVAL; | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
int | int | ||||
evdev_register_client(struct evdev_dev *evdev, struct evdev_client *client) | evdev_register_client(struct evdev_dev *evdev, struct evdev_client *client) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
debugf(evdev, "adding new client for device %s", evdev->ev_shortname); | debugf(evdev, "adding new client for device %s", evdev->ev_shortname); | ||||
EVDEV_LOCK_ASSERT(evdev); | EVDEV_LIST_LOCK_ASSERT(evdev); | ||||
if (LIST_EMPTY(&evdev->ev_clients) && evdev->ev_methods != NULL && | if (CK_SLIST_EMPTY(&evdev->ev_clients) && evdev->ev_methods != NULL && | ||||
evdev->ev_methods->ev_open != NULL) { | evdev->ev_methods->ev_open != NULL) { | ||||
debugf(evdev, "calling ev_open() on device %s", | debugf(evdev, "calling ev_open() on device %s", | ||||
evdev->ev_shortname); | evdev->ev_shortname); | ||||
ret = evdev->ev_methods->ev_open(evdev); | ret = evdev->ev_methods->ev_open(evdev); | ||||
} | } | ||||
if (ret == 0) | if (ret == 0) | ||||
LIST_INSERT_HEAD(&evdev->ev_clients, client, ec_link); | CK_SLIST_INSERT_HEAD(&evdev->ev_clients, client, ec_link); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
void | void | ||||
evdev_dispose_client(struct evdev_dev *evdev, struct evdev_client *client) | evdev_dispose_client(struct evdev_dev *evdev, struct evdev_client *client) | ||||
{ | { | ||||
debugf(evdev, "removing client for device %s", evdev->ev_shortname); | debugf(evdev, "removing client for device %s", evdev->ev_shortname); | ||||
EVDEV_LOCK_ASSERT(evdev); | EVDEV_LIST_LOCK_ASSERT(evdev); | ||||
LIST_REMOVE(client, ec_link); | CK_SLIST_REMOVE(&evdev->ev_clients, client, evdev_client, ec_link); | ||||
if (LIST_EMPTY(&evdev->ev_clients)) { | if (CK_SLIST_EMPTY(&evdev->ev_clients)) { | ||||
if (evdev->ev_methods != NULL && | if (evdev->ev_methods != NULL && | ||||
evdev->ev_methods->ev_close != NULL) | evdev->ev_methods->ev_close != NULL) | ||||
(void)evdev->ev_methods->ev_close(evdev); | (void)evdev->ev_methods->ev_close(evdev); | ||||
if (evdev_event_supported(evdev, EV_REP) && | if (evdev_event_supported(evdev, EV_REP) && | ||||
bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) | bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) { | ||||
if (evdev->ev_lock_type != EV_LOCK_MTX) | |||||
EVDEV_LOCK(evdev); | |||||
evdev_stop_repeat(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); | evdev_release_client(evdev, client); | ||||
if (evdev->ev_lock_type != EV_LOCK_MTX) | |||||
EVDEV_UNLOCK(evdev); | |||||
} | } | ||||
int | int | ||||
evdev_grab_client(struct evdev_dev *evdev, struct evdev_client *client) | evdev_grab_client(struct evdev_dev *evdev, struct evdev_client *client) | ||||
{ | { | ||||
EVDEV_LOCK_ASSERT(evdev); | EVDEV_LOCK_ASSERT(evdev); | ||||
Show All 17 Lines | evdev_release_client(struct evdev_dev *evdev, struct evdev_client *client) | ||||
evdev->ev_grabber = NULL; | evdev->ev_grabber = NULL; | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
evdev_repeat_callout(void *arg) | evdev_repeat_callout(void *arg) | ||||
{ | { | ||||
struct epoch_tracker et; | |||||
struct evdev_dev *evdev = (struct evdev_dev *)arg; | 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_KEY, evdev->ev_rep_key, KEY_EVENT_REPEAT); | ||||
evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1); | 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]) | if (evdev->ev_rep[REP_PERIOD]) | ||||
callout_reset(&evdev->ev_rep_callout, | callout_reset(&evdev->ev_rep_callout, | ||||
evdev->ev_rep[REP_PERIOD] * hz / 1000, | evdev->ev_rep[REP_PERIOD] * hz / 1000, | ||||
evdev_repeat_callout, evdev); | evdev_repeat_callout, evdev); | ||||
else | else | ||||
evdev->ev_rep_key = KEY_RESERVED; | evdev->ev_rep_key = KEY_RESERVED; | ||||
} | } | ||||
Show All 28 Lines |