linux内核代码解析-input子系统

460 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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.事件驱动

事件驱动注册程序分析:

7.设备驱动

8.输入子系统核心层