基于linux-4.4.20的 input 框架介绍

来源:互联网 发布:罗马 拜占庭 知乎 编辑:程序博客网 时间:2024/06/08 02:33

1.Linux Input子系统简介

Input子系统是对输入设备的抽象。人机交互的输入设备复杂多样,包括鼠标、键盘、按键、触摸屏、摇杆、麦克风等,在linux设备模型下,这些设备都是字符设备。繁杂的设备给开发带来了巨大工作量,为了简化设备驱动开发,linux将这些设备共性抽象出来,建立了Input子系统。

Linuxinput设备上报数据采用type code value的形式。

设备驱动层(Input Driver)主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件按照核心层定义的规范提交给核心层。

核心层(Input Core)负责连接驱动层和事件处理层,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按键数据、触摸屏坐标数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。设备驱动(input driver)和处理程序(handler)的注册需要通过核心层来完成,核心层接收来自驱动层的数据信息,并将数据信息选择对应的handler去处理

事件处理层(EventHandler)生成用户空间访问的接口(设备节点),并处理core层提交的数据并且缓存,等待用户空间读取,三者结构见下图:


 

2.Input子系统数据

   任何程序,核心都是数据的传递和控制,因此,如果想要弄清楚一个模块,必须把其主要结构体弄清楚,input子系统所涉及的数据结构如下:

1.input_dev结构体

        其中,input_dev是硬件驱动层,代表一个input设备,通过全局的input_dev_list链接在一起,在驱动设备注册的时候,通过input_register_devcie接口实现。上图省略了input_dev

一些比较重要字段,主要是此设备支持的typecode类型,现列出如下:

unsigned longevbit[BITS_TO_LONGS(EV_CNT)]; //事件支持的类型,是typecodevalue中的type

unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//按键位图,是code值。

unsigned longrelbit[BITS_TO_LONGS(REL_CNT)]; //相对坐标,属于code值。

unsigned longabsbit[BITS_TO_LONGS(ABS_CNT)];//绝对坐标,其中触摸屏驱动使用的就是这个

unsigned longmscbit[BITS_TO_LONGS(MSC_CNT)];

unsigned longledbit[BITS_TO_LONGS(LED_CNT)];

unsigned longsndbit[BITS_TO_LONGS(SND_CNT)];

unsigned longffbit[BITS_TO_LONGS(FF_CNT)];

unsigned longswbit[BITS_TO_LONGS(SW_CNT)];

 

结构体中open函数一般用来实现硬件初始化,或者开关时候的数据flush,如果相关操作已经在probe中实现,函数可以不用定义;close函数实现和open相反的功能。

input_register_device的内部实现:往input_device_list加入新增的input_device节点,然后对input_handler_list的所有结点(也就是所有的handler)进行遍历,通过handler 的.id_table查看该handler是否支持该device,对支持的device调用该handler.connect,一一地构建evdev结构体,从而构建了input_handle结构体。

 

2.input_handler结构体

input_handler是事件处理层,通过全局的input_handler_list链接在一起,事件处理器注册的时候通过input_register_handler实现(事件处理器一般内核自带,一般不需要我们来写);

input_register_handler的内部实现:往input_handler_list加入新增的handler节点,然后对input_device_list的所有结点(也就是所有的device)进行遍历,通过.id_table查看该device是否支持该handler,对支持的device调用.connect,一一地构建evdev结构体,从而构建了input_handle结构体。

Input_handler中,包含了几个重要函数,如下:

event函数:当事件处理器接收到了来自input设备传来的事件时调用的处理函数,负责处理事件;
  connect函数:input设备模块注册到内核的时候调用的,该函数申请evdev结构体,并且完成初始化,从而实现了handle的申请和初始化,完成input_devinput_handler的关联,最后注册字符设备;
  disconnect函数:实现connect相反的功能。

3.evdev,evdev_clientinput_handle结构体

