Input Subsystem - Kernel Driver - Android
android-goldfish-5.4-dev
AOSP > Documentation > Core Topics > Input
https://www.kernel.org/doc/Documentation/input/input.txt
https://www.kernel.org/doc/Documentation/input/event-codes.txt
https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
1. Input subsystem related definitions
1.1 Code location
Android_Kernel\goldfish\drivers\input
Android_Kernel\goldfish\include\linux\input.h
Defines a set of standard event types and codes
1.2 input_dev structure: represents the input device
Name: The name of the device
Phys: The physical path to the device in the system hierarchy
Uniq: The unique identifier of the device (if the device has one)
Id: The Id of the device (struct input_Id
)
Propbit: A bitmap of device properties and quirks.
Evbit: Bitmap of event types supported by the device (EV_KEY
,,EV_REL
etc.)
Keybit: Bitmap that this device has Relbit: Relative axis bitmap of the devicekeys/buttons
Absbit : Absolute axis bitmap of the device Mscbit: Bitmap of miscellaneous events supported by the device Ledbit: LED bitmap present on the device Sndbit: Sound effect bitmap supported by the device Ffbit: Force feedback effect bitmap supported by the device Swbit: Switch bitmap present on the device Hint_events_per_packet: The average number of events generated by the device in a data packet ( / of events between). Used by event handlers to estimate the buffer size required to accommodate the event. Keycodemax: The size of the key code table Keycodesize:
EV_SYN
SYN_REPORT
The size of the element in the keycode table
Keycode: The scancode to keycode mapping for this device
Getkeycode: Optional legacy method for retrieving the current keymap.
Setkeycode: Optional method to change the current key mapping, used to implement sparse key mapping. If not provided, the default mechanism will be used. This method is called while event_lock is held and therefore cannot be hibernated
Ff: The force feedback structure associated with the device if the device supports force feedback effects
Poller: The poller structure associated with the device if the device is set to use polling mode
Repeat_key : stores the key code of the last key pressed; used to implement automatic software supervision
Timer: timer for software automatic recovery
Rep: current value of automatic playback parameters (delay, rate)
Mt: pointer to multi-touch state
Absinfo: contains absolute axis&struct input_Absinfo
Element array of information (current value, minimum value, maximum value, flat value, blur value, resolution)
Key: reflects the current state of the device key/button
Led: reflects the current state of the device Led
Snd: reflects the current state of the sound effect
Sw: Reflects the current state of the device switch
Open: when the first user callsinput_Open_device()
This method is called when . The driver must prepare the device to start generating events (start polling thread, request IRQ, submit URB, etc.)
Close: This method is called when the last user calls input_Close_device().
Flush: clear the device. Most commonly used to eliminate the force feedback effects loaded into a device when disconnected from the device
Event: An event handler for events sent to the device, such asEV_LED
orEV_SND
. The device should perform the requested action (turn on LED, play sound, etc.) Call protected
Event_lock: and cannot sleep
Grab: currently grabs the device's input handle (via EVIOCGRABioctl
). When a handle grabs a device, it becomes the sole receiver of all input events from that device.
Event_lock: This spinlock is acquired wheninput core
new events for the device are received and processed ( in). Code that accesses and/or modifies device parameters (such as or , , etc.) must use this lockinput_Event()
after the device is registered with the input core . Mutex: Serializes calls to , and methods Users: Stores the number of users who have opened this device (input handlers). and use this to make sure it is only called when the first user opens the device and when the last user closes the devicekeymap
absmin
absmax
absfuzz
open()
close()
flush()
input_open_device()
input_close_device()
dev->open()
dev->close()
Going_away: Mark the device being logged out and use-ENODEV
to causeinput_open_device*()
failure.
Dev: Driver model view for this device
H_list: List of input handles associated with the device. When accessing the list, you must holddev->mutex
Node: used to place the device oninput_dev_list
Num_vals
: the number of values queued in the current frame
Max_vals: the maximum number of values queued in the frame
Vals: the array of values queued in the current frame
Devres_managed: represents the use of the deviceDevres框架
for management , no explicit unregistration or release is required.
Timestamp:input_set_Timestamp
stores the timestamp of settings called by the driver
include/linux/input.h
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int hint_events_per_packet;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);
struct ff_device *ff;
struct input_dev_poller *poller;
unsigned int repeat_key;
struct timer_list timer;
int rep[REP_CNT];
struct input_mt *mt;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
ktime_t timestamp[INPUT_CLK_MAX];
};
1.3 input_handler结构体:struct input_handler - implements one of interfaces for input devices
Private: Driver-specific data
Event: Event handler. This method is called by the input core while disabling interrupts and holding the dev->event_lock spinlock, so it may not sleep
the Events: event sequence handler. This method is called by the input core while disabling interrupts and keeping the dev->event_lock spinlock, so it may not sleep
Filter: Similar toevent
; Detach normal event handlers with "Filter
".
Match: Called after comparing the id of the device and the handlerid_table
for fine-grained matching between the device and the handler
Connect: Called when attaching the handler to the input device
Disconnect: Disconnect the handler from the input device
* *Start:**Start the handler for the given handle. This function isconnect()
called by the input core after the method, and also when the process that "grabbed" the device releases it
Legacy_minors: Set to Minor by a driver using the legacy minor range%true
: The 32 of the device that this driver can provide Start of the legacy secondary range
Name: The name of the handler, as shown in/proc/bus/input/handlers
Id_table
:input_device_Id
Pointer to the tables this driver can handle
H_list:A list of input handlers associated with
the Node: used to place the driver onto,input_handler_list
attach
Input handlers
toinput devices
and createinput handles
. There may be multiple handlers connected to any given input device simultaneously. All of them will get a copy of the input events generated by the device. Use the exact same structure to implement the input filter.Input core
Filters are allowed to run first, and if any filter indicates that the event should be filtered (byfilter()
returning from its method%true
), the event will not be passed to the regular handler. Note that the input core serializes calls toconnect()
the anddisconnect()
methods.
include/linux/input.h
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
bool legacy_minors;
int minor;
const char *name;
const struct input_device_id *id_table;
struct list_head h_list;
struct list_head node;
};
1.4 input_handle structure: link input devices and input handlers
One
input_dev
reported event can beinput_handler
received and processed by multiple, and oneinput_handler
can also process multipleinput_dev
reported events, so that multipleinput_dev
and multipleinput_handler
may form an intertwined network. In this case, a bridge is needed to build the connection between the two. Function calls on both sides can be made through this "intermediary", whichinput_handle
is this bridge.
Private: Handler-specific data
Open: Counter showing whether the handle is "open", i.e. events should be delivered from its device
Name: The name given to the handle by the handler that created it
Dev: The input device to which the handle is connected
Handler: The handler through which the handle is connected A handle for the device to work with
D_node: used to place the handle in the device's additional handle list
H_node: used to place the handle in the handler's handle list from which to get events
include/linux/input.h
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
2. Input core initialization
include/linux/input.h
drivers/input/input.c
sybsys_initcall
Register and set the startup level to ensure that its initialization will be earlier than the registration of input device and input_handlermodule_init
way to registerinput设备
andinput_handler
subsys_initcall(input_init);
module_exit(input_exit);
2.1 input_init initialization entry
drivers/input/input.c
class_register(&input_class)
Input class registration is/sys/class
input_proc_init();
mainly used for information viewing andinput_handler
file creation to register character devices.devices
Proc
register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input")
static int __init input_init(void)
{
int err;
err = class_register(&input_class);
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init();
if (err)
goto fail1;
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
2.1.1 class_register
class_register(&input_class)
Input class registration is placed in/sys/class
the Linux kernel API class_register|Geek Notes
drivers/input/input.c
struct class input_class = {
.name = "input",
.devnode = input_devnode,
};
EXPORT_SYMBOL_GPL(input_class);
include/linux/device.h
drivers/base/class.c
/* This is a #define to keep the compiler from merging different
* instances of the __key variable */
#define class_register(class) \
({
\
static struct lock_class_key __key; \
__class_register(class, &__key); \
})
2.1.2 input_proc_init
input_proc_init()
Mainly usedinput_handler
fordevices
information viewing andProc
file creation
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
proc_bus_input_dir = proc_mkdir("bus/input", NULL);
if (!proc_bus_input_dir)
return -ENOMEM;
entry = proc_create("devices", 0, proc_bus_input_dir,
&input_devices_fileops);
if (!entry)
goto fail1;
entry = proc_create("handlers", 0, proc_bus_input_dir,
&input_handlers_fileops);
if (!entry)
goto fail2;
return 0;
fail2: remove_proc_entry("devices", proc_bus_input_dir);
fail1: remove_proc_entry("bus/input", NULL);
return -ENOMEM;
}
2.1.3 register_chrdev_region
register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input")
Register character device; create an "input" device with primary device 13
include/uapi/linux/major.h
#define INPUT_MAJOR 13
drivers/input/input.c
#define INPUT_MAX_CHAR_DEVICES 1024
fs/char_dev.c
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
struct char_device_struct *cd;
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name);
if (IS_ERR(cd))
goto fail;
}
return 0;
fail:
to = n;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
return PTR_ERR(cd);
}
3. input_dev device registration
3.1 input_allocate_device: allocate input_dev structure memory
input_allocate_device
- allocate memory for the new input device
return a prepared structureinput_dev
or%NULL
.
Note: Useinput_free_device()
Release unregistered devices;input_unregister_device()
applies to registered devices.
drivers/input/input.c
struct input_dev *devm_input_allocate_device(struct device *dev)
{
struct input_dev *input;
struct input_devres *devres;
devres = devres_alloc(devm_input_device_release,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return NULL;
input = input_allocate_device();
if (!input) {
devres_free(devres);
return NULL;
}
input->dev.parent = dev;
input->devres_managed = true;
devres->input = input;
devres_add(dev, devres);
return input;
}
EXPORT_SYMBOL(devm_input_allocate_device);
/**
* input_allocate_device - allocate memory for new input device
*
* Returns prepared struct input_dev or %NULL.
*
* NOTE: Use input_free_device() to free devices that have not been
* registered; input_unregister_device() should be used for already
* registered devices.
*/
struct input_dev *input_allocate_device(void)
{
static atomic_t input_no = ATOMIC_INIT(-1);
struct input_dev *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev) {
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
timer_setup(&dev->timer, NULL, 0);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
dev_set_name(&dev->dev, "input%lu",
(unsigned long)atomic_inc_return(&input_no));
__module_get(THIS_MODULE);
}
return dev;
}
3.2 input_register_device: Register device with input core
This function registers the device to
input core
. Before registering, a device must be assignedinput_allocate_device()
and all of its capabilities included. If the function fails, the release device must be usedinput_free_device()
. Once the device has successfully registered, it can beinput_unregister_device()
deregistered using; in this case it should not be calledinput_free_device()
. Note that this function is also used to register managed input devices (usingdevm_input_allocate_device()
assigned devices). Such managed input devices do not need to be explicitly unregistered or released, their removal is controlled by the devres infrastructure. It's also worth noting that deleting a managed input device is internally a two-step process: a registered managed input device is first unregistered, but remains in memory, and calls can still be handled (although the events are not delivered anywhere)input_event()
. Later, when the devres stack is unwound to the point where device allocation is made, the managed input device is released.
device_add(&dev->dev)
: Register the device as a linux devicelist_add_tail(&dev->node, &input_dev_list)
: Add device to linux kernel global listinput_dev_list
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
:Traverseinput_handler_list
and find your own for the devicehandler
drivers/input/input.c
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;
if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
dev_err(&dev->dev,
"Absolute device without dev->absinfo, refusing to register\n");
return -EINVAL;
}
if (dev->devres_managed) {
devres = devres_alloc(devm_input_device_unregister,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
devres->input = dev;
}
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size;
dev->max_vals = dev->hint_events_per_packet + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
input_enable_softrepeat(dev, 250, 33);
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
if (dev->poller)
input_dev_poller_finalize(dev->poller);
error = device_add(&dev->dev);
if (error)
goto err_free_vals;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;
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);
if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return 0;
err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
EXPORT_SYMBOL(input_register_device);
3.3 Case: "gpio-keys" device registration
drivers/input/keyboard/gpio_keys.c
"gpio-keys": For other cases, please view the following directories:platform_driver_register(&gpio_keys_device_driver) -> gpio_keys_probe -> devm_input_allocate_device -> input_register_device
drivers/input/gameport
drivers/input/joystick
drivers/input/keyboard
drivers/input/misc
drivers/input/mouse
drivers/input/rmi4
drivers/input/serio
drivers/input/tablet
drivers/input/touchscreen
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.shutdown = gpio_keys_shutdown,
.driver = {
.name = "gpio-keys",
.pm = &gpio_keys_pm_ops,
.of_match_table = gpio_keys_of_match,
.dev_groups = gpio_keys_groups,
}
};
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);
}
static int gpio_keys_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
struct fwnode_handle *child = NULL;
struct gpio_keys_drvdata *ddata;
struct input_dev *input;
int i, error;
int wakeup = 0;
if (!pdata) {
pdata = gpio_keys_get_devtree_pdata(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
}
ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),
GFP_KERNEL);
if (!ddata) {
dev_err(dev, "failed to allocate state\n");
return -ENOMEM;
}
ddata->keymap = devm_kcalloc(dev,
pdata->nbuttons, sizeof(ddata->keymap[0]),
GFP_KERNEL);
if (!ddata->keymap)
return -ENOMEM;
input = devm_input_allocate_device(dev);
if (!input) {
dev_err(dev, "failed to allocate input device\n");
return -ENOMEM;
}
ddata->pdata = pdata;
ddata->input = input;
mutex_init(&ddata->disable_lock);
platform_set_drvdata(pdev, ddata);
input_set_drvdata(input, ddata);
input->name = pdata->name ? : pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = dev;
input->open = gpio_keys_open;
input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->keycode = ddata->keymap;
input->keycodesize = sizeof(ddata->keymap[0]);
input->keycodemax = pdata->nbuttons;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
const struct gpio_keys_button *button = &pdata->buttons[i];
if (!dev_get_platdata(dev)) {
child = device_get_next_child_node(dev, child);
if (!child) {
dev_err(dev,
"missing child device node for entry %d\n",
i);
return -EINVAL;
}
}
error = gpio_keys_setup_key(pdev, input, ddata,
button, i, child);
if (error) {
fwnode_handle_put(child);
return error;
}
if (button->wakeup)
wakeup = 1;
}
fwnode_handle_put(child);
error = input_register_device(input);
if (error) {
dev_err(dev, "Unable to register input device, error: %d\n",
error);
return error;
}
device_init_wakeup(dev, wakeup);
return 0;
}
4. input_handler registration
4.1 Common input_handler
Generally
input_handler注册
it will beinput_dev设备注册
before, commoninput_handler
:
evdev_handler
: Respond to most events, default input processing eventsmousedev_handler
: Mouse input eventjoydev_handler
: Game remote sensing input eventkbd_handler
:Keyboard eventinput_leds_handler
apmpower_handler
drivers/input/evdev.c
static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
drivers/tty/vt/keyboard.c
static struct input_handler kbd_handler = {
.event = kbd_event,
.match = kbd_match,
.connect = kbd_connect,
.disconnect = kbd_disconnect,
.start = kbd_start,
.name = "kbd",
.id_table = kbd_ids,
};
drivers/input/mousedev.c
static struct input_handler mousedev_handler = {
.event = mousedev_event,
.connect = mousedev_connect,
.disconnect = mousedev_disconnect,
.legacy_minors = true,
.minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev",
.id_table = mousedev_ids,
};
drivers/input/joydev.c
static struct input_handler joydev_handler = {
.event = joydev_event,
.match = joydev_match,
.connect = joydev_connect,
.disconnect = joydev_disconnect,
.legacy_minors = true,
.minor = JOYDEV_MINOR_BASE,
.name = "joydev",
.id_table = joydev_ids,
};
drivers/input/input-leds.c
static struct input_handler input_leds_handler = {
.event = input_leds_event,
.connect = input_leds_connect,
.disconnect = input_leds_disconnect,
.name = "leds",
.id_table = input_leds_ids,
};
drivers/input/apm-power.c
static struct input_handler apmpower_handler = {
.event = apmpower_event,
.connect = apmpower_connect,
.disconnect = apmpower_disconnect,
.name = "apm-power",
.id_table = apmpower_ids,
};
4.2 input_register_handler registration function
This function registers a new
input_handler
(interface) for the input device on the system and connects it to all that are compatible with this handlerinput devices
.
INIT_LIST_HEAD(&handler->h_list)
: Initialize the kernel linked list in Linuxlist_add_tail(&handler->node, &input_handler_list)
:Add handler to linux kernel global listinput_handler_list
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
:Traverseinput_handler_list
and find your own for the devicehandler
/**
* 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 error;
error = mutex_lock_interruptible(&input_mutex);
if (error)
return error;
INIT_LIST_HEAD(&handler->h_list);
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
EXPORT_SYMBOL(input_register_handler);
5. input_dev and input_handler match input_handle
5.1 input_match_device matching
input_dev设备注册
andinput_handler注册
will be calledinput_attach_handler
input_match_device
: Matching is returned successfullyhandler->id_table
, that isinput_device_id
handler->connect(handler, dev, id)
: Matches successfully calledconnect
functions, such asdrivers/input/evdev.c#evdev_connect
,drivers/input/mousedev.c#mousedev_connect
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (input_match_device_id(dev, id) &&
(!handler->match || handler->match(handler, dev))) {
return id;
}
}
return NULL;
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
5.2 connect function
Such as
drivers/input/evdev.c#evdev_connect
,drivers/input/mousedev.c#mousedev_connect
etc.; view general event processingevdev.c
:
.driver_info = 1
: whichevdev_ids
matches all devices,evdev_connect
: Once registeredevdev的connect
;
1》input_register_handle
Register a new oneinput_handle
, which will behandle
mainly mounted on theinput_dev
andinput_handler
member linked lists respectively;
(evdev->handle.dev = input_get_device(dev);
,evdev->handle.handler = handler;
)
2》input_get_new_minor
Can create up to 32 event devices#define EVDEV_MINORS 32
;
3》cdev_device_add
Final calldevice_add
, create a new event device to the Linux system/dev/input/eventX
drivers/input/evdev.c
struct evdev {
int open;
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
struct cdev cdev;
bool exist;
};
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect.
*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true;
dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
error = cdev_device_add(&evdev->cdev, &evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}
static const struct input_device_id evdev_ids[] = {
{
.driver_info = 1 }, /* Matches all devices */
{
}, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
5.3 input_register_handle
input_register_handle
-Register a new input handle
Handle: This function for registering Handle
places a new input handle in the list ofinput_dev
andinput_handler
so thatinput_open_device()
events can flow in it once it is opened with . This function should be called from the handler'sconnect()
method.
/**
* 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;
/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
/*
* 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_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
EXPORT_SYMBOL(input_register_handle);
5.4 input_dev \ input_handler \ input_handle 关系
input_dev
It is the hardware driver layer, representing an input device.
input_handler
It is the event processing layer, representing an event processor. It
input_handle
belongs to the core layer, representing a paired input device and input event processor.
input_dev
linked together globallyinput_dev_list
. This operation is implemented when the device is registered.
input_handler
linked together globallyinput_handler_list
. This operation is implemented when the event handler is registered.
input_hande
There is no global linked list. When it was registered, it hung itself oninput_dev
andinput_handler
respectivelyh_list
. You can find it
throughinput_dev
and In the device registration and event handler, pairing work must be done when registering, and the link will be implemented after pairing. and can also be found via .input_handler
input_handle
input_handle
input_dev
input_handler
6. Report input events
input事件
Generally, 中断方式
reporting, related methods input_report_abs
, input_report_key
, input_sync
etc. are used. Finally input_sync
, it means that an event is reported and input_event
the processing is finally called.
6.1 Reporting of underlying Input events
AOSP > Documentation > Core Topics > Keyboard Devices , AOSP > Documentation > Core Topics > Touch Devices
Different
input设备
reportinginput事件
formats are different, commonly used按键
or触摸屏
adopted中断方式
reporting.
For example, when reporting on a touch screen,input事件
it is generally necessary to report the finger's id, x coordinate, y coordinate and other information.
https://www.kernel.org/doc/Documentation/input/input.txt
https://www.kernel.org/doc/Documentation/input/event-codes.txt
https://www.kernel.org/ doc/Documentation/input/multi-touch-protocol.txt
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
input_sync(input);
- | type | code | value |
---|---|---|---|
Point 1 | EV_ABS | ABS_MT_SLOT | 0 |
\ | EV_ABS | ABS_MT_TRACKING_ID | id |
\ | EV_ABS | ABS_MT_POSITION_X | x |
\ | EV_ABS | ABS_MT_POSITION_Y | y |
… | … | … | … |
Point 2 | EV_ABS | ABS_MT_SLOT | n |
\ | EV_ABS | ABS_MT_TRACKING_ID | id |
\ | EV_ABS | ABS_MT_POSITION_X | x |
\ | EV_ABS | ABS_MT_POSITION_Y | y |
6.2 input_event reports new input events
Drivers that implement various input devices should use this function to report input events. See also
input_inject_event()
.
NOTE:input_event()
It isinput_allocate_device()
safe to use an input device immediately after using it, or even before usinginput_register_device()
registration, but the event will not reach any input handler.input_event()
This early call of can be used to "seed" the initial state of a switch or the initial position of an absolute axis, etc.
input_handle_event
: Each event reporting isinput_event
completed through the interface. After determining whether the event type is supported, it is mainlyinput_handle_event
completed by callinginput_get_disposition
: Determine what to do based on the reported informationhandler->events()/handler->event()
:input_dev
Correspondinginput_handler
, such asevdev_handler
etc(input_event -> input_handle_event -> input_pass_values -> input_to_handler -> handler->events()/handler->event()
)
drivers/input/input.c
/*
* 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;
}
/*
* 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);
}
}
}
}
/**
* 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);
}
}
EXPORT_SYMBOL(input_event);
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;
/*
* Reset the timestamp on flush so we won't end up
* with a stale one. Note we only need to reset the
* monolithic one as we use its presence when deciding
* whether to generate a synthetic timestamp.
*/
dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 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;
}
}
6.3 evdev_events processing in evdev_handler
evdev_events
Pass incoming events to all connected client
evdev_event/evdev_events -> evdev_pass_values -> __pass_event ->
input events and store them inclient->buffer
;kill_fasync
used to send notification events to tell the upper layerclient->buffer
that data can be read.
drivers/input/evdev.c
static void __pass_event(struct evdev_client *client,
const struct input_event *event)
{
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
client->tail = (client->head - 2) & (client->bufsize - 1);
client->buffer[client->tail] = (struct input_event) {
.input_event_sec = event->input_event_sec,
.input_event_usec = event->input_event_usec,
.type = EV_SYN,
.code = SYN_DROPPED,
.value = 0,
};
client->packet_head = client->tail;
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
}
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t *ev_time)
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
struct timespec64 ts;
bool wakeup = false;
if (client->revoked)
return;
ts = ktime_to_timespec64(ev_time[client->clk_type]);
event.input_event_sec = ts.tv_sec;
event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
for (v = vals; v != vals + count; v++) {
if (__evdev_is_filtered(client, v->type, v->code))
continue;
if (v->type == EV_SYN && v->code == SYN_REPORT) {
/* drop empty SYN_REPORT */
if (client->packet_head == client->head)
continue;
wakeup = true;
}
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
}
spin_unlock(&client->buffer_lock);
if (wakeup)
wake_up_interruptible(&evdev->wait);
}
/*
* Pass incoming events to all connected clients.
*/
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
ktime_t *ev_time = input_get_timestamp(handle->dev);
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_values(client, vals, count, ev_time);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count, ev_time);
rcu_read_unlock();
}
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_value vals[] = {
{
type, code, value } };
evdev_events(handle, vals, 1);
}
7. Input event kernel space is passed to user space
EventHub::getEvents -> resd
:input事件
Stored inclient->buffer
, when the application layer or framework layer calls toread函数
read/dev/input/event*
the file, for example, the return dataevdev.c
will be calledevdev_read
,event_fetch_next_event
: Determineclient->buffer
whether the head and tail pointers in this circular buffer are equal (if equal, there is no data in the buffer), if not equal, take out ainput_event
type of event and put it into the event;input_event_to_user
: Copy this event to the application layer.input_event_size
The function is used to obtaininput_event
the size of an event and copy the event in a loopclient->buffer
to the buffer of the application layer.
frameworks/native/services/inputflinger/reader/EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
std::scoped_lock _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
ALOGI("Reopening all input devices due to a configuration change.");
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
}
// Report any devices that had last been added/removed.
for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
std::unique_ptr<Device> device = std::move(*it);
ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
event->when = now;
event->deviceId = (device->id == mBuiltInKeyboardId)
? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
: device->id;
event->type = DEVICE_REMOVED;
event += 1;
it = mClosingDevices.erase(it);
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
while (!mOpeningDevices.empty()) {
std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
mOpeningDevices.pop_back();
ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
// Try to find a matching video device by comparing device names
for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
it++) {
std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
if (tryAddVideoDeviceLocked(*device, videoDevice)) {
// videoDevice was transferred to 'device'
it = mUnattachedVideoDevices.erase(it);
break;
}
}
auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
if (!inserted) {
ALOGW("Device id %d exists, replaced.", device->id);
}
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.fd == mINotifyFd) {
if (eventItem.events & EPOLLIN) {
mPendingINotify = true;
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (eventItem.data.fd == mWakeReadPipeFd) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
char wakeReadBuffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
Device* device = getDeviceByFdLocked(eventItem.data.fd);
if (device == nullptr) {
ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
eventItem.data.fd);
ALOG_ASSERT(!DEBUG);
continue;
}
if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) {
if (eventItem.events & EPOLLIN) {
size_t numFrames = device->videoDevice->readAndQueueFrames();
if (numFrames == 0) {
ALOGE("Received epoll event for video device %s, but could not read frame",
device->videoDevice->getName().c_str());
}
} else if (eventItem.events & EPOLLHUP) {
// TODO(b/121395353) - consider adding EPOLLRDHUP
ALOGI("Removing video device %s due to epoll hang-up event.",
device->videoDevice->getName().c_str());
unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
device->videoDevice = nullptr;
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
device->videoDevice->getName().c_str());
ALOG_ASSERT(!DEBUG);
}
continue;
}
// This must be an input event
if (eventItem.events & EPOLLIN) {
int32_t readSize =
read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %" PRId32
" bufferSize: %zu capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(*device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
ALOGE("could not get event (wrong size: %d)", readSize);
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
event->when = processEventTimestamp(iev);
event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
mPendingEventIndex -= 1;
break;
}
}
} else if (eventItem.events & EPOLLHUP) {
ALOGI("Removing device %s due to epoll hang-up event.",
device->identifier.name.c_str());
deviceChanged = true;
closeDeviceLocked(*device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
device->identifier.name.c_str());
}
}
// readNotify() will modify the list of devices so this must be done after
// processing all other events to ensure that we read all remaining events
// before closing the devices.
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked();
deviceChanged = true;
}
// Report added or removed devices immediately.
if (deviceChanged) {
continue;
}
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {
break;
}
// Poll for events.
// When a device driver has pending (unread) events, it acquires
// a kernel wake lock. Once the last pending event has been read, the device
// driver will release the kernel wake lock, but the epoll will hold the wakelock,
// since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
// is called again for the same fd that produced the event.
// Thus the system can only sleep if there are no events pending or
// currently being processed.
//
// The timeout is advisory only. If the device is asleep, it will not wake just to
// service the timeout.
mPendingEventIndex = 0;
mLock.unlock(); // release lock before poll
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mLock.lock(); // reacquire lock after poll
if (pollResult == 0) {
// Timed out.
mPendingEventCount = 0;
break;
}
if (pollResult < 0) {
// An error occurred.
mPendingEventCount = 0;
// Sleep after errors to avoid locking up the system.
// Hopefully the error is transient.
if (errno != EINTR) {
ALOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
}
// All done, return the number of events we read.
return event - buffer;
}
drivers/input/evdev.c
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
size_t read = 0;
int error;
if (count != 0 && count < input_event_size())
return -EINVAL;
for (;;) {
if (!evdev->exist || client->revoked)
return -ENODEV;
if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*
* count == 0 is special - no IO is done but we check
* for error conditions (see above).
*/
if (count == 0)
break;
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + read, &event))
return -EFAULT;
read += input_event_size();
}
if (read)
break;
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);
if (error)
return error;
}
}
return read;
}
7.1 evdev_fetch_next_event
event_fetch_next_event
: Determine client->buffer
whether the head and tail pointers in this circular buffer are equal (if they are equal, there is no data in the buffer), if they are not equal, take out a input_event
type of event and put it into the event.
drivers/input/evdev.c
static int evdev_fetch_next_event(struct evdev_client *client,
struct input_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->packet_head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
7.2 input_event_to_user
input_event_to_user
: Send this event copy
to the application layer. input_event_size
The function is used to obtain input_event
the size of an event and loop to copy client->buffer
the event to the buffer of the application layer.
drivers/input/input-compat.c
#ifdef CONFIG_COMPAT
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
compat_event.sec = event->input_event_sec;
compat_event.usec = event->input_event_usec;
compat_event.type = event->type;
compat_event.code = event->code;
compat_event.value = event->value;
if (copy_to_user(buffer, &compat_event,
sizeof(struct input_event_compat)))
return -EFAULT;
} else {
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
}
return 0;
}
#else
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
return 0;
}
#endif /* CONFIG_COMPAT */
references
https://www.kernel.org/doc/Documentation/input/input.txt
Linux value input subsystem analysis (detailed explanation)
input input subsystem