INPUT子系统
输入有按键, 鼠标, 键盘, 触摸屏等, linux做了一个专门的框架, 本质上还是字符设备
不同设备的输入代表的含义不同, 我们需要按照要求上报事件
主要分为三层:
- 驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
- 核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
- 事件层:主要和用户空间进行交互。
input核心层会帮我们注册一个字符设备类, 主设备号为13, 创建的类放在/sys/class目录下面, 在文件drivers/input/input.c
进行创建
内核已经写好了input驱动, 需要我们完善具体的输入设备, 按照input子系统驱动框架
实际编写
实际就是注册设备, 使用input_dev结构体
evbit:事件的类型, 可选的事件类型定义在include/uapi/linux/input.h文件中
那么就需要注册EV_KEY事件,如果要使用连按功能的话还需要注册EV_REP事件
设置按键的值, 就是keybit, Linux内核定义了很多按键值,这些按键值定义在include/uapi/linux/input.h文件中
- 申请结构体
struct input_dev *input_allocate_device(void)
得到一个input结构体
- 注销结构体
void input_free_device(struct input_dev *dev)
- 注册input_dev驱动
int input_register_device(struct input_dev *dev)
- 卸载驱动
void input_unregister_device(struct input_dev *dev)
- 初始化结构体
10 /*********第一种设置事件和事件值的方法***********/
11 __set_bit(EV_KEY,inputdev->evbit);/* 设置产生按键事件*/
12 __set_bit(EV_REP,inputdev->evbit);/* 重复事件*/
13 __set_bit(KEY_0,inputdev->keybit);/*设置产生哪些按键值*/
16 /*********第二种设置事件和事件值的方法***********/
17 keyinputdev.inputdev-> evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
18 keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
21 /*********第三种设置事件和事件值的方法***********/
22 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
23 input_set_capability(keyinputdev.inputdev,EV_KEY,KEY_0);
- 上报事件
对于按键来说就是在按键中断或者销抖函数中获取按键的按下的情况, 然后上报, 可以使用
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
dev:需要上报的input_dev。type:上报的事件类型,比如EV_KEY。code:事件码,也就是我们注册的按键值,比如KEY_0、KEY_1等等。value:事件值,比如1表示按键按下,0表示按键松开。
这个函数是用来提供所有时间的, linux也有专门的函数处理特定的事件,
staticinline void input_report_key(struct input_dev *dev ,
unsigned int code,int value)
{
input_event(dev,EV_KEY,code,!!value);
}
对于按键而言, 也可以使用这个
- 上报完成之后使用函数进行同步, 实际上也是一个事件, 同步事件
void input_sync(struct input_dev *dev)
1 /* 用于按键消抖的定时器服务函数*/
2 voidtimer_function(unsignedlongarg)
3 {
4 unsigned char value;
5
6 value = gpio_get_value(keydesc->gpio);/* 读取IO值*/
7 if(value ==0){/* 按下按键*/
8 /* 上报按键值*/
9 input_report_key(inputdev,KEY_0,1);/* 1,按下*/
10 input_sync(inputdev);/* 同步事件*/
11 }else{/* 按键松开*/
12 input_report_key(inputdev,KEY_0,0);/*0,松开*/
13 input_sync(inputdev);/* 同步事件*/
14 }
15 }
最后的结果
内核使用input_event这个结构体来表示所有的输入事件,input_envent结构体定义在include/uapi/linux/input.h文件中
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
time:时间,也就是此事件发生的时间
struct timeval { __kernel_time_t tv_sec; /* seconds */ __kernel_suseconds_t tv_usec; /* microseconds */ };
都是long类型的
type:事件类型,比如EV_KEY, code:事件码,比如在EV_KEY事件中code就表示具体的按键码, value:值,比如EV_KEY事件中value就是按键值,表示按键有没有被按下
测试
/lib/modules/4.1.15 # hexdump /dev/input/event1
0000000 4497 0000 8ac1 0008 0001 000b 0001 0000
0000010 4497 0000 8ac1 0008 0000 0000 0000 0000
0000020 4497 0000 1167 000a 0001 000b 0000 0000
0000030 4497 0000 1167 000a 0000 0000 0000 0000
使用这个命令返回的就是上面的input_event结构体
hexdump是Linux下的一个二进制文件查看工具,它可以将二进制文件转换为ASCII、八进制、十进制、十六进制格式进行查看。
32位表示秒, 32位表微秒, 16位的时间类型, 16位按键码, 32位状态
0000000 4497 0000 8ac1 0008 0001 000b 0001 0000
| 编号 | 秒 | 微秒 | type | code | value |
EV_KEY事件值为1,EV_SYN事件值为0。因此第1行表示EV_KEY事件,第2行表示EV_SYN事件, KEY_0这个按键编号为11,对应的十六进制为0xb
软件编写
通过读取文件得到的形式是input_event形式的
根据结构体的各个数值进行使用, 一般key作为键盘使用, button为按键
Linux自带的驱动
Device driver –> Input device support –> -*- Generic input layer (needed for keyboard, mouse, …) , keyboard –> GPIO Buttons
drivers/input/keyboard/gpio_keys.c文件
gpio_keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
pinctrl-name = "default";
pinctrl-0 = <&pinctrl_key>;
status = "okay";
key0 {
label = "GPIO KEY Enter";
linux,code = <KEY_ENTER>;
gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
};
};
设置为回车键