Input子系统 - Kernel驱动程序 - Android

Input子系统 - Kernel驱动程序 - Android

  • 1、Input子系统相关定义
    • [1.1 代码位置](#1.1 代码位置)
    • [1.2 input_dev结构体:表示输入设备](#1.2 input_dev结构体:表示输入设备)
    • [1.3 input_handler结构体:struct input_handler - implements one of interfaces for input devices](#1.3 input_handler结构体:struct input_handler - implements one of interfaces for input devices)
    • [1.4 input_handle结构体:将输入设备与输入处理程序链接](#1.4 input_handle结构体:将输入设备与输入处理程序链接)
  • [2、input core 初始化](#2、input core 初始化)
    • [2.1 input_init 初始化入口](#2.1 input_init 初始化入口)
      • [2.1.1 class_register](#2.1.1 class_register)
      • [2.1.2 input_proc_init](#2.1.2 input_proc_init)
      • [2.1.3 register_chrdev_region](#2.1.3 register_chrdev_region)
  • [3、 input_dev设备注册](#3、 input_dev设备注册)
    • [3.1 input_allocate_device:分配input_dev结构体内存](#3.1 input_allocate_device:分配input_dev结构体内存)
    • [3.2 input_register_device:带输入核心的寄存器设备](#3.2 input_register_device:带输入核心的寄存器设备)
    • [3.3 案例:"gpio-keys"设备注册](#3.3 案例:"gpio-keys"设备注册)
  • 4、input_handler注册
    • [4.1 常见的input_handler](#4.1 常见的input_handler)
    • [4.2 input_register_handler注册函数](#4.2 input_register_handler注册函数)
  • 5、input_dev和input_handler匹配input_handle
    • [5.1 input_match_device匹配](#5.1 input_match_device匹配)
    • [5.2 connect函数](#5.2 connect函数)
    • [5.3 input_register_handle](#5.3 input_register_handle)
    • [5.4 input_dev \ input_handler \ input_handle 关系](#5.4 input_dev \ input_handler \ input_handle 关系)
  • 6、input事件上报
    • [6.1 底层Input事件上报](#6.1 底层Input事件上报)
    • [6.2 input_event 报告新的input事件](#6.2 input_event 报告新的input事件)
    • [6.3 evdev_handler中evdev_events处理](#6.3 evdev_handler中evdev_events处理)
  • 7、Input事件内核空间传递到用户空间
    • [7.1 evdev_fetch_next_event](#7.1 evdev_fetch_next_event)
    • [7.2 input_event_to_user](#7.2 input_event_to_user)
  • 参考文献

android-goldfish-5.4-dev
AOSP > 文档 > 核心主题 > 输入

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子系统相关定义

1.1 代码位置

Android_Kernel\goldfish\drivers\input
Android_Kernel\goldfish\include\linux\input.h 定义了一组标准事件类型和代码

1.2 input_dev结构体:表示输入设备

Name: 设备名称
Phys: 系统层次结构中设备的物理路径
Uniq: 设备的唯一标识码(如果设备有)
Id: 设备的Id(struct input_Id
Propbit: 设备属性和怪癖的位图。
Evbit: 设备支持的事件类型的位图(EV_KEYEV_REL等)
Keybit: 此设备具有的keys/buttons位图
Relbit: 设备的相对轴位图
Absbit: 设备的绝对轴位图
Mscbit: 设备支持的杂项事件的位图
Ledbit: 设备上存在的LED位图
Sndbit: 设备支持的音效位图
Ffbit: 设备支持的力反馈效果位图
Swbit: 设备上存在的开关位图
Hint_events_per_packet: 设备在数据包中生成的平均事件数(EV_SYN/SYN_REPORT事件之间)。由事件处理程序用于估计容纳事件所需的缓冲区大小。
Keycodemax: 键代码表的大小
Keycodesize: 键代码表中元素的大小
Keycode: 此设备的扫描码到密钥码的映射
Getkeycode: 用于检索当前密钥映射的可选遗留方法。
Setkeycode: 更改当前密钥映射的可选方法,用于实现稀疏密钥映射。如果未提供,将使用默认机制。该方法在保持event_lock时被调用,因此不能休眠
Ff: 如果设备支持力反馈效应,则与设备相关的力反馈结构
Poller: 如果设备设置为使用轮询模式,则与设备关联的轮询器结构
Repeat_key: 存储上次按键的按键代码;用于实现软件自动监管
Timer: 软件自动恢复的计时器
Rep: 自动回放参数的当前值(延迟、速率)
Mt: 指向多点触摸状态的指针
Absinfo: 包含绝对轴信息(当前值、最小值、最大值、平坦值、模糊值、分辨率)的&struct input_Absinfo元素数组
Key: 反映设备按键/按钮的当前状态
Led: 反映设备Led的当前状态
Snd: 反映音效的当前状态
Sw: 反映设备开关的当前状态
Open: 当第一个用户调用input_Open_device()时,会调用此方法。驱动程序必须准备设备开始生成事件(启动轮询线程、请求IRQ、提交URB等)
Close: 当最后一个用户调用input_Close_device()时,会调用此方法。
Flush: 清除设备。最常用于消除与设备断开连接时加载到设备中的力反馈效应
Event: 发送到设备的事件的事件处理程序,如EV_LEDEV_SND。该设备应执行请求的操作(打开LED、播放声音等)呼叫受保护

Event_lock:并且不能休眠
Grab: 当前抓取设备的输入句柄(通过EVIOCGRAB ioctl)。当句柄抓取设备时,它将成为来自该设备的所有输入事件的唯一接收者
Event_lock:input core接收并处理设备的新事件时(在input_Event()中),将获取此spinlock。在设备向输入核心注册后,访问和/或修改设备参数(如keymapabsminabsmaxabsfuzz等)的代码必须使用此锁。
Mutex: 序列化对open()close()flush()方法的调用
Users: 存储打开此设备的用户数(输入处理程序)。input_open_device()input_close_device()使用它来确保dev->open()仅在第一个用户打开设备时调用,dev->close()在最后一个用户关闭设备时调用
Going_away: 标记正在注销的设备,并使用-ENODEV导致input_open_device*()失败。
Dev: 此设备的驱动程序模型视图
H_list: 与设备相关联的输入句柄列表。访问列表时,必须持有dev->mutex
Node: 用于将设备放置在input_dev_list
Num_vals: 当前帧中排队的值数
Max_vals: 帧中排队的最大值数
Vals: 当前帧中排队的值数组
Devres_managed: 表示设备使用Devres框架进行管理,不需要显式注销或释放。
Timestamp: 存储由驱动程序调用的input_set_Timestamp设置的时间戳

include/linux/input.h

c 复制代码
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: 驱动程序特定数据
Event: 事件处理程序。此方法由输入核心调用,同时禁用中断并保持dev->event_lock spinlock,因此它可能不会休眠
Events: 事件序列处理程序。此方法由输入核心调用,同时禁用中断并保持dev->event_lock spinlock,因此它可能不会休眠
Filter: 类似于event;将普通事件处理程序与"Filter"分离。
Match: 在比较设备的id和处理程序的id_table后调用,以便在设备和处理程序之间进行细粒度匹配
Connect: 在将处理程序附加到输入设备时调用
Disconnect: 断开处理程序与输入设备的连接

**Start:**启动给定句柄的处理程序。这个函数是在connect()方法之后由输入核心调用的,当"抓取"设备的进程释放它时也是如此
Legacy_minors: 由使用旧版次要范围的驱动程序设置为%true
Minor: 此驱动程序可以提供的设备的32个遗留次要范围的开始
Name: 处理程序的名称,显示在/proc/bus/input/handlers
Id_table: 指向此驱动程序可以处理的input_device_Id表的指针
H_list: 与处理程序关联的输入处理程序列表
Node: 用于将驱动程序放置到input_handler_list

Input handlers附加到input devices并创建input handles。可能有多个处理程序同时连接到任何给定的输入设备。他们所有人都将获得设备生成的输入事件的副本。使用完全相同的结构来实现输入过滤器。 Input core允许过滤器首先运行,并且如果任何过滤器指示应该过滤事件(通过从其filter()方法返回%true),则不会将事件传递给常规处理程序。请注意,输入核心序列化对connect()disconnect()方法的调用。

include/linux/input.h

c 复制代码
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结构体:将输入设备与输入处理程序链接

一个input_dev上报的事件可以被多个input_handler接收处理,一个input_handler也可以处理多个input_dev上报的事件,这样多个input_dev和多个input_handler之间可能会形成交织的网状。在这种情况下,需要一个桥梁来搭建两者之间的联系,两边的函数调用都可以通过这个"中介"进行,input_handle就是这个桥梁。
Private: 特定于处理程序的数据
Open: 显示句柄是否"打开"的计数器,即应从其设备传递事件
Name: 创建句柄的处理程序赋予句柄的名称
Dev: 句柄所连接的输入设备
Handler: 通过该句柄与设备一起工作的句柄
D_node: 用于将句柄放在设备的附加句柄列表中
H_node: 用于将句柄放在处理程序的句柄列表中,从中获取事件

include/linux/input.h

c 复制代码
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 初始化

include/linux/input.h
drivers/input/input.c

  • sybsys_initcall注册设定启动等级,保证其初始化会早于input设备和input_handler的注册
  • module_init方式注册input设备input_handler
c 复制代码
subsys_initcall(input_init);
module_exit(input_exit);

2.1 input_init 初始化入口

drivers/input/input.c

class_register(&input_class) input类注册,放在/sys/class
input_proc_init(); 主要用于input_handlerdevices信息查看,Proc文件创建
register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input") 注册字符设备

c 复制代码
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类注册,放在/sys/class

Linux内核API class_register|极客笔记

drivers/input/input.c

c 复制代码
struct class input_class = {
	.name		= "input",
	.devnode	= input_devnode,
};
EXPORT_SYMBOL_GPL(input_class);

include/linux/device.h
drivers/base/class.c

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() 主要用于input_handlerdevices信息查看,Proc文件创建

c 复制代码
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") 注册字符设备;创建一个主设备为13的"input"设备

include/uapi/linux/major.h

c 复制代码
#define INPUT_MAJOR		13

drivers/input/input.c

c 复制代码
#define INPUT_MAX_CHAR_DEVICES		1024

fs/char_dev.c

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设备注册

3.1 input_allocate_device:分配input_dev结构体内存

input_allocate_device-为新的输入设备分配内存

返回准备好的结构input_dev%NULL

注意:使用input_free_device()释放尚未注册的设备;input_unregister_device()应用于已注册的设备。

drivers/input/input.c

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);
c 复制代码
/**
 * 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:带输入核心的寄存器设备

此函数将设备注册到 input core 。在注册之前,必须为设备分配input_allocate_device()及其所有功能。如果函数失败,则必须使用input_free_device()释放设备。一旦设备成功注册,就可以使用input_unregister_device()进行注销;在这种情况下,不应调用input_free_device()。请注意,此函数还用于注册托管输入设备(使用devm_input_allocate_device()分配的设备)。这样的托管输入设备不需要明确地注销或释放,它们的拆除由devres基础设施控制。同样值得注意的是,删除托管输入设备在内部是一个两步过程:注册的托管输入设备首先未注册,但保留在内存中,并且仍然可以处理input_event()调用(尽管事件不会传递到任何地方)。稍后,当devres堆栈展开到进行设备分配的点时,将释放托管输入设备。

  • device_add(&dev->dev):将设备注册为linux设备
  • list_add_tail(&dev->node, &input_dev_list):将设备添加到linux内核全局列表input_dev_list
  • list_for_each_entry(handler, &input_handler_list, node)
    input_attach_handler(dev, handler);:遍历input_handler_list,为设备找到自己的handler

drivers/input/input.c

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 案例:"gpio-keys"设备注册

drivers/input/keyboard/gpio_keys.c
"gpio-keys": 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

c 复制代码
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);
}
c 复制代码
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注册

4.1 常见的input_handler

一般来说input_handler注册会在input_dev设备注册之前,常见的input_handler

  • evdev_handler:响应绝大部分事件,默认input处理事件
  • mousedev_handler:鼠标类input事件
  • joydev_handler :游戏遥感类input事件
  • kbd_handler:键盘类事件
  • input_leds_handler
  • apmpower_handler

drivers/input/evdev.c

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

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

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

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

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

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注册函数

此函数为系统中的输入设备注册一个新的input_handler(接口),并将其连接到与该处理程序兼容的所有input devices

  • INIT_LIST_HEAD(&handler->h_list):初始化在Linux的内核链表
  • list_add_tail(&handler->node, &input_handler_list):将handler添加到linux内核全局列表input_handler_list
  • list_for_each_entry(handler, &input_handler_list, node)
    input_attach_handler(dev, handler);:遍历input_handler_list,为设备找到自己的handler
c 复制代码
/**
 * 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和input_handler匹配input_handle

5.1 input_match_device匹配

input_dev设备注册input_handler注册都会调用input_attach_handler

  • input_match_device:匹配成功返回handler->id_table,即input_device_id
  • handler->connect(handler, dev, id):匹配成功调用connect函数,如drivers/input/evdev.c#evdev_connectdrivers/input/mousedev.c#mousedev_connect
c 复制代码
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函数

drivers/input/evdev.c#evdev_connectdrivers/input/mousedev.c#mousedev_connect等;查看通用事件处理evdev.c

  • .driver_info = 1: 其中evdev_ids匹配所有设备,
  • evdev_connect:一旦注册就会evdev的connect
    1》input_register_handle注册一个新的input_handle,主要将handle分别挂载在input_devinput_handler成员链表;
    (evdev->handle.dev = input_get_device(dev);evdev->handle.handler = handler;)
    2》input_get_new_minor最多能创建32个event设备#define EVDEV_MINORS 32
    3》cdev_device_add最终调用device_add,向Linux系统新创建一个event设备/dev/input/eventX

drivers/input/evdev.c

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-注册一个新的输入句柄

Handle:用于注册的Handle

这个函数将一个新的输入句柄放在input_devinput_handler的列表中,这样,一旦使用input_open_device()打开它,事件就可以在其中流动。这个函数应该从处理程序的connect()方法调用。

c 复制代码
/**
 * 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 是硬件驱动层,代表一个input设备
input_handler 是事件处理层,代表一个事件处理器
input_handle 属于核心层,代表一个配对的input设备与input事件处理器
input_dev 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。
input_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作。
input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_devinput_handlerh_list上了。

通过input_devinput_handler就可以找到input_handle 在设备注册和事件处理器, 注册的时候都要进行配对工作,配对后就会实现链接。

通过input_handle也可以找到input_devinput_handler

6、input事件上报

input事件一般采用中断方式上报,相关方法input_report_absinput_report_keyinput_sync等。最终input_sync来表示一次事件上报,最终调用input_event处理。

6.1 底层Input事件上报

AOSP > 文档 > 核心主题 > 键盘设备AOSP > 文档 > 核心主题 > 触摸设备

不同的input设备上报的input事件的格式不同,常用的按键或者触摸屏采用的中断方式上报。

比如触摸屏上报input事件时一般需要上报手指的id、x坐标、y坐标等信息。

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

c 复制代码
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
input_sync(input);
- type code value
第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
... ... ... ...
第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 报告新的input事件

实现各种输入设备的驱动程序应使用此功能来报告输入事件。另请参见input_inject_event()

注意:input_event()可以在使用input_allocate_device()分配输入设备之后立即安全使用,甚至在使用input_register_device()注册之前也是如此,但该事件不会到达任何输入处理程序。input_event()的这种早期调用可以用于"种子"开关的初始状态或绝对轴的初始位置等

  • input_handle_event:每一个事件上报都是通过input_event接口来完成,在判定事件类型是否支持后,主要是调用input_handle_event来完成
  • input_get_disposition:根据上报信息判断怎么处理
  • handler->events()/handler->event()input_dev对应input_handler,如evdev_handler等(input_event -> input_handle_event -> input_pass_values -> input_to_handler -> handler->events()/handler->event())

drivers/input/input.c

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_handler中evdev_events处理

evdev_events将传入事件传递给所有连接的客户端
evdev_event/evdev_events -> evdev_pass_values -> __pass_event ->

input事件存储在client->buffer中;kill_fasync用于发送通知事件,告诉上层client->buffer中有数据可以读了。

drivers/input/evdev.c

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事件内核空间传递到用户空间

  • EventHub::getEvents -> resdinput事件存储在client->buffer中,当应用层或框架层调用read函数读取/dev/input/event*文件时,例如evdev.c会调用evdev_read返回数据,
  • event_fetch_next_event:判断client->buffer这个循环缓冲区中的头尾指针是否相等(相等时buffer中没有数据),不相等时取出一个input_event类型的事件放入到event中;
  • input_event_to_user:将此事件copy到应用层,input_event_size函数是用来获取一个input_event事件的大小,循环复制client->buffer中的事件到应用层的buffer中。

frameworks/native/services/inputflinger/reader/EventHub.cpp

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

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:判断client->buffer这个循环缓冲区中的头尾指针是否相等(相等时buffer中没有数据),不相等时取出一个input_event类型的事件放入到event中

drivers/input/evdev.c

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:将此事件copy到应用层,input_event_size函数是用来获取一个input_event事件的大小,循环复制client->buffer中的事件到应用层的buffer中

drivers/input/input-compat.c

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 */

参考文献

https://www.kernel.org/doc/Documentation/input/input.txt
Linux值输入子系统分析(详解)
input输入子系统

相关推荐
一起搞IT吧11 分钟前
相机Camera日志实例分析之五:相机Camx【萌拍闪光灯后置拍照】单帧流程日志详解
android·图像处理·数码相机
浩浩乎@30 分钟前
【openGLES】安卓端EGL的使用
android
Kotlin上海用户组2 小时前
Koin vs. Hilt——最流行的 Android DI 框架全方位对比
android·架构·kotlin
zzq19962 小时前
Android framework 开发者模式下,如何修改动画过度模式
android
木叶丸2 小时前
Flutter 生命周期完全指南
android·flutter·ios
阿幸软件杂货间3 小时前
阿幸课堂随机点名
android·开发语言·javascript
没有了遇见3 小时前
Android 渐变色整理之功能实现<二>文字,背景,边框,进度条等
android
没有了遇见4 小时前
Android RecycleView 条目进入和滑出屏幕的渐变阴影效果
android
站在巨人肩膀上的码农4 小时前
去掉长按遥控器power键后提示关机、飞行模式的弹窗
android·安卓·rk·关机弹窗·power键·长按·飞行模式弹窗
呼啦啦--隔壁老王5 小时前
屏幕旋转流程
android