cregit-Linux how code gets into the kernel

Release 4.11 drivers/input/input.c

Directory: drivers/input
/*
 * The input core
 *
 * Copyright (c) 1999-2002 Vojtech Pavlik
 */

/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */


#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt

#include <linux/init.h>
#include <linux/types.h>
#include <linux/idr.h>
#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/major.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
#include "input-compat.h"

MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL");


#define INPUT_MAX_CHAR_DEVICES		1024

#define INPUT_FIRST_DYNAMIC_DEV		256
static DEFINE_IDA(input_ida);

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 const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };


static inline int is_event_supported(unsigned int code, unsigned long *bm, unsigned int max) { return code <= max && test_bit(code, bm); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov32100.00%1100.00%
Total32100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov97100.00%1100.00%
Total97100.00%1100.00%


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])); } }

Contributors

PersonTokensPropCommitsCommitProp
Henrik Rydberg6487.67%133.33%
Linus Torvalds (pre-git)810.96%133.33%
Dmitry Torokhov11.37%133.33%
Total73100.00%3100.00%


static void input_stop_autorepeat(struct input_dev *dev) { del_timer(&dev->timer); }

Contributors

PersonTokensPropCommitsCommitProp
Henrik Rydberg19100.00%1100.00%
Total19100.00%1100.00%

/* * Pass event first through all filters and then, if event has not been * filtered out, through all open handles. This function is called with * dev->event_lock held and interrupts disabled. */
static unsigned int input_to_handler(struct input_handle *handle, struct input_value *vals, unsigned int count) { struct input_handler *handler = handle->handler; struct input_value *end = vals; struct input_value *v; if (handler->filter) { for (v = vals; v != vals + count; v++) { if (handler->filter(handle, v->type, v->code, v->value)) continue; if (end != v) *end = *v; end++; } count = end - vals; } if (!count) return 0; if (handler->events) handler->events(handle, vals, count); else if (handler->event) for (v = vals; v != vals + count; v++) handler->event(handle, v->type, v->code, v->value); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Henrik Rydberg10659.22%111.11%
Dmitry Torokhov2815.64%333.33%
Vojtech Pavlik1910.61%222.22%
Linus Torvalds (pre-git)137.26%111.11%
Anshul Garg116.15%111.11%
Matt Mackall21.12%111.11%
Total179100.00%9100.00%

/* * Pass values first through all filters and then, if event has not been * filtered out, through all open handles. This function is called with * dev->event_lock held and interrupts disabled. */
static void input_pass_values(struct input_dev *dev, struct input_value *vals, unsigned int count) { struct input_handle *handle; struct input_value *v; if (!count) return; rcu_read_lock(); handle = rcu_dereference(dev->grab); if (handle) { count = input_to_handler(handle, vals, count); } else { list_for_each_entry_rcu(handle, &dev->h_list, d_node) if (handle->open) { count = input_to_handler(handle, vals, count); if (!count) break; } } rcu_read_unlock(); /* trigger auto repeat for key events */ if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) { for (v = vals; v != vals + count; v++) { if (v->type == EV_KEY && v->value != 2) { if (v->value) input_start_autorepeat(dev, v->code); else input_stop_autorepeat(dev); } } } }

Contributors

PersonTokensPropCommitsCommitProp
Henrik Rydberg10658.56%218.18%
Dmitry Torokhov3318.23%327.27%
Anshul Garg3016.57%218.18%
Linus Torvalds (pre-git)63.31%19.09%
Vojtech Pavlik52.76%218.18%
Matt Mackall10.55%19.09%
Total181100.00%11100.00%


static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_value vals[] = { { type, code, value } }; input_pass_values(dev, vals, ARRAY_SIZE(vals)); }

Contributors

PersonTokensPropCommitsCommitProp
Henrik Rydberg3775.51%233.33%
Dmitry Torokhov918.37%350.00%
Vojtech Pavlik36.12%116.67%
Total49100.00%6100.00%

