Changeset View
Standalone View
sys/dev/evdev/evdev_private.h
Show All 25 Lines | |||||
* | * | ||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#ifndef _DEV_EVDEV_EVDEV_PRIVATE_H | #ifndef _DEV_EVDEV_EVDEV_PRIVATE_H | ||||
#define _DEV_EVDEV_EVDEV_PRIVATE_H | #define _DEV_EVDEV_EVDEV_PRIVATE_H | ||||
#include <sys/bitstring.h> | #include <sys/bitstring.h> | ||||
#include <sys/ck.h> | |||||
#include <sys/epoch.h> | |||||
#include <sys/kbio.h> | #include <sys/kbio.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/selinfo.h> | #include <sys/selinfo.h> | ||||
#include <sys/sx.h> | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <dev/evdev/evdev.h> | #include <dev/evdev/evdev.h> | ||||
#include <dev/evdev/input.h> | #include <dev/evdev/input.h> | ||||
#include <dev/kbd/kbdreg.h> | #include <dev/kbd/kbdreg.h> | ||||
#define NAMELEN 80 | #define NAMELEN 80 | ||||
Show All 22 Lines | |||||
/* evdev clock IDs in Linux semantic */ | /* evdev clock IDs in Linux semantic */ | ||||
enum evdev_clock_id | enum evdev_clock_id | ||||
{ | { | ||||
EV_CLOCK_REALTIME = 0, /* UTC clock */ | EV_CLOCK_REALTIME = 0, /* UTC clock */ | ||||
EV_CLOCK_MONOTONIC, /* monotonic, stops on suspend */ | EV_CLOCK_MONOTONIC, /* monotonic, stops on suspend */ | ||||
EV_CLOCK_BOOTTIME /* monotonic, suspend-awared */ | 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 | enum evdev_lock_type | ||||
markj: I think a comment explaining the locking protocols would be useful here. After reading the code… | |||||
{ | { | ||||
EV_LOCK_INTERNAL = 0, /* Internal evdev mutex */ | EV_LOCK_INTERNAL = 0, /* Internal epoch */ | ||||
EV_LOCK_MTX, /* Driver`s mutex */ | EV_LOCK_MTX, /* Driver`s mutex */ | ||||
EV_LOCK_EXT_EPOCH, /* External epoch */ | |||||
Not Done Inline ActionsI think one EPOCH for all of EVDEV's will be plenty. Can you explain a bit why you think there will be a need for multiple EPOCH's. hselasky: I think one EPOCH for all of EVDEV's will be plenty. Can you explain a bit why you think there… | |||||
Done Inline ActionsFor now it reuses global_epoch_preempt; So the number of new epochs is 0. wulf: For now it reuses global_epoch_preempt; So the number of new epochs is 0. | |||||
Done Inline ActionsAnd external epoch is needed to avoid epoch recursion as hidbus uses the same technique to avoid LORs in interrupt handler. In this case evdev is called with global_epoch_preempt already entered. wulf: And external epoch is needed to avoid epoch recursion as hidbus uses the same technique to… | |||||
Not Done Inline ActionsBeware that number of EPOCHs in the system is limited: And that there is some overhead creating many epochs for small things like HID and EVDEV. If you can create one global EPOCH for these things related to input, that would be great. hselasky: Beware that number of EPOCHs in the system is limited:
sys/kern/subr_epoch.c:#define MAX_EPOCHS… | |||||
Done Inline ActionsDo you suggest to create dedicated input epoch and hard-code it's name into HID and EVDEV like network stack does? wulf: Do you suggest to create dedicated input epoch and hard-code it's name into HID and EVDEV like… | |||||
}; | }; | ||||
struct evdev_dev | struct evdev_dev | ||||
{ | { | ||||
char ev_name[NAMELEN]; | char ev_name[NAMELEN]; | ||||
char ev_shortname[NAMELEN]; | char ev_shortname[NAMELEN]; | ||||
char ev_serial[NAMELEN]; | char ev_serial[NAMELEN]; | ||||
struct cdev * ev_cdev; | struct cdev * ev_cdev; | ||||
int ev_unit; | int ev_unit; | ||||
enum evdev_lock_type ev_lock_type; | enum evdev_lock_type ev_lock_type; | ||||
struct mtx * ev_lock; | struct mtx * ev_state_lock; /* State lock */ | ||||
struct mtx ev_mtx; | struct mtx ev_mtx; /* Internal state lock */ | ||||
struct sx ev_list_lock; /* Client list lock */ | |||||
struct input_id ev_id; | struct input_id ev_id; | ||||
struct evdev_client * ev_grabber; | struct evdev_client * ev_grabber; /* (s) */ | ||||
size_t ev_report_size; | size_t ev_report_size; | ||||
/* Supported features: */ | /* Supported features: */ | ||||
bitstr_t bit_decl(ev_prop_flags, INPUT_PROP_CNT); | bitstr_t bit_decl(ev_prop_flags, INPUT_PROP_CNT); | ||||
bitstr_t bit_decl(ev_type_flags, EV_CNT); | bitstr_t bit_decl(ev_type_flags, EV_CNT); | ||||
bitstr_t bit_decl(ev_key_flags, KEY_CNT); | bitstr_t bit_decl(ev_key_flags, KEY_CNT); | ||||
bitstr_t bit_decl(ev_rel_flags, REL_CNT); | bitstr_t bit_decl(ev_rel_flags, REL_CNT); | ||||
bitstr_t bit_decl(ev_abs_flags, ABS_CNT); | bitstr_t bit_decl(ev_abs_flags, ABS_CNT); | ||||
bitstr_t bit_decl(ev_msc_flags, MSC_CNT); | bitstr_t bit_decl(ev_msc_flags, MSC_CNT); | ||||
bitstr_t bit_decl(ev_led_flags, LED_CNT); | bitstr_t bit_decl(ev_led_flags, LED_CNT); | ||||
bitstr_t bit_decl(ev_snd_flags, SND_CNT); | bitstr_t bit_decl(ev_snd_flags, SND_CNT); | ||||
bitstr_t bit_decl(ev_sw_flags, SW_CNT); | bitstr_t bit_decl(ev_sw_flags, SW_CNT); | ||||
struct input_absinfo * ev_absinfo; | struct input_absinfo * ev_absinfo; /* (s) */ | ||||
bitstr_t bit_decl(ev_flags, EVDEV_FLAG_CNT); | bitstr_t bit_decl(ev_flags, EVDEV_FLAG_CNT); | ||||
/* Repeat parameters & callout: */ | /* Repeat parameters & callout: */ | ||||
int ev_rep[REP_CNT]; | int ev_rep[REP_CNT]; /* (s) */ | ||||
struct callout ev_rep_callout; | struct callout ev_rep_callout; /* (s) */ | ||||
uint16_t ev_rep_key; | uint16_t ev_rep_key; /* (s) */ | ||||
/* State: */ | /* State: */ | ||||
bitstr_t bit_decl(ev_key_states, KEY_CNT); | bitstr_t bit_decl(ev_key_states, KEY_CNT); /* (s) */ | ||||
bitstr_t bit_decl(ev_led_states, LED_CNT); | bitstr_t bit_decl(ev_led_states, LED_CNT); /* (s) */ | ||||
bitstr_t bit_decl(ev_snd_states, SND_CNT); | bitstr_t bit_decl(ev_snd_states, SND_CNT); /* (s) */ | ||||
bitstr_t bit_decl(ev_sw_states, SW_CNT); | bitstr_t bit_decl(ev_sw_states, SW_CNT); /* (s) */ | ||||
bool ev_report_opened; | bool ev_report_opened; /* (s) */ | ||||
/* KDB state: */ | /* KDB state: */ | ||||
bool ev_kdb_active; | bool ev_kdb_active; | ||||
bitstr_t bit_decl(ev_kdb_led_states, LED_CNT); | bitstr_t bit_decl(ev_kdb_led_states, LED_CNT); | ||||
/* Multitouch protocol type B state: */ | /* Multitouch protocol type B state: */ | ||||
struct evdev_mt * ev_mt; | struct evdev_mt * ev_mt; /* (s) */ | ||||
/* Counters: */ | /* Counters: */ | ||||
uint64_t ev_event_count; | uint64_t ev_event_count; /* (s) */ | ||||
uint64_t ev_report_count; | uint64_t ev_report_count; /* (s) */ | ||||
/* Parent driver callbacks: */ | /* Parent driver callbacks: */ | ||||
const struct evdev_methods * ev_methods; | const struct evdev_methods * ev_methods; | ||||
void * ev_softc; | void * ev_softc; | ||||
/* Sysctl: */ | /* Sysctl: */ | ||||
struct sysctl_ctx_list ev_sysctl_ctx; | struct sysctl_ctx_list ev_sysctl_ctx; | ||||
LIST_ENTRY(evdev_dev) ev_link; | LIST_ENTRY(evdev_dev) ev_link; | ||||
LIST_HEAD(, evdev_client) ev_clients; | CK_SLIST_HEAD(, evdev_client) ev_clients; /* (l) */ | ||||
}; | }; | ||||
#define SYSTEM_CONSOLE_LOCK &Giant | #define SYSTEM_CONSOLE_LOCK &Giant | ||||
#define EVDEV_LOCK(evdev) mtx_lock((evdev)->ev_lock) | #define EVDEV_LOCK(evdev) mtx_lock((evdev)->ev_state_lock) | ||||
#define EVDEV_UNLOCK(evdev) mtx_unlock((evdev)->ev_lock) | #define EVDEV_UNLOCK(evdev) mtx_unlock((evdev)->ev_state_lock) | ||||
#define EVDEV_LOCK_ASSERT(evdev) do { \ | #define EVDEV_LOCK_ASSERT(evdev) do { \ | ||||
if ((evdev)->ev_lock != SYSTEM_CONSOLE_LOCK) \ | if ((evdev)->ev_state_lock != SYSTEM_CONSOLE_LOCK) \ | ||||
mtx_assert((evdev)->ev_lock, MA_OWNED); \ | mtx_assert((evdev)->ev_state_lock, MA_OWNED); \ | ||||
} while (0) | } while (0) | ||||
#define EVDEV_ENTER(evdev) do { \ | #define EVDEV_ENTER(evdev) do { \ | ||||
if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL) \ | if ((evdev)->ev_lock_type != EV_LOCK_MTX) \ | ||||
EVDEV_LOCK(evdev); \ | EVDEV_LOCK(evdev); \ | ||||
else \ | else \ | ||||
EVDEV_LOCK_ASSERT(evdev); \ | EVDEV_LOCK_ASSERT(evdev); \ | ||||
} while (0) | } while (0) | ||||
#define EVDEV_EXIT(evdev) do { \ | #define EVDEV_EXIT(evdev) do { \ | ||||
if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL) \ | if ((evdev)->ev_lock_type != EV_LOCK_MTX) \ | ||||
EVDEV_UNLOCK(evdev); \ | EVDEV_UNLOCK(evdev); \ | ||||
} while (0) | } 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) | |||||
struct evdev_client | struct evdev_client | ||||
{ | { | ||||
struct evdev_dev * ec_evdev; | struct evdev_dev * ec_evdev; | ||||
struct mtx ec_buffer_mtx; | struct mtx ec_buffer_mtx; /* Client queue lock */ | ||||
size_t ec_buffer_size; | size_t ec_buffer_size; | ||||
size_t ec_buffer_head; | size_t ec_buffer_head; /* (q) */ | ||||
size_t ec_buffer_tail; | size_t ec_buffer_tail; /* (q) */ | ||||
size_t ec_buffer_ready; | size_t ec_buffer_ready; /* (q) */ | ||||
enum evdev_clock_id ec_clock_id; | enum evdev_clock_id ec_clock_id; | ||||
struct selinfo ec_selp; | struct selinfo ec_selp; /* (q) */ | ||||
struct sigio * ec_sigio; | struct sigio * ec_sigio; | ||||
bool ec_async; | bool ec_async; /* (q) */ | ||||
bool ec_revoked; | bool ec_revoked; /* (l) */ | ||||
bool ec_blocked; | bool ec_blocked; /* (q) */ | ||||
bool ec_selected; | bool ec_selected; /* (q) */ | ||||
LIST_ENTRY(evdev_client) ec_link; | CK_SLIST_ENTRY(evdev_client) ec_link; /* (l) */ | ||||
struct input_event ec_buffer[]; | struct input_event ec_buffer[]; /* (q) */ | ||||
}; | }; | ||||
#define EVDEV_CLIENT_LOCKQ(client) mtx_lock(&(client)->ec_buffer_mtx) | #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_UNLOCKQ(client) mtx_unlock(&(client)->ec_buffer_mtx) | ||||
#define EVDEV_CLIENT_LOCKQ_ASSERT(client) \ | #define EVDEV_CLIENT_LOCKQ_ASSERT(client) \ | ||||
mtx_assert(&(client)->ec_buffer_mtx, MA_OWNED) | mtx_assert(&(client)->ec_buffer_mtx, MA_OWNED) | ||||
#define EVDEV_CLIENT_EMPTYQ(client) \ | #define EVDEV_CLIENT_EMPTYQ(client) \ | ||||
((client)->ec_buffer_head == (client)->ec_buffer_ready) | ((client)->ec_buffer_head == (client)->ec_buffer_ready) | ||||
Show All 37 Lines |
I think a comment explaining the locking protocols would be useful here. After reading the code for some time it makes sense to me, but it is not obvious why you write
instead of
for instance.