一、前言
linux内核专门做了一个input子系统的框架来处理输入实践。输入设备的本质还是字符设备,只是在其基础上套上了input框架。
二、子系统
2.1、简介
input子系统就是管理输入的子系统,和pinctrl、gpio子系统一样,都是linux内核针对某一类设备而创建的框架,如下:
驱动编写只需要关注中间的驱动层、核心层、事件层,分工如下:
驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
事件层:主要和用户空间进行交互
2.2、编写流程
input 核心层会向 Linux 内核注册一个字符设备,在drivers/input/input.c 这个文件,input.c 就是 input 输入子系统的核心层。
注册一个input类,系统启动后,就会在/sys/class目录下有一个input子目录。
input子系统的所有设备主设备号都为13,因此在使用input子系统处理输入设备的时候就不需要注册字符设备了,只需要向系统注册一个input_device即可。
2.2.1、注册input_dev
2.2.1.1、申请
input_dev表示input设备,定义在include/linux/input.h,使用input_allocate_device来申请。
struct input_dev *input_allocate_device(void)
如果要注销input设备,使用input_free_device来释放前面申请到的input_dev
void input_free_device(struct input_dev *dev)
2.2.1.2、初始化
需要初始化的内容主要为事件类型(evbit)和事件值(keybit)。
2.2.1.3、注册
初始化完成后就需要向linux内核注册input_dev,
int input_register_device(struct input_dev *dev)
同样,注销驱动的时候也需要使用input_unregister_device函数来注销前面注册的input_dev,
void input_unregister_device(struct input_dev *dev)
示例:
struct input_dev *inputdev; //input结构体变量
static int __init xxx_init(void)
{
//1、申请
inputdev = input_allocate_device(); //申请input_dev
inputdec->name = "test_input"; //设置input_dev名字
//2、初始化
__set_bit(EV_KEY, inputdev->evbit);
__set_bit(EV_REP, inputdev->evbit);
__set_bit(KEY_0, inputdev->keybit);
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_MASK(EV_REP);
keyinputdev.inputdev->keybit[BIT_WORd(KEY_0)] |=
BIT_MASK(KEY_0);
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_MASK(EV_REP);
input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
//3、注册
input_register_device(inputdev);
}
static void __exit xxx_exit(void)
{
//注销
input_unregister_device(inputdev);
//释放
input_free_device(inputdev);
}
2.2.2、上报事件
向内核注册input_dev后,还得将获取到的具体输入值或者输入事件,上报给内核,比如在按键中断,定时中断中将按键值上报给内核,这样内核才能获取输入值。
不同的事件,其上报事件的API函数不同,下面是常见的:
void input_event(struct input_dev *dev,
unsigned int type,
unsigned int code,
int value)
input_event 函数可以上报所有的事件类型和事件值,Linux 内核也提供了其他的针对具体事件的上报函数,这些函数其实都用到了 input_event 函数。比如上报按键所使用的input_report_key 函数:
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_KEY, code, !!value); }还有其他事件上报函数:
void input_report_rel(struct input_dev *dev, unsigned int code, int value) void input_report_abs(struct input_dev *dev, unsigned int code, int value) void input_report_ff_status(struct input_dev *dev, unsigned int code, int value) void input_report_switch(struct input_dev *dev, unsigned int code, int value) void input_mt_sync(struct input_dev *dev)
上报事件以后还需要使用input_sync函数来告诉内核input子系统上报结束:
void input_sync(struct input_dev *dev)
示例如下:
void timer_function(unsigned long arg)
{
unsigned char value;
value = gpio_get_value(keydesc->gpio);
if(value == 0){
input_report_key(inputdev, KEY_0, 1);
input_sync(inputdev);
} else {
input_report_key(inputdev, KEY_0, 0);
input_sync(inputdev);
}
}
2.3、input_event结构体
Linux 内核使用 input_event 这个结构体来表示所有的输入事件,input_envent 结构体定义在include/uapi/linux/input.h 文件中,结构体内容如下
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};



