Lazy loaded image
🥳嵌入式Linux开发
INPUT 驱动专题01 - INPUT 子系统架构详解
Words 3821Read Time 10 min
2025-4-24
2025-4-24
type
date
slug
category
icon
password

Input 子系统结构和工作流

1. 输入系统架构

Linux Input 子系统是一个处理输入设备的框架,它通过三个核心组件实现:input_devinput_handlerinput_handle
 

2. 核心数据结构

input_dev 代表物理输入设备(如键盘、鼠标),input_handler 代表处理输入事件的处理程序,而 input_handle 则是连接两者的桥梁。

2.1 输入设备结构体 (input_dev)

  • 设备能力位图:用于表示设备支持的事件类型和事件代码
事件类型和代码含义说明
  1. evbit :表示设备支持的事件类型
      • EV_KEY :按键事件
      • EV_REL :相对坐标事件(如鼠标移动)
      • EV_ABS :绝对坐标事件(如触摸屏)
      • EV_SW :开关事件
      • EV_LED :LED灯事件
      • EV_SND :声音事件
      • EV_FF :力反馈事件
      • EV_SYN :同步事件,用于标记事件包的结束
  1. keybit :当支持 EV_KEY 时,表示设备支持的按键
      • KEY_A 到 KEY_Z :字母键
      • KEY_0 到 KEY_9 :数字键
      • BTN_LEFT 、 BTN_RIGHT :鼠标按钮
      • BTN_TOUCH :触摸事件
  1. relbit :当支持 EV_REL 时,表示设备支持的相对坐标
      • REL_X 、 REL_Y :X和Y轴相对移动
      • REL_WHEEL :滚轮移动
  1. absbit :当支持 EV_ABS 时,表示设备支持的绝对坐标
      • ABS_X 、 ABS_Y :X和Y轴绝对位置
      • ABS_PRESSURE :压力值
      • ABS_MT_POSITION_X 、 ABS_MT_POSITION_Y :多点触控位置
  • h_list 是一个input_handle 链表头(区别input_handler),用于管理连接到该设备的所有处理程序(handlers)

2.2 事件处理程序结构体 (input_handler )

  • id_table :用于设备ID与处理程序ID表比较,表明支持哪些input_dev
  • match :在 id_table 表比较后调用,用于执行设备和处理程序之间的精细匹配
  • connect :当处理程序连接到输入设备时调用,负责处理程序与设备建立连接
  • void *private :处理程序的私有数据指针,可用于存储驱动程序特定的数据
  • filter :事件过滤回调函数,如果返回 true ,则事件会被过滤掉
  • event :单个事件处理回调函数,当输入设备产生事件时被调用。注意此函数在中断禁用和设备事件锁持有的情况下调用,因此不能休眠
  • events :批量事件处理回调函数,用于一次处理多个输入事件,提高效率
  • disconnect :当处理程序与输入设备断开连接时调用
  • start :在 connect() 方法之后以及当"抓取"设备的进程释放设备时,由输入核心调用
  • legacy_minors :由使用传统次设备号范围的驱动程序设置为 true
  • minor :此驱动程序可以提供的32个传统次设备号范围的起始值
  • name :处理程序的名称,将显示在 /proc/bus/input/handlers 中
  • h_list :与处理程序 input_handler 关联的所有句柄 input_handle 链表头,与input_dev中h_list关联。
  • node :用于将驱动程序放置到全局 input_handler_list 链表中的节点

2.3 输入事件结构体 (input_event)

输入事件通过 input_event 结构体在内核和用户空间之间传递:

3. 注册与匹配流程

3.1 注册 input_dev

3.2 注册 input_handler

3.3 设备与处理程序匹配过程

3.4 事件处理流程

输入子系统 evdev.c 文件详细分析

evdev.c 是 Linux 输入子系统中的核心文件,它实现了字符设备接口,允许用户空间程序访问原始输入事件。

1. 架构概述

2. 关键数据结构

2.1 evdev 结构体

2.2 evdev_client 结构体

3. 时序交互图

  • APP调用open函数打开/dev/input/event0
    • 在驱动程序evdev_open里,创建一个evdev_client,表示一个"客户"
  • APP调用read/poll读取、等待数据
    • 没有数据时休眠:wait_event_interruptible(evdev->wait, ...)
  • 点击、操作输入设备,产生中断
  • 在中断服务程序里
    • 从硬件读取到数据
    • 使用以下函数上报数据
    • input_event做什么?
      • dev->h_list中取出input_handle,从input_handle取出input_handler
      • 优先调用input_handler->filter来处理
      • 如果没有input_handler->filter或者没处理成功
        • 调用input_handler->events
        • 没有input_handler->events的话,调用input_handler->event
    • 以evdev.c为例
      • 它有evdev_events:用来处理多个事件
      • 也有evdev_event:实质还是调用evdev_events
      • 唤醒"客户":wake_up_interruptible(&evdev->wait);

    4. 输入设备创建的主要工作流程

    4.1 模块初始化

    evdev 模块在初始化时注册一个输入处理程序:

    4.2 设备连接

    当输入设备注册时,输入核心会调用 evdev_connect 函数创建对应的 evdev 设备:

    4.3 设备打开

    当用户空间程序打开设备节点时,会调用 evdev_open 函数:

    4.4 事件处理

    当输入设备产生事件时,输入核心会调用 evdev_eventevdev_events 函数:

    4.5 读取事件

    用户空间程序通过 read 系统调用读取事件:

    4.6 写入事件

    用户空间程序可以通过 write 系统调用注入事件:

    5. 总结

    evdev.c 实现了 Linux 输入子系统中的字符设备接口,它是用户空间程序访问输入设备事件的主要方式。主要功能包括:
    1. 为每个输入设备创建对应的字符设备节点
    1. 管理用户空间程序与输入设备的连接
    1. 提供事件缓冲和过滤机制
    1. 支持多种时钟类型的时间戳
    1. 实现设备独占(grab)机制
    1. 提供丰富的 ioctl 接口用于查询和控制设备
    通过这些功能,evdev 模块使得用户空间程序能够以统一的方式访问各种输入设备,是 Linux 输入子系统的关键组成部分。
    上一篇
    模板设计模式:让你的代码结构更清晰
    下一篇
    Guide to Linux System

    Comments
    Loading...