为什么要用INPUT子系统
在不采用input子系统,而是自己实现的按键字符驱动中,会自己注册驱动,提供file_operations接口,并在读接口中,读取按键的电平值上传给应用。在linux系统中(linux4.9.88),构建了input子系统,所有采用input子系统的设备,在有输入事件后都会主动上报输入事件。
在输入设备中会有以下几个问题:
a. 何时上报?是在输入设备输入事件中断产生时上报。
b. 如何上报?输入设备在中断函数中调用input提供的input_report_key函数。
kernel源码分析
static int __init input_init(void) { int err; err = class_register(&input_class); if (err) { pr_err("unable to register input_dev class\n"); return err; } err = input_proc_init(); if (err) goto fail1; err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input"); if (err) { pr_err("unable to register char major %d", INPUT_MAJOR); goto fail2; } ...... }
先注册一个 input 类型的 class
static int __init input_proc_init(void) { struct proc_dir_entry *entry; proc_bus_input_dir = proc_mkdir("bus/input", NULL); if (!proc_bus_input_dir) return -ENOMEM; entry = proc_create("devices", 0, proc_bus_input_dir, &input_devices_proc_ops); if (!entry) goto fail1; entry = proc_create("handlers", 0, proc_bus_input_dir, &input_handlers_proc_ops); ...... }
然后在 /proc/input 下创建两个文件,用于向应用层提供 input 类型的设备信息
用户层
应用层代码会通过如下结构体读取事件信息
struct input_event { struct timeval time; unsigned short type; unsigned short code; unsigned int value; };
tv_sec / tv_usec:事件产生的事件
type:事件类型,如果是按键就选 EV_KEY,如下图:
#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)
EV_CNT 决定了事件类型的最大数量
code:哪个设备产生的事件,对于键盘按键,code 的取值小于 BTN_MISC,每一个code值对应键盘的一个按键,如下图:
value:对于按键,可以设置产生事件value等于1、没产生事件value等于0
应用代码实例:
static struct input_event inputevent; err = read(fd, &inputevent, sizeof(inputevent));
驱动层
input 设备类型结构体
struct input_dev { const char *name; const char *phys; const char *uniq; struct input_id id; unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // 1比特对应一个事件类型,EV_CNT为32,即最大32个事件类型,只需要一个 unsigned long 类型变量,所以 BITS_TO_LONGS(EV_CNT) == 1
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
......
ktime_t timestamp[INPUT_CLK_MAX];
bool inhibited;
};
struct input_dev *input_allocate_device(void);
动态申请了一个 struct input_dev 类型变量dev,并初始化dev->dev.kobj.name = "input0",这个就是设备节点的名字;dev->dev的class name为 "input"
int __must_check input_register_device(struct input_dev *);
如果 input_dev.dev没有设置父设备,kobj 是 /sys/devices/virtual/input,并作为 input_dev.dev的父设备。如果 input_dev.dev 没有设置 init_name,inpu_dev.dev的名字默认为 inputX(X表示数字,每添加一个,数字加1)
evdev这个设备干啥用
梳理这个驱动用途
标签:err,dev,linux,input,EV,proc,子系统,define From: https://www.cnblogs.com/god-of-death/p/17224630.html