Commit 8006479c authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: implement proper locking in input core

Also add some kerneldoc documentation to input.h
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 501cc54c
......@@ -17,10 +17,10 @@
#include <linux/major.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
......@@ -31,273 +31,478 @@ MODULE_LICENSE("GPL");
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
/*
* input_mutex protects access to both input_dev_list and input_handler_list.
* This also causes input_[un]register_device and input_[un]register_handler
* be mutually exclusive which simplifies locking in drivers implementing
* input handlers.
*/
static DEFINE_MUTEX(input_mutex);
static struct input_handler *input_table[8];
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event()
static inline int is_event_supported(unsigned int code,
unsigned long *bm, unsigned int max)
{
return code <= max && test_bit(code, bm);
}
static int input_defuzz_abs_event(int value, int old_val, int fuzz)
{
if (fuzz) {
if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
return old_val;
if (value > old_val - fuzz && value < old_val + fuzz)
return (old_val * 3 + value) / 4;
if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
return (old_val + value) / 2;
}
return value;
}
/*
* Pass event through all open handles. This function is called with
* dev->event_lock held and interrupts disabled. Because of that we
* do not need to use rcu_read_lock() here although we are using RCU
* to access handle list. Note that because of that write-side uses
* synchronize_sched() instead of synchronize_ru().
*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
struct input_handle *handle = rcu_dereference(dev->grab);
if (type > EV_MAX || !test_bit(type, dev->evbit))
return;
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value);
}
add_input_randomness(type, code, value);
/*
* Generate software autorepeat event. Note that we take
* dev->event_lock here to avoid racing with input_event
* which may cause keys get "stuck".
*/
static void input_repeat_key(unsigned long data)
{
struct input_dev *dev = (void *) data;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
if (test_bit(dev->repeat_key, dev->key) &&
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
if (dev->sync) {
/*
* Only send SYN_REPORT if we are not in a middle
* of driver parsing a new hardware packet.
* Otherwise assume that the driver will send
* SYN_REPORT once it's done.
*/
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
}
if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies +
msecs_to_jiffies(dev->rep[REP_PERIOD]));
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
static void input_start_autorepeat(struct input_dev *dev, int code)
{
if (test_bit(EV_REP, dev->evbit) &&
dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
dev->timer.data) {
dev->repeat_key = code;
mod_timer(&dev->timer,
jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}
}
#define INPUT_IGNORE_EVENT 0
#define INPUT_PASS_TO_HANDLERS 1
#define INPUT_PASS_TO_DEVICE 2
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
if (dev->event)
dev->event(dev, type, code, value);
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (dev->sync)
return;
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return;
if (value == 2)
break;
change_bit(code, dev->key);
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
return;
change_bit(code, dev->sw);
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
if (code > ABS_MAX || !test_bit(code, dev->absbit))
return;
if (dev->absfuzz[code]) {
if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
return;
if ((value > dev->abs[code] - dev->absfuzz[code]) &&
(value < dev->abs[code] + dev->absfuzz[code]))
value = (dev->abs[code] * 3 + value) >> 2;
if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] << 1)))
value = (dev->abs[code] + value) >> 1;
}
if (dev->abs[code] == value)
return;
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
case EV_REL:
if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
return;
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (code > MSC_MAX || !test_bit(code, dev->mscbit))
return;
if (dev->event)
dev->event(dev, type, code, value);
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
return;
change_bit(code, dev->led);
if (dev->event)
dev->event(dev, type, code, value);
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (code > SND_MAX || !test_bit(code, dev->sndbit))
return;
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
change_bit(code, dev->snd);
if (dev->event)
dev->event(dev, type, code, value);
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code > REP_MAX || value < 0 || dev->rep[code] == value)
return;
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
if (dev->event)
dev->event(dev, type, code, value);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value < 0)
return;
if (dev->event)
dev->event(dev, type, code, value);
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
}
if (type != EV_SYN)
dev->sync = 0;
if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
EXPORT_SYMBOL(input_event);
/**
* input_inject_event() - send input event from input handler
* @handle: input handle to send event through
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* Similar to input_event() but will ignore event if device is "grabbed" and handle
* injecting event is not the one that owns the device.
* This function should be used by drivers implementing various input
* devices. See also input_inject_event().
*/
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
if (!handle->dev->grab || handle->dev->grab == handle)
input_event(handle->dev, type, code, value);
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_inject_event);
EXPORT_SYMBOL(input_event);
static void input_repeat_key(unsigned long data)
/**
* input_inject_event() - send input event from input handler
* @handle: input handle to send event through
* @type: type of the event
* @code: event code
* @value: value of the event
*
* Similar to input_event() but will ignore event if device is
* "grabbed" and handle injecting event is not the one that owns
* the device.
*/
void input_inject_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_dev *dev = (void *) data;
struct input_dev *dev = handle->dev;
struct input_handle *grab;
unsigned long flags;
if (!test_bit(dev->repeat_key, dev->key))
return;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
input_event(dev, EV_KEY, dev->repeat_key, 2);
input_sync(dev);
grab = rcu_dereference(dev->grab);
if (!grab || grab == handle)
input_handle_event(dev, type, code, value);
if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_inject_event);
/**
* input_grab_device - grabs device for exclusive use
* @handle: input handle that wants to own the device
*
* When a device is grabbed by an input handle all events generated by
* the device are delivered only to this handle. Also events injected
* by other input handles are ignored while device is grabbed.
*/
int input_grab_device(struct input_handle *handle)
{
if (handle->dev->grab)
return -EBUSY;
struct input_dev *dev = handle->dev;
int retval;
handle->dev->grab = handle;
return 0;
retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;
if (dev->grab) {
retval = -EBUSY;
goto out;
}
rcu_assign_pointer(dev->grab, handle);
/*
* Not using synchronize_rcu() because read-side is protected
* by a spinlock with interrupts off instead of rcu_read_lock().
*/
synchronize_sched();
out:
mutex_unlock(&dev->mutex);
return retval;
}
EXPORT_SYMBOL(input_grab_device);
void input_release_device(struct input_handle *handle)
static void __input_release_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
if (dev->grab == handle) {
dev->grab = NULL;
rcu_assign_pointer(dev->grab, NULL);
/* Make sure input_pass_event() notices that grab is gone */
synchronize_sched();
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->handler->start)
if (handle->open && handle->handler->start)
handle->handler->start(handle);
}
}
/**
* input_release_device - release previously grabbed device
* @handle: input handle that owns the device
*
* Releases previously grabbed device so that other input handles can
* start receiving input events. Upon release all handlers attached
* to the device have their start() method called so they have a change
* to synchronize device state with the rest of the system.
*/
void input_release_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
mutex_lock(&dev->mutex);
__input_release_device(handle);
mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL(input_release_device);
/**
* input_open_device - open input device
* @handle: handle through which device is being accessed
*
* This function should be called by input handlers when they
* want to start receive events from given input device.
*/
int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int err;
int retval;
err = mutex_lock_interruptible(&dev->mutex);
if (err)
return err;
retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;
if (dev->going_away) {
retval = -ENODEV;
goto out;
}
handle->open++;
if (!dev->users++ && dev->open)
err = dev->open(dev);
retval = dev->open(dev);
if (err)
handle->open--;
if (retval) {
dev->users--;
if (!--handle->open) {
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_sched();
}
}
out:
mutex_unlock(&dev->mutex);
return err;
return retval;
}
EXPORT_SYMBOL(input_open_device);
int input_flush_device(struct input_handle* handle, struct file* file)
int input_flush_device(struct input_handle *handle, struct file *file)
{
if (handle->dev->flush)
return handle->dev->flush(handle->dev, file);
struct input_dev *dev = handle->dev;
int retval;
return 0;
retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;
if (dev->flush)
retval = dev->flush(dev, file);
mutex_unlock(&dev->mutex);
return retval;
}
EXPORT_SYMBOL(input_flush_device);
/**
* input_close_device - close input device
* @handle: handle through which device is being accessed
*
* This function should be called by input handlers when they
* want to stop receive events from given input device.
*/
void input_close_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
input_release_device(handle);
mutex_lock(&dev->mutex);
__input_release_device(handle);
if (!--dev->users && dev->close)
dev->close(dev);
handle->open--;
if (!--handle->open) {
/*
* synchronize_sched() makes sure that input_pass_event()
* completed and that no more input events are delivered
* through this handle
*/
synchronize_sched();
}
mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL(input_close_device);
/*
* Prepare device for unregistering
*/
static void input_disconnect_device(struct input_dev *dev)
{
struct input_handle *handle;
int code;
/*
* Mark device as going away. Note that we take dev->mutex here
* not to protect access to dev->going_away but rather to ensure
* that there are no threads in the middle of input_open_device()
*/
mutex_lock(&dev->mutex);
dev->going_away = 1;
mutex_unlock(&dev->mutex);
spin_lock_irq(&dev->event_lock);
/*
* Simulate keyup events for all pressed keys so that handlers
* are not left with "stuck" keys. The driver may continue
* generate events even after we done here but they will not
* reach any handlers.
*/
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
for (code = 0; code <= KEY_MAX; code++) {
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
test_bit(code, dev->key)) {
input_pass_event(dev, EV_KEY, code, 0);
}
}
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
}
list_for_each_entry(handle, &dev->h_list, d_node)
handle->open = 0;
spin_unlock_irq(&dev->event_lock);
}
static int input_fetch_keycode(struct input_dev *dev, int scancode)
{
switch (dev->keycodesize) {
......@@ -473,7 +678,8 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
{
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
if (mutex_lock_interruptible(&input_mutex))
return NULL;
return seq_list_start(&input_dev_list, *pos);
}
......@@ -485,7 +691,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void input_devices_seq_stop(struct seq_file *seq, void *v)
{
/* release lock here */
mutex_unlock(&input_mutex);
}
static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
......@@ -569,7 +775,9 @@ static const struct file_operations input_devices_fileops = {
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
{
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
if (mutex_lock_interruptible(&input_mutex))
return NULL;
seq->private = (void *)(unsigned long)*pos;
return seq_list_start(&input_handler_list, *pos);
}
......@@ -582,7 +790,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void input_handlers_seq_stop(struct seq_file *seq, void *v)
{
/* release lock here */
mutex_unlock(&input_mutex);
}
static int input_handlers_seq_show(struct seq_file *seq, void *v)
......@@ -1005,6 +1213,7 @@ struct input_dev *input_allocate_device(void)
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
......@@ -1022,7 +1231,7 @@ EXPORT_SYMBOL(input_allocate_device);
* This function should only be used if input_register_device()
* was not called yet or if it failed. Once device was registered
* use input_unregister_device() and memory will be freed once last
* refrence to the device is dropped.
* reference to the device is dropped.
*
* Device should be allocated by input_allocate_device().
*
......@@ -1092,6 +1301,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
}
EXPORT_SYMBOL(input_set_capability);
/**
* input_register_device - register device with input core
* @dev: device to be registered
*
* This function registers device with input core. The device must be
* allocated with input_allocate_device() and all it's capabilities
* set up before registering.
* If function fails the device must be freed with input_free_device().
* Once device has been successfully registered it can be unregistered
* with input_unregister_device(); input_free_device() should not be
* called in this case.
*/
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
......@@ -1099,7 +1320,7 @@ int input_register_device(struct input_dev *dev)
const char *path;
int error;
set_bit(EV_SYN, dev->evbit);
__set_bit(EV_SYN, dev->evbit);
/*
* If delay and period are pre-set by the driver, then autorepeating
......@@ -1120,8 +1341,6 @@ int input_register_device(struct input_dev *dev)
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
list_add_tail(&dev->node, &input_dev_list);
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
......@@ -1137,49 +1356,79 @@ int input_register_device(struct input_dev *dev)
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
EXPORT_SYMBOL(input_register_device);
/**
* input_unregister_device - unregister previously registered device
* @dev: device to be unregistered
*
* This function unregisters an input device. Once device is unregistered
* the caller should not try to access it as it may get freed at any moment.
*/
void input_unregister_device(struct input_dev *dev)
{
struct input_handle *handle, *next;
int code;
for (code = 0; code <= KEY_MAX; code++)
if (test_bit(code, dev->key))
input_report_key(dev, code, 0);
input_sync(dev);
input_disconnect_device(dev);
del_timer_sync(&dev->timer);
mutex_lock(&input_mutex);
list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
handle->handler->disconnect(handle);
WARN_ON(!list_empty(&dev->h_list));
del_timer_sync(&dev->timer);
list_del_init(&dev->node);
device_unregister(&dev->dev);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
device_unregister(&dev->dev);
}
EXPORT_SYMBOL(input_unregister_device);
/**
* input_register_handler - register a new input handler
* @handler: handler to be registered
*
* This function registers a new input handler (interface) for input
* devices in the system and attaches it to all input devices that
* are compatible with the handler.
*/
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5])
return -EBUSY;
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
......@@ -1189,14 +1438,26 @@ int input_register_handler(struct input_handler *handler)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
out:
mutex_unlock(&input_mutex);
return retval;
}
EXPORT_SYMBOL(input_register_handler);
/**
* input_unregister_handler - unregisters an input handler
* @handler: handler to be unregistered
*
* This function disconnects a handler from its input devices and
* removes it from lists of known handlers.
*/
void input_unregister_handler(struct input_handler *handler)
{
struct input_handle *handle, *next;
mutex_lock(&input_mutex);
list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
handler->disconnect(handle);
WARN_ON(!list_empty(&handler->h_list));
......@@ -1207,14 +1468,50 @@ void input_unregister_handler(struct input_handler *handler)
input_table[handler->minor >> 5] = NULL;
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
}
EXPORT_SYMBOL(input_unregister_handler);
/**
* input_register_handle - register a new input handle
* @handle: handle to register
*
* This function puts a new input handle onto device's
* and handler's lists so that events can flow through
* it once it is opened using input_open_device().
*
* This function is supposed to be called from handler's
* connect() method.
*/
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
/*
* We don't use synchronize_rcu() here because we rely
* on dev->event_lock to protect read-side critical
* section in input_pass_event().
*/
synchronize_sched();
list_add_tail(&handle->d_node, &handle->dev->h_list);
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail(&handle->h_node, &handler->h_list);
if (handler->start)
......@@ -1224,10 +1521,29 @@ int input_register_handle(struct input_handle *handle)
}
EXPORT_SYMBOL(input_register_handle);
/**
* input_unregister_handle - unregister an input handle
* @handle: handle to unregister
*
* This function removes input handle from device's
* and handler's lists.
*
* This function is supposed to be called from handler's
* disconnect() method.
*/
void input_unregister_handle(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
list_del_init(&handle->h_node);
list_del_init(&handle->d_node);
/*
* Take dev->mutex to prevent race with input_release_device().
*/
mutex_lock(&dev->mutex);
list_del_rcu(&handle->d_node);
mutex_unlock(&dev->mutex);
synchronize_sched();
}
EXPORT_SYMBOL(input_unregister_handle);
......
......@@ -845,7 +845,7 @@ struct ff_rumble_effect {
* defining effect parameters
*
* This structure is sent through ioctl from the application to the driver.
* To create a new effect aplication should set its @id to -1; the kernel
* To create a new effect application should set its @id to -1; the kernel
* will return assigned @id which can later be used to update or delete
* this effect.
*
......@@ -925,9 +925,82 @@ struct ff_effect {
#define BIT(x) (1UL<<((x)%BITS_PER_LONG))
#define LONG(x) ((x)/BITS_PER_LONG)
/**
* struct input_dev - represents an input device
* @name: name of the device
* @phys: physical path to the device in the system hierarchy
* @uniq: unique identification code for the device (if device has it)
* @id: id of the device (struct input_id)
* @evbit: bitmap of types of events supported by the device (EV_KEY,
* EV_REL, etc.)
* @keybit: bitmap of keys/buttons this device has
* @relbit: bitmap of relative axes for the device
* @absbit: bitmap of absolute axes for the device
* @mscbit: bitmap of miscellaneous events supported by the device
* @ledbit: bitmap of leds present on the device
* @sndbit: bitmap of sound effects supported by the device
* @ffbit: bitmap of force feedback effects supported by the device
* @swbit: bitmap of switches present on the device
* @keycodemax: size of keycode table
* @keycodesize: size of elements in keycode table
* @keycode: map of scancodes to keycodes for this device
* @setkeycode: optional method to alter current keymap, used to implement
* sparse keymaps. If not supplied default mechanism will be used
* @getkeycode: optional method to retrieve current keymap. If not supplied
* default mechanism will be used
* @ff: force feedback structure associated with the device if device
* supports force feedback effects
* @repeat_key: stores key code of the last key pressed; used to implement
* software autorepeat
* @timer: timer for software autorepeat
* @sync: set to 1 when there were no new events since last EV_SYNC
* @abs: current values for reports from absolute axes
* @rep: current values for autorepeat parameters (delay, rate)
* @key: reflects current state of device's keys/buttons
* @led: reflects current state of device's LEDs
* @snd: reflects current state of sound effects
* @sw: reflects current state of device's switches
* @absmax: maximum values for events coming from absolute axes
* @absmin: minimum values for events coming from absolute axes
* @absfuzz: describes noisiness for axes
* @absflat: size of the center flat position (used by joydev)
* @open: this method is called when the very first user calls
* input_open_device(). The driver must prepare the device
* to start generating events (start polling thread,
* request an IRQ, submit URB, etc.)
* @close: this method is called when the very last user calls
* input_close_device().
* @flush: purges the device. Most commonly used to get rid of force
* feedback effects loaded into the device when disconnecting
* from it
* @event: event handler for events sent _to_ the device, like EV_LED
* or EV_SND. The device is expected to carry out the requested
* action (turn on a LED, play sound, etc.) The call is protected
* by @event_lock and must not sleep
* @grab: input handle that currently has the device grabbed (via
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
* recipient for all input events coming from the device
* @event_lock: this spinlock is is taken when input core receives
* and processes a new event for the device (in input_event()).
* Code that accesses and/or modifies parameters of a device
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
* has been registered with input core must take this lock.
* @mutex: serializes calls to open(), close() and flush() methods
* @users: stores number of users (input handlers) that opened this
* device. It is used by input_open_device() and input_close_device()
* to make sure that dev->open() is only called when the first
* user opens device and dev->close() is called when the very
* last user closes the device
* @going_away: marks devices that are in a middle of unregistering and
* causes input_open_device*() fail with -ENODEV.
* @dev: driver model's view of this device
* @h_list: list of input handles associated with the device. When
* accessing the list dev->mutex must be held
* @node: used to place the device onto input_dev_list
*/
struct input_dev {
void *private;
void *private; /* do not use */
const char *name;
const char *phys;
......@@ -955,8 +1028,6 @@ struct input_dev {
unsigned int repeat_key;
struct timer_list timer;
int state;
int sync;
int abs[ABS_MAX + 1];
......@@ -979,8 +1050,11 @@ struct input_dev {
struct input_handle *grab;
struct mutex mutex; /* serializes open and close operations */
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
int going_away;
struct device dev;
union { /* temporarily so while we switching to struct device */
......@@ -1046,7 +1120,9 @@ struct input_handle;
/**
* struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data
* @event: event handler
* @event: event handler. This method is being called by input core with
* interrupts disabled and dev->event_lock spinlock held and so
* it may not sleep
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
......@@ -1058,10 +1134,18 @@ struct input_handle;
* @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can
* handle
* @blacklist: prointer to a table of input_device_ids this driver should
* @blacklist: pointer to a table of input_device_ids this driver should
* ignore even if they match @id_table
* @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list
*
* Input handlers attach to input devices and create input handles. There
* are likely several handlers attached to any given input device at the
* same time. All of them will get their copy of input event generated by
* the device.
*
* Note that input core serializes calls to connect() and disconnect()
* methods.
*/
struct input_handler {
......@@ -1083,6 +1167,18 @@ struct input_handler {
struct list_head node;
};
/**
* struct input_handle - links input device with an input handler
* @private: handler-specific data
* @open: counter showing whether the handle is 'open', i.e. should deliver
* events from its device
* @name: name given to the handle by handler that created it
* @dev: input device the handle is attached to
* @handler: handler that works with the device through this handle
* @d_node: used to put the handle on device's list of attached handles
* @h_node: used to put the handle on handler's list of handles from which
* it gets events
*/
struct input_handle {
void *private;
......@@ -1205,7 +1301,7 @@ extern struct class input_class;
* @max_effects: maximum number of effects supported by device
* @effects: pointer to an array of effects currently loaded into device
* @effect_owners: array of effect owners; when file handle owning
* an effect gets closed the effcet is automatically erased
* an effect gets closed the effect is automatically erased
*
* Every force-feedback device must implement upload() and playback()
* methods; erase() is optional. set_gain() and set_autocenter() need
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment