diff options
Diffstat (limited to 'freebsd/sys/dev/evdev')
-rw-r--r-- | freebsd/sys/dev/evdev/cdev.c | 13 | ||||
-rw-r--r-- | freebsd/sys/dev/evdev/evdev.c | 44 | ||||
-rw-r--r-- | freebsd/sys/dev/evdev/evdev_private.h | 12 |
3 files changed, 68 insertions, 1 deletions
diff --git a/freebsd/sys/dev/evdev/cdev.c b/freebsd/sys/dev/evdev/cdev.c index 5ae14fed..a9370e7e 100644 --- a/freebsd/sys/dev/evdev/cdev.c +++ b/freebsd/sys/dev/evdev/cdev.c @@ -351,6 +351,19 @@ evdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, if (client->ec_revoked || evdev == NULL) return (ENODEV); + /* + * Fix evdev state corrupted with discarding of kdb events. + * EVIOCGKEY and EVIOCGLED ioctls can suffer from this. + */ + if (evdev->ev_kdb_active) { + EVDEV_LOCK(evdev); + if (evdev->ev_kdb_active) { + evdev->ev_kdb_active = false; + evdev_restore_after_kdb(evdev); + } + EVDEV_UNLOCK(evdev); + } + /* file I/O ioctl handling */ switch (cmd) { case FIOSETOWN: diff --git a/freebsd/sys/dev/evdev/evdev.c b/freebsd/sys/dev/evdev/evdev.c index a355ec50..63e651a2 100644 --- a/freebsd/sys/dev/evdev/evdev.c +++ b/freebsd/sys/dev/evdev/evdev.c @@ -34,9 +34,11 @@ #include <sys/param.h> #include <sys/bitstring.h> #include <sys/conf.h> +#include <sys/kdb.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/module.h> +#include <sys/proc.h> #include <sys/sysctl.h> #include <sys/systm.h> @@ -769,6 +771,30 @@ evdev_send_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, } } +void +evdev_restore_after_kdb(struct evdev_dev *evdev) +{ + int code; + + EVDEV_LOCK_ASSERT(evdev); + + /* Report postponed leds */ + for (code = 0; code < LED_CNT; code++) + if (bit_test(evdev->ev_kdb_led_states, code)) + evdev_send_event(evdev, EV_LED, code, + !bit_test(evdev->ev_led_states, code)); + bit_nclear(evdev->ev_kdb_led_states, 0, LED_MAX); + + /* Release stuck keys (CTRL + ALT + ESC) */ + evdev_stop_repeat(evdev); + for (code = 0; code < KEY_CNT; code++) { + if (bit_test(evdev->ev_key_states, code)) { + evdev_send_event(evdev, EV_KEY, code, KEY_EVENT_UP); + evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1); + } + } +} + int evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) @@ -777,8 +803,26 @@ evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, if (evdev_check_event(evdev, type, code, value) != 0) return (EINVAL); + /* + * Discard all but LEDs kdb events as unrelated to userspace. + * Aggregate LED updates and postpone reporting until kdb deactivation. + */ + if (kdb_active || SCHEDULER_STOPPED()) { + evdev->ev_kdb_active = true; + if (type == EV_LED) + bit_set(evdev->ev_kdb_led_states, + bit_test(evdev->ev_led_states, code) != value); + return (0); + } + EVDEV_ENTER(evdev); + /* Fix evdev state corrupted with discarding of kdb events */ + if (evdev->ev_kdb_active) { + evdev->ev_kdb_active = false; + evdev_restore_after_kdb(evdev); + } + evdev_modify_event(evdev, type, code, &value); if (type == EV_SYN && code == SYN_REPORT && bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL)) diff --git a/freebsd/sys/dev/evdev/evdev_private.h b/freebsd/sys/dev/evdev/evdev_private.h index 05206a9d..71bdecaa 100644 --- a/freebsd/sys/dev/evdev/evdev_private.h +++ b/freebsd/sys/dev/evdev/evdev_private.h @@ -117,6 +117,10 @@ struct evdev_dev bitstr_t bit_decl(ev_sw_states, SW_CNT); bool ev_report_opened; + /* KDB state: */ + bool ev_kdb_active; + bitstr_t bit_decl(ev_kdb_led_states, LED_CNT); + /* Multitouch protocol type B state: */ struct evdev_mt * ev_mt; @@ -132,9 +136,14 @@ struct evdev_dev LIST_HEAD(, evdev_client) ev_clients; }; +#define SYSTEM_CONSOLE_LOCK &Giant + #define EVDEV_LOCK(evdev) mtx_lock((evdev)->ev_lock) #define EVDEV_UNLOCK(evdev) mtx_unlock((evdev)->ev_lock) -#define EVDEV_LOCK_ASSERT(evdev) mtx_assert((evdev)->ev_lock, MA_OWNED) +#define EVDEV_LOCK_ASSERT(evdev) do { \ + if ((evdev)->ev_lock != SYSTEM_CONSOLE_LOCK) \ + mtx_assert((evdev)->ev_lock, MA_OWNED); \ +} while (0) #define EVDEV_ENTER(evdev) do { \ if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL) \ EVDEV_LOCK(evdev); \ @@ -185,6 +194,7 @@ 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 *); |