
input 子系统 Input Core 实现代码是 ~/drivers/input/input.c 以及 ~/include/linux/input.h 两个文件。为我们提供了注册输入子系统的 API,通过操作这些 API 就可以实现输入事件的注册、初始化、上报、注销等等工作。下面我们介绍输入子系统常用的 API 接口及数据结构。
一、input_dev 结构体
在输入子系统中 input_dev 代表一个具体的输入设备,后面将会根据具体的设备来初始化这个结构体,结构体成员介绍如下:(input_dev 参数很多,有些不需要我们手动配置,所以这里只列出和介绍常用的参数,完整内容位于 input.h 文件)。
struct input_dev {
const char *name; //提供给用户的输入设备的名称
const char *phys; //提供给编程者的设备节点的名称
const char *uniq; //指定唯一的ID号
struct input_id 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)]; //记录支持的绝对坐标位图
......
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
结构体成员中最重要的是 evbit、keybit、relbit 等数组,这些数组设置了设备输入事件的类型和键值。
- evbit: 用于指定支持的事件类型,这要根据实际输入设备能够产生的事件来选择,可选选项如下所示。
#define EV_SYN 0x00 //同步事件
#define EV_KEY 0x01 //用于描述键盘、按钮或其他类似按键的设备。
#define EV_REL 0x02 //用于描述相对位置变化,例如鼠标移动
#define EV_ABS 0x03 //用于描述绝对位置变化,例如触摸屏的触点坐标
#define EV_MSC 0x04 //其他事件类型
#define EV_SW 0x05 //用于描述二进制开关类型的设备,例如拨码开关。
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)2
3
4
5
6
7
8
9
10
11
12
13
14
上面代码中前几个宏定义较为常用的输入事件类型,介绍如代码后面所示。完整的介绍可以参考内核源码目录下的 ~/Documentation/input/event-codes.txt 内核文档。很明显,我们按键要使用的按键的事件类型应该使用 EV_KEY。
- keybit:记录支持的键值,“键值”在程序中用于区分不同的按键,可选“键值”如下所示。
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 62
3
4
5
6
7
可以看出“键值”就是一些数字。只要实际设备与按键对应即可。例如本章的按键可以使用 KEY_1、也可以使用 KEY_4 等。
- relbit、absbit:这两个参数和上面的 keybit 都和参数 evbit 有关,如果 evbit 中只选择了
EV_KEY,那么我们就不需要设置 relbit(相对坐标)和 absbit(绝对坐标)以及后面省略成员的内容。这些内容使用到时再具体介绍。使用不同的输入事件类型需要设备不同的。
总之,input_dev 结构体成员很多,但是对应到一个具体的输入设备,只需要设置自己用到的其中一两个属性。
二、input_dev 结构体的申请和释放
一个 input_dev 结构体代表了一个输入设备,它实际会占输入子系统的一个次设备号。input 子系统为我们提供了申请和释放 input_dev 结构体的函数。由于 input_dev 结构体的成员很多,初始化过程也相对麻烦,一般都使用 input 子系统为我们提供的接口函数来申请和释放 input_dev 结构体,如下所示。
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; //dev->dev为struct device类型结构体
device_initialize(&dev->dev); //初始化dev->dev结构体内部成员
mutex_init(&dev->mutex); //初始化互斥锁
spin_lock_init(&dev->event_lock); //初始化自旋锁
timer_setup(&dev->timer, NULL, 0); //初始化定时器
INIT_LIST_HEAD(&dev->h_list); //初始化handle链表节点
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;
}
EXPORT_SYMBOL(input_allocate_device);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
参数:无
返回值:
- 成功:
struct input_dev类型指针 - 失败: NULL
三、注册和注销 input_dev 结构体
input_dev 申请成功后,我们需要根据自己的实际输入设备配置 input_dev 结构体,具体配置在实验代码编写部分会详细说明,配置完成后还要使用注册和注销函数将 input_dev 注册到输入子系统。注册和注销函数如下:
INFO
int input_register_device(struct input_dev *dev)
参数:dev:struct input_dev 类型指针
返回值:
- 成功: 0
- 失败: 返回非 0 值
input_register_device 函数将输入设备(input_dev)注册到输入子系统的核心层。该函数使用需要注意以下几点:
- 使用该函数注册的
input_dev必须是使用input_allocate_device函数申请得到的。 - 注册之前需要根据实际输入设备配置好
input_dev结构体。 - 如果注册失败必须调用
input_free_device函数释放input_dev结构体。 - 如果注册成功,在函数退出时只需要使用
input_unregister_device函数注销input_dev结构体不需要再调用input_free_device函数释放input_dev结构体。
INFO
void input_unregister_device(struct input_dev *dev)
参数:dev:struct input_dev 类型指针
返回值: 无
input_unregister_device 是注销函数,输入子系统的资源是有限的,不使用时应当注销。调用 input_unregister_device 注销函数之后就不必调用 input_free_device 函数释放 input_dev。
四、上报事件函数和上报结束函数
以按键为例,按键按下后需要使用上报函数向输入子系统核心层上报按键事件,并且上报后还要发送上报结束信息。函数定义如下所示。
INFO
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
input_event 函数用于上报事件,共有 4 个参数介绍如下。
参数:
dev,指定输设备(input_dev结构体)。type,事件类型。我们在根据实际输入设备配置input_dev结构体时会设置input_dev->evbit参数,用于设置输入设备能够产生的事件类型(可能是多个)。上报事件时要从“能够产生”的这些事件类型中选择。code,编码。以按键为例,按键的编码就是我们设置的按键键值。value,指定事件的值。
返回值: 无
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}2
3
4
5
6
7
8
9
input 子系统为不同的输入事件函数提供了不同的函数接口,这些函数接口只是对 input_event 函数进行简单的封装,具体的参数参照 input_event 函数。input_report_key 用于上报按键事件,input_sync 用于发送同步信号,表示上报结束。