Linux的输入子系统框架简析

一、Linux的输入子系统框架介绍

  1. 输入子系统在内核中分为设备驱动层、输入核心层和事件处理层
  2. 事件处理层,位于输入核心层的上层,接收来自输入核心层的输入事件,并根据事件的类型和属性进行对应处理,通常较为通用,实现创建设备节点、实现fops等,关键结构体是input_handler
  3. 输入核心层,连接事件处理层和设备驱动层,提供统一的接口,将设备驱动层上报的事件进行解析过滤等操作,然后分发给不同的事件案处理层,关键结构体是input_handle
  4. 设备驱动层,抽象硬件设备,完成设备的初始化、中断处理以及事件上报,关键结构体是input_dev

二、通用的事件处理层程序-evdev.c

  1. 入口函数中使用input_register_handler注册一个input_handler结构体
  2. input_register_handler函数中实现了将input_handler挂载到input_handler_list链表上,并遍历input_dev_list链表上的所有input_dev,调用input_attach_handler进行匹配
  3. input_attach_handler函数中使用input_match_device进行规则的匹配,最终调用input_handler的connect函数
  4. input_handler结构体成员event处理单个事件、events处理多个事件、filter过滤事件、connect连接handler和dev,disconnect断开连接
  5. evdev_connect函数,传入参数input_handler指针、input_dev指针、input_device_id指针,函数中获得此设备号、获得dev的名称,构建input_handle,并使用input_register_handle注册,最后注册一个字符设备,形成节点/dev/input/eventx
  6. 注册字符设备,就会有相关的fops,open函数中初始化一个evdev_client以及对应的buffer,并设置为private_data,并调用evdev_open_device打开evdev(实际操作了一个标志位、打开计数加1),ioctl函数中对很多的CMD进行了操作,poll函数中将进程挂在等待队列,并返回了mask,fasync函数调用fasync_helper,release函数和open函数实现了相反的操作,调用evdev_close_device
  7. evdev的read函数从evdev_client的buffer中获得一系列的event,最后拷贝到用户空间,write函数从用户空间拷贝数据并转换为event,调用input_handle_event将event注入到设备驱动中(方向应该是到dev,而不是handler)
  8. evdev_event函数,传入参数input_handler指针、input_valuezh指针以及count,调用evdev_pass_values将vals传递给所有注册的evdev_client,evdev_pass_values函数中调用__pass_event传递event到evdev_client,即操作evdev_client中的buffer

三、简单的设备驱动层代码框架

  1. 通过input_allocate_device分配并简单初始化一个input_dev结构体
  2. 并使用__set_bit设置输入设备支持的事件类型,evbit表示支持的事件大类,其他的xxbit表示支持的事件小类,这个是位图,每个bit都表示相应的信息
  3. 使用input_register_device注册一个input_dev结构体,该函数中进行一些必要的操作,之后遍历input_handler_list链表上的所有input_handler,调用input_attach_handler进行匹配

四、input_dev和input_handler的匹配规则

  1. 通用的事件处理层程序-evdev.c的匹配规则,其中的成员id_table成员只有一项,并且driver_info为1,表示匹配所有的input_dev,因此对于evdev.c的input_handler在函数input_match_device中必定返回true
  2. 其他的时间处理层程序的id_table成员的flags设置匹配标志,xxbit表示匹配的bit,关键通过bitmap_subset判断id的xxbit和input_dev的xxbit的子集关系完成匹配
  3. 因此只要设备和事件处理可以匹配,就会形成一个input_handle,进而形成多对多的匹配关系,因此一个设备可以向多个事件处理层发送事件

五、如何上报事件

  1. 设备驱动层中调用input_event并传入参数input_dev以及type、code、value(evbit所指、xxbit所指、值)上报事件
  2. 使用同步事件对不同批次的事件进行隔离
  3. 上报的数据是time(16B)+type(2B)+code(2B)+value(4B)
  4. input_event调用input_handle_event,input_handle_event根据不同的type确定了事件的传递方向,如果事件应该传递给应用程序,将type、code、value添加到input_dev的vals成员中,并通过input_pass_values进行传递
  5. input_pass_values通过input_to_handler函数将包含事件信息的vals传递给input_handler,执行filter函数,之后执行events/event函数

六、输入核心层,input.c

  1. 入口函数中初始化了input_class、初始化procfs、申请了1024个字符设备号
  2. 其他的就是用作上层和下层的一系列API

七、固定一个输入设备编号

  1. 对于通用的事件处理层程序-evdev.c,在connect函数中申请了次设备号,并通过dev_set_name设备了名称,在此通过比较dev的name成员,将预先分配的x号设备的名称由eventx改为你需要的名称即可
posted @ 2025-12-16 21:50  gramming  阅读(3)  评论(0)    收藏  举报