上面connet中用到的input_handle evdevevdev_client结构体属于核心层,input_handle代表一个配对的input设备与input事件处理器。Input_handle没有一个全局的链表,它注册的时候将自己分别挂在了input_dev input_handlerh_list上了,所以通过input_devinput_handler就可以找到input_handle

 

Input_handle结构体字段说明如下:

void *private;  //每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,初始化handle时,保存到这里。    

int open;       //打开标志,每个input_handle打开后才能操作,这个一般通过事件处理器的open方法间接设置     

const char *name;     

struct input_dev *dev; //关联的input_dev结构    

struct input_handler *handler; //关联的input_handler结构     

struct list_head   d_node; //input_handle通过d_node连接到了input_dev上的h_list链表上     

struct list_head   h_node; //input_handle通过h_node连接到了input_handlerh_list链表上

 

evdev结构体的申请和初始化主要是由handlerconnet函数完成,

 

evdev_client结构体主要是在input字符设备的open函数中完成申请和初始化。此结构体主要是用来申请缓存空间,存放event送上来的事件。

 

3.Input子系统在linux下生成的文件

   Linux kernel启动完成后,input子系统按照先后顺序会生成以下文件:

1./proc/bus/input/devices/proc/bus/input/handlers/dev/input目录

Deviceshandlers两个文件,主要是用来查看input子系统所支持的设备和事件处理方法。/dev/input主要是为后续event设备的注册提供一个公共目录,具体如下图:

 

2. sys属性文件以及字符设备文件

rda_keypad.c为例,当keypad driver注册完成后,会匹配dts中注册的keypad设备,然后调用keypadprobe函数。Keyapd probe函数中,主要完成如下工作:

input_register_device中,生成sys/devcies/platform/rda-keypad/目录以及相关子文件。如果需要注册keypad本身特有的一些属性文件,都是放在此目录下面的,input class本身的属性文件,位于sys/devices/platform/rda-keypad/input目录下,包括该input设备名字,支持特性等,最重要的字符设备/dev/input/event0也是在此时生成。该字符设备主要是被上层用来获取数据,

 

在上面章节中,input_reigster_device完成的仅仅是Inputdriver层,event handler层呢?对于input子设备而言,提交事件的处理方法已经被归纳成几个固定的类并且已经在代码中实现,比如evdev.cmousedev.c,所以只要在probe函数中,把相应id设置好,就能够和相应event handler函数中的id table匹配。所以,event handler基本不用再写handler处理,这也是linux input 框架带来的好处。

 

linux设备启动后,可以功过getevent/dev/input/eventx来获得input设备上报的数据,上报数据结构为: type code valueTypecode的定义可以参考input-event-codes.h文件。

 

4.Input字符设备文件操作函数调用

Linux为所有的文件提供了统一的文件操作接口,input设备也不例外。以evdev.c为例,其文件操作接口如下:

Static const struct file_operationsevdev_fops = {

        .owner= THIS_MODULE;

        .read= evdev_read;

        .write= evdev_write;

        .poll= evdev_poll;

        .open= evdev_open;

        .release= evdev_realease;

        .unlocked_ioctl= evdev_ioctl;

#ifdef CONFIG_COMPAT

        .compat_ioctl= evdev_ioctl_compat;

#endif

        .fasync= evdev_fasync;

        .flush= evdev_flush;

        .llseek= no_llseek;

};

 

        Evdev_open函数主要是申请evdev_client结构,为input事件申请缓存空间,如下:

上图中比较重要的是open()函数,如果需要在读取数据前完成一些初始化,可以在此接口中实现,但是一定要在release接口中做相反工作。

 

evdev_read函数主要是从缓存中读取input_event上报的event事件,并且返回给用户空间,如下图:

Evdev_write函数主要是把event事件从用户空间拷贝到内核空间,然后发送到client_buffer的缓存队列中,等待读取。

Evdev其他的文件操作函数因为使用很少,此处不再介绍了。

 

0 0