需要了解Linux input子系统编程、分析与模板的原理

网友投稿 259 2022-10-29

需要了解Linux input子系统编程、分析与模板的原理

设备驱动层是具体硬件相关的实现,也是驱动开发中主要完成的部分,

输入核心层主要提供一些API供设备驱动层调用,通过这些API设备驱动层上报的数据就可以传递到事件处理层,

事件处理层负责创建设备文件以及将上报的事件传递到用户空间,

input的使用

struct input_dev --122--> 这个name不是设备名,input子系统的设备名在子系统源码中指定的,不是这。 --129--> 设备支持的输入事件位图,EV_KEY,EV_REL, etc --130--> 对于按键事件,设备支持的输入子事件位图 --132--> 对于相对坐标事件,设备支持的相对坐标子事件位图 --133--> 对于绝对坐标事件,设备支持的绝对坐标子事件位图 --134--> 混杂设备的支持的子事件位图 --180-->表示这是一个device。 --182-->h_list是用来链接相关handle的链表 --183-->node用来链接其他input_dev的链表

分配/释放

初始化

下面这些是按键子事件的类型,可以看到对PC键值的定义

除了对常用的事件进行描述,内核同样提供了工具将这些事件正确的填充到input对象中描述事件的位图中。

//第一种//这种方式非常适合同时注册多个事件button_dev->evbit[0] = BIT_MASK(EV_KEY); button_dev->keybit[BIT_WORD(BTN_0|BTN_1)] = BIT_MASK(BTN_0|BTN_1);

//第二种//通常用于只注册一个事件set_bit(EV_KEY,button_dev.evbit);set_bit(BTN_0,button_dev.keybit);

注册/注销

初始化好了一个input对象,接下来就需要将其注册到内核

//注册input对象到内核int input_register_device(struct input_dev *dev);//从内核注销一个input对象void input_unregister_device(struct input_dev *dev);

驱动层报告事件

在合适的时机(由于输入最终是中断表示的,所以通常在驱动的中断处理函数中)驱动可以将注册好的事件上报,且可以同时上报多个事件,下面是内核提供的API

//上报指定的事件+子事件+值void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value);//上报键值void input_report_key(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_rel(struct input_dev *dev,unsigned int code,int value);//同步所有的上报void input_sync(struct input_dev *dev);

上报事件有2点需要注意:

report函数们并不会真的上报,只是准备上报,sync才会真的将刚刚report的事件真的上报搭input核心

input核心会进行裁决再上报的事件处理层,所以对于按键事件,一定要先报1再报0(或者反过来),不能只report 1或0, 这样核心会认为是一个事件被误触发了多次而只上报一次,虽然我们真的按下了多次。

应用层解析

事件处理层最终会将驱动sync一次时所有report的事件组织成一个struct input_value[]的形式上报到应用层,在应用层从相应的设备文件中获取上报的事件的时候,需要注意:

收到数组元素的数量会比底层多一个空元素,类似于写of_device_id[]时最后的空元素,这点应用层在解析的时候需要注意。

事件处理层并不会缓存收到的事件,如果有新的事件到来,即使旧的事件没有被读取,也会被覆盖,所以应用程序需要及时读取。

前文已经说过,"include/uapi/linux/input.h"中的宏是应用层和驱动层共用的通信协议,所以应用层在解析收到的struct input_value对象的时候,只需要"include "即可使用其中的宏。

input分析

上文已经说过,input子系统使用三层结构来实现驱动事件到应用层的传递。具体的,这三个层次每一个层次都由一条结构体链表组成,在设备驱动层,核心结构体是input_dev;在input核心层,是input_handle;在事件处理层,是input_handler。内核通过链表和指针将三者结合到一起,最终实现了input_dev和input_handler的多对多的映射关系,这种关系可用下图简单描述。

模板

下面的这个模板首先使用input子系统上报按键事件,然后在应用层读取。

input按键设备驱动

应用层获取键值

#include struct input_event { struct timeval time; unsigned short type; unsigned short code; int value;};int main(int argc, char * const argv[]){ int fd = 0; struct input_event event[3] = {0}; //3!!!,驱动上传了2个事件,第三个用来装空元素 int ret = 0; fd = open(argv[1],O_RDONLY); while(1){ ret = read(fd,&event,sizeof(event)); printf("ret:%d,val0:%d,val1:%d,val12:%d\n",ret,event[0].value,event[1].value,event[2].value); //2!!!,最后一个是空 sleep(1); } return 0;}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java实现AES算法的实例代码
下一篇:docker部署持续集成环境
相关文章

 发表评论

暂时没有评论,来抢沙发吧~