持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
input子系统
输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见。同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分析很有意义。
1.Linux如何查看所有注册的输入子系统
1. /dev/input目录
/dev/input目录下的事件都是在驱动中调用input_register_device(struct input_dev *dev)产生的。
如我的uos中显示:
2. 与event对应的设备信息。
位于/proc/bus/input/devices,例子如下:
3. 写测试程序读取鼠标和键盘的上报信息。
代码如下:
#include <linux/input.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
//具体哪个event要通过设备信息来看。
#define INPUT_MOUSE "/dev/input/event6" /* 我电脑的鼠标*/
#define INPUT_KEY "/dev/input/event1"
#define INPUT_MOUSE1 "/dev/input/mouse0"
int main(void)
{
int fd = -1, ret = -1;
struct input_event ev;
fd = open(INPUT_MOUSE , O_RDONLY); /* 打开鼠标的设备文件 */
if(fd < 0)
{
perror("open");
return -1;
}
while(1)
{
memset(&ev, 0, sizeof(struct input_event));
ret = read(fd, &ev, sizeof(struct input_event)); /* 读鼠标(会阻塞) */
if(ret != sizeof(struct input_event))
{
perror("read");
close(fd);
return -1;
}
/* 打印读到的键值 */
printf("--------------------\n");
printf("type = %u.\n", ev.type);
printf("code = %u.\n", ev.code);
printf("value = %u.\n", ev.value);
}
return 0;
}
执行效果:鼠标移动就会打印数据。
4.字符设备和input子系统处理流程对比
input子系统本质: 将字符设备驱动进行封装.
字符设备(按键举例):中断触发->延后机制->字符设备->文件操作接口->提供给应用层
input子系统(按键举例) :中断触发->延后机制->input子系统专用的API函数->提供给应用层
2. input子系统结构图
input子系统由下面3部分组成:
驱动层(Drivers)
输入子系统核心层(Input Core)
事件处理层(Event Handler)
3. input子系统介绍
Linux input 子系统将一个输入设备的输入过程分成了设备驱动(input device driver)和事件驱动(input event driver)两个层。前者负责从底层硬件采集数据;后者负责与用户程序接口,将采集到的数据分发给不同的用户接口。通过这样的设计,将千差万别的设备统一到了为数不多的几种驱动接口上。同一种事件驱动可以用来处理多个同类设备;同一个设备也可以和多种事件驱动相衔接。而事件驱动和设备驱动则由输入核心层进行连接,匹配。而事件驱动是系统已经定义好的, 再写设备驱动时需要哪种类型的事件驱动就去做匹配就可以了.
设备驱动(input device driver) :
自己写的,比如 drivers/hid/usbhid/usbmouse.c
事件驱动(input event driver) :
系统已经开发好了 drivers/input/evdev.c
专门处理输入设备产生的按键(Event)事件: evdev
鼠标事件:mousedev
上:输入事件驱动层 (打包数据,面向应用)
中:输入核心层 (向下提供注册接口,向上给具体的hander发送数据)
下:输入设备驱动层 (底层驱动,面向硬件)
4. input子系统工作过程
以鼠标按键为例说明input子系统的工作过程:
鼠标按下->出发中断(中断是已经注册好的)->中断处理函数->在函数中就会读硬件寄存器来判断按下的是哪个按键和状态->将按键信息上报给input core层->input core层处理完成后会上报给input event层,input event层会将我们的输入事件封装成一个input_event结构体放入一个缓冲区中->应用层read就会将将缓冲区中的数据读取出去.
5.重要结构
Input_device: 代表着具体的输入设备,它直接从硬件中读取数据,并以事件的形式转发
Hanler: 代表接收某一类事件的上层接口,对应于一类事件设备文件
Handle : 用于将input_device 和 handler 连接起来,对应于某1个具体的设备文件
Client: 对应于用户程序对文件的访问接口,每open一次事件驱动,就创建一个client
input子系统的核心层维护着两条链表
static LIST_HEAD(input_dev_list); //记录所有的输入设备
static LIST_HEAD(input_handler_list); //记录所有的事件驱动
每当有新设备或者新的事件驱动被系统加载(调用input_register_device()或者input_register_driver()),都会扫描整个链表,并调用函数input_match_device(struct input_handler *handler, struct input_dev *dev)尝试配对工作.input_handler->id_table记录了需要匹配的特征.
id_table中有bitmap来控制匹配.
6.事件驱动
事件驱动注册程序分析: