04、input_dev 输入设备
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
2
3
4
5
6
可以看出“键值”就是一些数字。只要实际设备与按键对应即可。例如本章的按键可以使用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注册到输入子系统。注册和注销函数如下:
::: 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结构体。
::: void input_unregister_device(struct input_dev *dev)
参数: dev:struct input_dev类型指针
返回值: 无 :::
input_unregister_device是注销函数,输入子系统的资源是有限的,不使用是应当注销。 调用input_unregister_device注销函数之后就不必调用input_free_device函数释放input_dev。
四、上报事件函数和上报结束函数
以按键为例,按键按下后需要使用上报函数向输入子系统核心层上报按键事件,并且上报后还要发送上报结束信息。函数定义如下所示。
::: 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用于发送同步信号,表示上报结束。输入子系统数据结构体