前言

上一节有展示Android Input子系统的架构图,这里我们关心Linux kernel层

可以看到kernel层分为三层:

输入子系统设备驱动:处理与硬件相关的信息,调用input API注册输入设备,并把数据往上报

输入子系统核心层:为事件处理层和设备驱动层提供API接口调用

输入子系统事件处理:通过核心层的API获取输入事件上报的数据,定义input API与应用层交互

数据结构

数据结构 代码位置 描述
struct input_dev input.h input设备驱动中的实例
struct evdev
struct mousedev
struct keybdev
evdev.c
mousedev.c
keybdev.c
Event Handler层逻辑input设备的数据结构
struct input_handler input.h Event Handler的结构,handler层实例化对象
struct input_handle input.h 用于创建驱动层input_dev和handler链表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//kernel/include/linux/input.h
/* 描述输入设备 */
struct input_dev { //代表一个输入设备
const char *name; //设备名字,sys文件名
//...
struct input_id id; //与handler匹配:总线类型、厂商、版本等
/* 输入设备支持时间的位图bitmap */
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 int keycodemax; //支持按键值个数
int (*setkeycode)(); //修改当前keymap
int (*getkeycode)(); //检索keymap

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; //当前占用该设备的input_handle
//...
struct list_head h_list; //handle链表,链接此input_dev
struct list_head node; //链入input_dev_list
//...
};

/*事件处理*/
struct input_handler {
void *private;
void (*event)(); //处理设备驱动报告的事件
void (*events)();
bool (*filter)();
bool (*match)();
int (*connect)(); //连接handler和input_dev
void (*disconnect)(); //断开连接
void (*start)(); //启动指定handle的handler函数

bool legacy_minors;
int minor;
const char *name; //handler名

const struct input_device_id *id_table; //输入设备id列表,匹配input_dev设备信息

struct list_head h_list; //链入handle链表
struct list_head node; //链入input_handler_list
};

/*
* 连接 input_dev 和 handler 的桥梁
* 一个 input_dev 可以对应多个 handler , 一个 handler 也可以对应多个dev
*/
struct input_handle {
int open; // 设备打开次数(上层访问次数)
const char *name;

struct input_dev *dev; // 所属 input_dev
struct input_handler *handler; // 所属 handler

struct list_head d_node; // 链入对应 input_dev 的 h_list
struct list_head h_node; // 链入对应 handler 的 h_list
};
/* 事件载体,输入子系统的事件包装为 input_event 上传到 Framework*/
struct input_event {
struct timeval time; // 时间戳
__u16 type; // 事件类型
__u16 code; // 事件代码
__s32 value; // 事件值,如坐标的偏移值
};

对于handler和device,分别用链表input_handler_listinput_device_list进行维护,这两条是全局链表

input_handle结构体代表一个成功配对的input_devinput_handlerinput_handle没有一个全局的链表,它注册的时候将自己分别挂在input_device_listinput_handler_listh_list上;同时,input_handle的成员.dev,关联到input_dev结构,.handler关联到input_handler结构。

输入子系统流程

  • 子系统入口函数:subsys_initcall(input_init);

    • class_register(&input_class):在/sys/class下创建input类
    • input_proc_init():在/proc下建立相关文件
    • register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input"):申请字符设备主设备号为13
  • 注册input设备

    • ​ 添加设备
    • ​ 把输入设备挂到输入设备链表input_dev_list
    • ​ 遍历input_handler_list链表,查找并匹配输入设备对应的时间处理层,如果匹配上,就调用handlerconnect函数进行连接。
  • 事件处理入口函数:module_init(evdev_init); -> input_register_handler

    • 把设备处理器挂到全局的input子系统设备链表input_handler_list
    • 遍历input_dev_list,与每一个input_dev进行匹配:input_attach_handler
    • input_attach_handler
      • input_match_device
      • handler->connect
      • 申请此设备号:input_get_new_monor
      • 设置设备节点名称/dev/eventX
      • 设置应用层使用的设备号
      • input_dev设备驱动和handler事件处理层的关联:input_register_handler
      • 将设备加入到Linux设备模型:device_add
  • 上报事件

    • input_report_xx()
      • input_event()
    • input_sync
      • input_event()
  • 举例一个简单的input设备驱动:

    https://landlock.io/linux-doc/landlock-v7/input/input-programming.html