/* * 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)) { struct input_value vals[] = { { EV_KEY, dev->repeat_key, 2 }, input_value_sync }; input_pass_values(dev, vals, ARRAY_SIZE(vals)); if (dev->rep[REP_PERIOD]) mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); } spin_unlock_irqrestore(&dev->event_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov9269.17%125.00%
Henrik Rydberg2015.04%125.00%
Linus Torvalds (pre-git)1712.78%125.00%
Vojtech Pavlik43.01%125.00%
Total133100.00%4100.00%

#define INPUT_IGNORE_EVENT 0 #define INPUT_PASS_TO_HANDLERS 1 #define INPUT_PASS_TO_DEVICE 2 #define INPUT_SLOT 4 #define INPUT_FLUSH 8 #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
static int input_handle_abs_event(struct input_dev *dev, unsigned int code, int *pval) { struct input_mt *mt = dev->mt; bool is_mt_event; int *pold; if (code == ABS_MT_SLOT) { /* * "Stage" the event; we'll flush it later, when we * get actual touch data. */ if (mt && *pval >= 0 && *pval < mt->num_slots) mt->slot = *pval; return INPUT_IGNORE_EVENT; } is_mt_event = input_is_mt_value(code); if (!is_mt_event) { pold = &dev->absinfo[code].value; } else if (mt) { pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST]; } else { /* * Bypass filtering for multi-touch events when * not employing slots. */ pold = NULL; } if (pold) { *pval = input_defuzz_abs_event(*pval, *pold, dev->absinfo[code].fuzz); if (*pold == *pval) return INPUT_IGNORE_EVENT; *pold = *pval; } /* Flush pending "slot" event */ if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) { input_abs_set_val(dev, ABS_MT_SLOT, mt->slot); return INPUT_PASS_TO_HANDLERS | INPUT_SLOT; } return INPUT_PASS_TO_HANDLERS; }

Contributors

PersonTokensPropCommitsCommitProp
Henrik Rydberg19792.49%457.14%
Daniel Mack146.57%228.57%
Dmitry Torokhov20.94%114.29%
Total213100.00%7100.00%


static int input_get_disposition(struct input_dev *dev, unsigned int type, unsigned int code, int *pval) { int disposition = INPUT_IGNORE_EVENT; int value = *pval; switch (type) { case EV_SYN: switch (code) { case SYN_CONFIG: disposition = INPUT_PASS_TO_ALL; break; case SYN_REPORT: disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH; break; case SYN_MT_REPORT: disposition = INPUT_PASS_TO_HANDLERS; break; } break; case EV_KEY: if (is_event_supported(code, dev->keybit, KEY_MAX)) { /* auto-repeat bypasses state updates */ if (value == 2) { disposition = INPUT_PASS_TO_HANDLERS; break; } if (!!test_bit(code, dev->key) != !!value) { __change_bit(code, dev->key); disposition = INPUT_PASS_TO_HANDLERS; } } break; case EV_SW: if (is_event_supported(code, dev->swbit, SW_MAX) && !!test_bit(code, dev->sw) != !!value) { __change_bit(code, dev->sw); disposition = INPUT_PASS_TO_HANDLERS; } break; case EV_ABS: if (is_event_supported(code, dev->absbit, ABS_MAX)) disposition = input_handle_abs_event(dev, code, &value); break; case EV_REL: if (is_event_supported(code, dev->relbit, REL_MAX) && value) disposition = INPUT_PASS_TO_HANDLERS; break; case EV_MSC: 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) { __change_bit(code, dev->led); disposition = INPUT_PASS_TO_ALL; } break; case EV_SND: if (is_event_supported(code, dev->sndbit, SND_MAX)) { if (!!test_bit(code, dev->snd) != !!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) { dev->rep[code] = value; disposition = INPUT_PASS_TO_ALL; } break; case EV_FF: if (value >= 0) disposition = INPUT_PASS_TO_ALL; break; case EV_PWR: disposition = INPUT_PASS_TO_ALL; break; } *pval = value; return disposition; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov17541.18%323.08%
Henrik Rydberg11627.29%538.46%
Linus Torvalds (pre-git)9422.12%215.38%
Richard Purdie255.88%17.69%
Linus Torvalds122.82%17.69%
Vojtech Pavlik30.71%17.69%
Total425100.00%13100.00%


static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { int disposition = input_get_disposition(dev, type, code, &value); if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) add_input_randomness(type, code, value); if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); if (!dev->vals) return; if (disposition & INPUT_PASS_TO_HANDLERS) { struct input_value *v; if (disposition & INPUT_SLOT) { v = &dev->vals[dev->num_vals++]; v->type = EV_ABS; v->code = ABS_MT_SLOT; v->value = dev->mt->slot; } v = &dev->vals[dev->num_vals++]; v->type = type; v->code = code; v->value = value; } if (disposition & INPUT_FLUSH) { if (dev->num_vals >= 2) input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; } else if (dev->num_vals >= dev->max_vals - 2) { dev->vals[dev->num_vals++] = input_value_sync; input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; } }

Contributors

PersonTokensPropCommitsCommitProp
Henrik Rydberg18672.37%17.69%
Dmitry Torokhov3614.01%538.46%
Linus Torvalds (pre-git)145.45%17.69%
Zephaniah E. Hull114.28%17.69%
Vojtech Pavlik41.56%215.38%
Patrick Mochel31.17%17.69%
Richard Purdie20.78%17.69%
Linus Torvalds10.39%17.69%
Total257100.00%13100.00%

/** * 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 to report input events. See also input_inject_event(). * * NOTE: input_event() may be safely used right after input device was * allocated with input_allocate_device(), even before it is registered * with input_register_device(), but the event will not reach any of the * input handlers. Such early invocation of input_event() may be used * to 'seed' initial state of a switch or initial position of absolute * axis, etc. */
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { unsigned long flags; if (is_event_supported(type, dev->evbit, EV_MAX)) { spin_lock_irqsave(&dev->event_lock, flags); input_handle_event(dev, type, code, value); spin_unlock_irqrestore(&dev->event_lock, flags); } }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov5780.28%133.33%
Linus Torvalds (pre-git)1419.72%266.67%
Total71100.00%3100.00%

EXPORT_SYMBOL(input_event); /** * 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 = handle->dev; struct input_handle *grab; unsigned long flags; if (is_event_supported(type, dev->evbit, EV_MAX)) { spin_lock_irqsave(&dev->event_lock, flags); rcu_read_lock(); grab = rcu_dereference(dev->grab); if (!grab || grab == handle) input_handle_event(dev, type, code, value); rcu_read_unlock(); spin_unlock_irqrestore(&dev->event_lock, flags); } }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov109100.00%3100.00%
Total109100.00%3100.00%

EXPORT_SYMBOL(input_inject_event); /** * input_alloc_absinfo - allocates array of input_absinfo structs * @dev: the input device emitting absolute events * * If the absinfo struct the caller asked for is already allocated, this * functions will not do anything. */
void input_alloc_absinfo(struct input_dev *dev) { if (!dev->absinfo) dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo), GFP_KERNEL); WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__); }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack46100.00%1100.00%
Total46100.00%1100.00%

EXPORT_SYMBOL(input_alloc_absinfo);
void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat) { struct input_absinfo *absinfo; input_alloc_absinfo(dev); if (!dev->absinfo) return; absinfo = &dev->absinfo[axis]; absinfo->minimum = min; absinfo->maximum = max; absinfo->fuzz = fuzz; absinfo->flat = flat; __set_bit(EV_ABS, dev->evbit); __set_bit(axis, dev->absbit); }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack8588.54%150.00%
Dmitry Torokhov1111.46%150.00%
Total96100.00%2100.00%

EXPORT_SYMBOL(input_set_abs_params); /** * 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) { struct input_dev *dev = handle->dev; int retval; retval = mutex_lock_interruptible(&dev->mutex); if (retval) return retval; if (dev->grab) { retval = -EBUSY; goto out; } rcu_assign_pointer(dev->grab, handle); out: mutex_unlock(&dev->mutex); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4659.74%233.33%
Zephaniah E. Hull1620.78%116.67%
Linus Torvalds (pre-git)1114.29%116.67%
Vojtech Pavlik45.19%233.33%
Total77100.00%6100.00%

EXPORT_SYMBOL(input_grab_device);
static void __input_release_device(struct input_handle *handle) { struct input_dev *dev = handle->dev; struct input_handle *grabber; grabber = rcu_dereference_protected(dev->grab, lockdep_is_held(&dev->mutex)); if (grabber == handle) { rcu_assign_pointer(dev->grab, NULL); /* Make sure input_pass_event() notices that grab is gone */ synchronize_rcu(); list_for_each_entry(handle, &dev->h_list, d_node) if (handle->open && handle->handler->start) handle->handler->start(handle); } }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov6368.48%466.67%
Zephaniah E. Hull2122.83%116.67%
Andrew Morton88.70%116.67%
Total92100.00%6100.00%

/** * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov3997.50%150.00%
Zephaniah E. Hull12.50%150.00%
Total40100.00%2100.00%

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 retval; 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) retval = dev->open(dev); if (retval) { dev->users--; if (!--handle->open) { /* * Make sure we are not delivering any more events * through this handle */ synchronize_rcu(); } } out: mutex_unlock(&dev->mutex); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov8369.75%350.00%
Linus Torvalds (pre-git)3226.89%233.33%
Jes Sorensen43.36%116.67%
Total119100.00%6100.00%

EXPORT_SYMBOL(input_open_device);
int input_flush_device(struct input_handle *handle, struct file *file) { struct input_dev *dev = handle->dev; int retval; retval = mutex_lock_interruptible(&dev->mutex); if (retval) return retval; if (dev->flush) retval = dev->flush(dev, file); mutex_unlock(&dev->mutex); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4055.56%150.00%
Vojtech Pavlik3244.44%150.00%
Total72100.00%2100.00%

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; mutex_lock(&dev->mutex); __input_release_device(handle); if (!--dev->users && dev->close) dev->close(dev); if (!--handle->open) { /* * synchronize_rcu() makes sure that input_pass_event() * completed and that no more input events are delivered * through this handle */ synchronize_rcu(); } mutex_unlock(&dev->mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4156.16%350.00%
Linus Torvalds (pre-git)2838.36%233.33%
Jes Sorensen45.48%116.67%
Total73100.00%6100.00%

EXPORT_SYMBOL(input_close_device); /* * Simulate keyup events for all keys that are marked as pressed. * The function must be called with dev->event_lock held. */
static void input_dev_release_keys(struct input_dev *dev) { bool need_sync = false; int code; if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { for_each_set_bit(code, dev->key, KEY_CNT) { input_pass_event(dev, EV_KEY, code, 0); need_sync = true; } if (need_sync) input_pass_event(dev, EV_SYN, SYN_REPORT, 1); memset(dev->key, 0, sizeof(dev->key)); } }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov6875.56%250.00%
Anshul Garg2022.22%125.00%
Oliver Neukum22.22%125.00%
Total90100.00%4100.00%

/* * Prepare device for unregistering */
static void input_disconnect_device(struct input_dev *dev) { struct input_handle *handle; /* * 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 = true; 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. */ input_dev_release_keys(dev); list_for_each_entry(handle, &dev->h_list, d_node) handle->open = 0; spin_unlock_irq(&dev->event_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Oliver Neukum5269.33%150.00%
Dmitry Torokhov2330.67%150.00%
Total75100.00%2100.00%

/** * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry * @ke: keymap entry containing scancode to be converted. * @scancode: pointer to the location where converted scancode should * be stored. * * This function is used to convert scancode stored in &struct keymap_entry * into scalar form understood by legacy keymap handling methods. These * methods expect scancodes to be represented as 'unsigned int'. */
int input_scancode_to_scalar(const struct input_keymap_entry *ke, unsigned int *scancode) { switch (ke->len) { case 1: *scancode = *((u8 *)ke->scancode); break; case 2: *scancode = *((u16 *)ke->scancode); break; case 4: *scancode = *((u32 *)ke->scancode); break; default: return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mauro Carvalho Chehab86100.00%1100.00%
Total86100.00%1100.00%

EXPORT_SYMBOL(input_scancode_to_scalar); /* * Those routines handle the default case where no [gs]etkeycode() is * defined. In this case, an array indexed by the scancode is used. */
static unsigned int input_fetch_keycode(struct input_dev *dev, unsigned int index) { switch (dev->keycodesize)