当前位置: 首页 > news >正文

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

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

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

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

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

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
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") 注册字符设备

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

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() 主要用于input_handlerdevices信息查看,Proc文件创建
在这里插入图片描述

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

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

3.1 input_allocate_device:分配input_dev结构体内存

input_allocate_device-为新的输入设备分配内存
返回准备好的结构input_dev%NULL
注意:使用input_free_device()释放尚未注册的设备;input_unregister_device()应用于已注册的设备。

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:带输入核心的寄存器设备

此函数将设备注册到 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

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

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

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

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

此函数为系统中的输入设备注册一个新的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
/*** 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
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

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()方法调用。

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

input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
input_sync(input);
-typecodevalue
第1个点EV_ABSABS_MT_SLOT0
\EV_ABSABS_MT_TRACKING_IDid
\EV_ABSABS_MT_POSITION_Xx
\EV_ABSABS_MT_POSITION_Yy
第2个点EV_ABSABS_MT_SLOTn
\EV_ABSABS_MT_TRACKING_IDid
\EV_ABSABS_MT_POSITION_Xx
\EV_ABSABS_MT_POSITION_Yy

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

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

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);elselist_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

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 namesfor (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 EPOLLRDHUPALOGI("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 eventif (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 pollint pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);mLock.lock(); // reacquire lock after pollif (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:判断client->buffer这个循环缓冲区中的头尾指针是否相等(相等时buffer中没有数据),不相等时取出一个input_event类型的事件放入到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:将此事件copy到应用层,input_event_size函数是用来获取一个input_event事件的大小,循环复制client->buffer中的事件到应用层的buffer中

drivers/input/input-compat.c

#ifdef CONFIG_COMPATint 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;
}#elseint 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输入子系统

相关文章:

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

Input子系统 - Kernel驱动程序 - Android 1、Input子系统相关定义1.1 代码位置1.2 input_dev结构体&#xff1a;表示输入设备1.3 input_handler结构体&#xff1a;struct input_handler - implements one of interfaces for input devices1.4 input_handle结构体&#xff1a;将…...

MySQL里的查看操作

文章目录 查看当前mysql有谁连接查看数据库或者表 查看当前mysql有谁连接 show processlist;查看数据库或者表 列出所有数据库&#xff1a; show databases;查看正在使用的数据库&#xff08;必须大写&#xff09;&#xff1a; SELECT DATABASE();列出数据库中的表&#xf…...

Vim的基础操作

前言 本文将向您介绍关于vim的基础操作 基础操作 在讲配置之前&#xff0c;我们可以新建一个文件 .vimrc&#xff0c;并用vim打开在里面输入set nu 先给界面加上行数&#xff0c;然后shift &#xff1b;输入wq退出 默认打开&#xff1a;命令模式 在命令模式中&#xff1a…...

十天学完基础数据结构-第一天(绪论)

1. 数据结构的研究内容 数据结构的研究主要包括以下核心内容和目标&#xff1a; 存储和组织数据&#xff1a;数据结构研究如何高效地存储和组织数据&#xff0c;以便于访问和操作。这包括了在内存或磁盘上的数据存储方式&#xff0c;如何将数据元素组织成有序或无序的集合&…...

神经网络 03(参数初始化)

一、参数初始化 对于某一个神经元来说&#xff0c;需要初始化的参数有两类&#xff1a;一类是权重W&#xff0c;还有一类是偏置b&#xff0c;偏置b初始化为0即可。而权重W的初始化比较重要&#xff0c;我们着重来介绍常见的初始化方式。 &#xff08;1&#xff09;随机初始化 …...

div设置圆角#前端

要在 div元素上设置圆角&#xff0c;您可以使用 CSS 的 border-radius 属性。 这个属性允许您指定元素的边角为圆角&#xff0c;可以将其应用于一个或多个边角。以下是一些示例代码&#xff1a;1.设置所有四个边角为圆角&#xff1a; div {border-radius: 10px; /* 设置所有四…...

Windows开机密码破解

Windows11以及Windows10(21H2)以上版本 先开机&#xff0c;不进行任何操作&#xff0c;静静的等待登录界面 按住Shift重启 进入“选择一个选项”界面&#xff0c;点击疑难解答 点击高级选项 点击命令提示符 输入两行命令 copy C:\windows\system32\uti1man.exe C: \Window…...

Mobirise for Mac:轻松创建手机网站的手机网站建设软件

如果你是一位设计师或者开发人员&#xff0c;正在寻找一款强大的手机网站建设软件&#xff0c;那么Mobirise for Mac绝对值得你尝试。这个独特的应用程序将帮助你轻松创建优雅而实用的手机网站&#xff0c;而无需编写复杂的代码。 Mobirise for Mac的主要特点包括&#xff1a;…...

[npm] npx 介绍与使用说明

[npm] npx 介绍与使用说明 npm 的由来npx 是什么&#xff1f;npx 特点npx 的特点项目安装包的使用全局安装包的避免指定工具包版本--no-install 参数和--ignore-existing 参数使用不同版本的 node-p 参数-c 参数实战应用 执行 GitHub 源码 npm 的由来 说到 npm 就离不开社区文…...

QT : 仿照QQ 完成弹出登录窗口,并实例化组件

1. 运行效果图 2. Headers #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>class MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr);~MainWindow(); }; #endif // MAINWINDOW_H 3. mainWindow.cpp &#xff1a…...

typescrip接口 interface详解,以及ts实现多态

ts 接口 当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的 示例如下 当一个对象类型被多次使用时,可以看到,很明显代码有大量的冗余 let personTom: { name: string, age?: number, sayHi(name: string): void } {name: Tom,sayHi(n…...

Vivado IP中Generate Output Products的设置说明

文章目录 Vivado IP中Generate Output Products的设置说明Synthesis OptionsRun Settings 官方文档中的介绍Generate Output ProductsSynthesis Options for IP 参考文献 Vivado IP中Generate Output Products的设置说明 在创建IP核时&#xff0c;将IP核的信息配置完成之后会弹…...

9.3.5网络原理(应用层HTTP/HTTPS)

一.HTTP: 1. HTTP是超文本传输协议,除了传输字符串,还可以传输图片,字体,视频,音频. 2. 3.HTTP协议报文格式:a.首行,b.请求头(header),c.空行(相当于一个分隔符,分隔了header和body),d.正文(body). 4. 5.URL:唯一资源描述符(长度不限制). a. b.注意:查询字符串(query stri…...

vue基础知识十一:Vue组件之间的通信方式都有哪些?

一、组件间通信的概念 开始之前&#xff0c;我们把组件间通信这个词进行拆分 组件通信 都知道组件是vue最强大的功能之一&#xff0c;vue中每一个.vue我们都可以视之为一个组件通信指的是发送者通过某种媒体以某种格式来传递信息到收信者以达到某个目的。广义上&#xff0c;…...

高阶数据结构(2)-----红黑树(未完成)

一)红黑树的基本概念和基本性质: 1)红黑树就是一种高度平衡的二叉搜索树&#xff0c;但是在每一个节点上面都增加了一个存储位来表示结点的颜色&#xff0c;可以是红色或者是黑色&#xff0c;通过对任何一条从根节点到叶子节点上面的路径各个节点着色方式的限制&#xff0c;红黑…...

[mockjs]Mock使用过程中的坑

[mockjs]Mock使用过程中的坑 现象描述原因分析解决方案修改源码处理无法识别的文件流 现象描述 mockjs在使用的过程中出现了下载文件无法正常打开的问题&#xff0c;但是在线上环境是正常的 console.log打印返回的response,发现是本地无法正常解析response.data 在代码中&am…...

华为云云耀云服务器L实例评测|部署前后端分离项目

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 学习测评 ✨特色专栏&#xff1a; MyS…...

02目标检测-传统检测方法

目录 一、目标学习的检测方法变迁及对比 二、 基于传统手工特征的检测算法的定义 三、传统主要手工特征与算法 Haar特征与 人脸检测算法 - Viola-Jones(了解) HOG特征与 SVM 算法(了解)&#xff08;行人检测、opencv实现&#xff09; SIFT特征与SIFT算法(了解) DPM&#…...

RP-母版 流程图 发布和预览 团队项目

母版 创建一个模版&#xff0c;可根据形态不同引用不同母版。若不想母版受页面变化影响&#xff0c;也可以在引用时脱离母版 创建母版&#xff1a; 1) 转换为母版 2&#xff09;在母版页面中添加 母版拖放行为 拖放行为&#xff0c;在母版名称上右键&#xff0c; 、 任意…...

【第200篇原创文章】解决低于1%概率出现的芯片VPSS模块跑飞的问题

在发布SDK内测的时候&#xff0c;我们发现在切换视频分辨率的时候有低概率出现VPSS模块跑飞的情况&#xff0c;概率低于1%&#xff0c;试个两三百次&#xff0c;能出1~2次。切换视频分辨率这个功能在安防产品上也确实存在需求&#xff0c;网络带宽不大好的地方分辨率可以适当下…...

微信小程序——生命周期详解(代码解读)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

多分类中混淆矩阵的TP,TN,FN,FP计算

关于混淆矩阵&#xff0c;各位可以在这里了解&#xff1a;混淆矩阵细致理解_夏天是冰红茶的博客-CSDN博客 上一篇中我们了解了混淆矩阵&#xff0c;并且进行了类定义&#xff0c;那么在这一节中我们将要对其进行扩展&#xff0c;在多分类中&#xff0c;如何去计算TP&#xff0…...

Linux系统:OpenSSH7.4p升级到9.0p(服务器漏洞)

清华大学开源软件镜像站下载地址&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/pub/OpenBSD/OpenSSH/portable/openssh-9.0p1.tar.gz 一、升级 0、安装Telnet &#xff08;1&#xff09;为防止安装失败&#xff0c;无法用ssh做远程连接&#xff0c;因此先安装telnet yum…...

【面试刷题】——C++的特点简单说明

C是一种通用的编程语言&#xff0c;具有许多强大的特点&#xff0c;以下是其中一些主要特点的简单说明&#xff1a; 面向对象编程&#xff08;OOP&#xff09;&#xff1a; C支持面向对象编程&#xff0c;允许将数据和操作封装在类中&#xff0c;提高了代码的可维护性和重用性…...

C2基础设施威胁情报对抗策略

威胁情报是指在信息安全和安全防御领域&#xff0c;收集、分析和解释与潜在威胁相关的信息&#xff0c;以便预先发现并评估可能对组织资产造成损害的潜在威胁&#xff0c;是一种多维度、综合性的方法&#xff0c;其通过信息的收集、分析和研判&#xff0c;帮助组织了解可能对其…...

差异备份详细说明(InsCode AI 创作助手)

差异备份详细说明 差异备份&#xff08;Differential Backup&#xff09;是一种备份策略&#xff0c;它与增量备份类似&#xff0c;但有一些关键区别。差异备份备份的是自上一次完整备份以来的所有更改数据&#xff0c;而不是自上一次备份以来的所有更改。这意味着差异备份文件…...

flask要点与坑

简介 Flask是一个用Python编写的Web应用程序框架&#xff0c;该框架简单易用、模块化、灵活性高。 该笔记主要记录Flask的关键要点和容易踩坑的地方 Flask 日志配置 Flask 中的自带logger模块&#xff08;也是python自带的模块&#xff09;&#xff0c;通过简单配置可以实现…...

EasyUI combobox 实现搜索(模糊匹配)功能

很简单的一个下拉框搜索模糊匹配功能&#xff0c;在此记录&#xff1a; 1&#xff1a;页面实现&#xff1a; <select class"easyui-combobox" name"combobox" id"combobox" style"width:135px;height:25px;" headerValue"请选…...

Postman的高级用法一:重新认识postman核心模块

本请求示例来自于免费天气API&#xff1a; 实况天气接口API开发指南 未来一天天气预报api - 天气API 关于Postman的核心模块 全局变量请求接口请求体预处理脚本 类似beforeTest&#xff0c;在发起请求前的预执行逻辑&#xff0c;通常是生成一些动态变量值 测试用例模块 测试者…...

git命令的操作

git命令操作及命令大全 1.创建一个新的本地仓库&#xff1a;2.添加文件到仓库&#xff1a;3.远程仓库操作&#xff1a;4.分支操作&#xff1a;5.git命令大全 1.创建一个新的本地仓库&#xff1a; 使用命令git init在本地目录中创建一个新的git仓库。 2.添加文件到仓库&#x…...

海澜之家网站建设的计划/网站制作流程图

MySQL不支持的功能(转)[more]本节介绍其他数据库中有而MySQL中无的功能。它介绍省略了什么功能&#xff0c;以及在需要这些功能时怎么办。一般情况下&#xff0c; MySQL之所以忽略某些功能是因为它们有负面性能影响。有的功能正在开发者的计划清单上&#xff0c;一旦找到一种方…...

抓取网站后台/广州网站建设费用

一面 自我介绍项目中的监控&#xff1a;那个监控指标常见的有哪些&#xff1f;微服务涉及到的技术以及需要注意的问题有哪些&#xff1f;注册中心你了解了哪些&#xff1f;consul 的可靠性你了解吗&#xff1f;consul 的机制你有没有具体深入过&#xff1f;有没有和其他的注册…...

单机网页游戏网站/可以免费发帖的网站

数据统计&#xff0c;截至3月21日&#xff0c;电力板块已有16家上市公司发布了2016年年报&#xff0c;业绩两极分化趋势明显。其中&#xff0c;梅雁吉祥、银星能源等水电、新能源发电公司业绩翻倍&#xff0c;深圳能源、大唐发电等火电公司净利润下滑明显。 水电新能源发电表现…...

杨浦网站建设 网站外包/沈阳seo排名公司

前言 我们知道&#xff0c;React Fiber是React v16中新的reconciliation引擎&#xff0c;是React团队用时2年对Stack Reconciler版本的核心算法进行的重写。它的主要目标是实现虚拟DOM的增量渲染&#xff0c;可以将渲染工作拆分成块并将其分散到多个帧的能力。在新的更新到来时…...

贵阳企业网站建设/百度查重入口免费版

escape编码和unescape编码&#xff0c;就是将一个字符转换为16进制unicode编码&#xff0c;前面加%字符进行标识。 此处不再多做解释&#xff0c;参考这里&#xff1a;http://www.jb51.net/article/23657.htm。 原本是js的一个方法&#xff0c;后来被转成java方法。具体参考这里…...

第三方做公司网站/2345网址导航电脑版

sessionid, uuid 2个字段上都有索引&#xff0c;但是查询条件 (sessionidxxx or uuidyyy) 不能使用索引 > 拆成2个sql sessionidxxx, uuidyyy 将查询出来的结果合并 /*** param array $a* param array $b* param callable|NULL $compare* return array*/private function…...