input子系统分析

来源:互联网 发布:西医综合网络课程 编辑:程序博客网 时间:2024/06/16 18:28

linux系统中,input输入子系统驱动主要可以分为:设备驱动层、input core层和input handler事件处理层。

设备驱动层提供具体用户设备驱动,由struct input-dev 结构表示,input_register_device来注册。

input handler事件处理层 主要和用户空间交互

input core 层负责管理系统中的input dev设备和inputhander事件处理,并起到承上启下作用,负责输入设备驱动和input handler之间信息传输

input子系统结构如图:

主要分为两部分:设备驱动层的注册、handler的注册,在注册的过程中会有handle的创建以及它的作用,会说明设备驱动与handler是如何匹配的

                                                    设备驱动层的注册

注册一个input设备主要是对input_register_device的分析

1、  input_dev结构

struct input_dev {

const char *name;                     /*设备名*/

struct input_id id;                       /*id号,用于匹配事件处理层handler*/


unsigned longevbit[BITS_TO_LONGS(EV_CNT)]; /*用于记录支持的事件类型的位图*/

unsigned longkeybit[BITS_TO_LONGS(KEY_CNT)]; /*记录支持的按键值的位图*/

unsigned longrelbit[BITS_TO_LONGS(REL_CNT)]; /*记录支持的相对坐标的位图  */

unsigned longabsbit[BITS_TO_LONGS(ABS_CNT)]; /*记录支持的绝对坐标的位图  */

unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];

unsigned longledbit[BITS_TO_LONGS(LED_CNT)];  /*led */

unsigned longsndbit[BITS_TO_LONGS(SND_CNT)]; /*beep*/

unsigned int keycodemax;  /*支持的按键值的个数*/

unsigned int keycodesize;  /*每个键值的字节数  */

void *keycode;                    /*存储按键值的数组首地址*/

int (*setkeycode)(structinput_dev *dev, const struct input_keymap_entry *ke,

                      unsigned int *old_keycode);                  /* 修改键值的函数,可选 */

int (*getkeycode)(structinput_dev *dev,

                      struct input_keymap_entry *ke);        /* 获取扫描码的键值,可选  */

unsigned int repeat_key;                                               /*最近一次按键值,用于连击*/

struct timer_list timer;                                                  /*自动连击计时器              */

struct input_mt *mt;                                                      /*多点触控状态*/

struct input_absinfo *absinfo;                                     /*绝对轴信息(当前值,最小值,最大值,平,模糊,解析度)*/

unsigned longkey[BITS_TO_LONGS(KEY_CNT)];  /*反映当前按键状态的位图       */

unsigned longled[BITS_TO_LONGS(LED_CNT)];  /*反映当前led状态的位图        */

unsigned longsnd[BITS_TO_LONGS(SND_CNT)]; /*反映当前beep状态的位图     */

int (*open)(struct input_dev*dev);                           /*打开函数 */

void (*close)(struct input_dev*dev);                          /*关闭函数 */

int (*flush)(struct input_dev*dev, struct file *file); /* 断开连接时冲洗数据 */

struct list_head        h_list;                                           

struct list_head        node;

struct device dev;

};

现在通过分析注册之前的配置过程,理解input_dev结构作用。

input_allocate_device()            //初始化一个设备

dev.type = &input_dev_type;

dev.class = &input_class;

device_initialize(dev);

dev->name = xxx_DEVICE;                //设备名

dev->id.vendor =0x00;                        //设备id的配置

dev->id.product =pid;

dev->id.version=vid;

set_bit(EV_ABS, dev->evbit);             //支持绝对坐标事件  (为了不影响分析,事件支持全部类型在文章末尾列出)

set_bit(EV_KEY, dev->evbit);             //支持按键事件

set_bit(ABS_X, dev->absbit);            //相对于屏幕的绝对x坐标

set_bit(ABS_Y, dev->absbit);             //相对于屏幕的绝对y坐标

set_bit(ABS_PRESSURE, dev->absbit);//绝对坐标事件压力值

set_bit(BTN_TOUCH,tpd->dev->keybit);// 触摸接触事件

set_bit(INPUT_PROP_DIRECT,dev->propbit);//表明设备的坐标直接和屏幕坐标向对应

//设置坐标的位置范围(例如480*720的屏幕TPD_RES_X=480,TPD_RES_Y=720),预示着超出这个范围不再有效

input_set_abs_params(tpd->dev,ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0);

input_set_abs_params(tpd->dev,ABS_MT_POSITION_Y, 0, TPD_RES_Y, 0, 0);


2、  input_register_device函数操作

device_add(&dev->dev);                                   //input_dev->dev注册

list_add_tail(&dev->node,&input_dev_list);  //加入input链表

//遍历input_handler_list,取出每一个handler进行input_attach_handler操作

list_for_each_entry(handler,&input_handler_list, node)

                   input_attach_handler(dev,handler);

----- >input_attach_handler(structinput_dev *dev, struct input_handler *handler)

     ----- > input_match_device(handler,dev);

          handler->connect(handler, dev,id);

input_dev与handler匹配,匹配成功之后connect,查看input_match_device源码,可以发现匹配的关键是input_dev->id,

这里暂时不分析connect操作,这一步在handler注册的时候再进行分析。到此完成了input_dev的注册

                                                handler的注册过程

1、input_handler结构

struct input_handler {

void *private;

void (*event)(structinput_handle *handle, unsigned int type, unsigned int code, int value);                  //事件处理程序

void (*events)(structinput_handle *handle,

                  const struct input_value *vals, unsignedint count);//事件序列处理程序

bool (*filter)(structinput_handle *handle, unsigned int type, unsigned int code, int value); //事件处理适配器

bool (*match)(structinput_handler *handler, struct input_dev *dev);// id比较

int (*connect)(structinput_handler *handler, struct input_dev *dev, const struct input_device_id*id);     //上面用到的connect程序

void (*disconnect)(structinput_handle *handle);// disconnect处理程序

void (*start)(structinput_handle *handle);             //启动处理程序

int minor;                                                                           //input设备结点的次设备号

const char *name;                                                         

const struct input_device_id*id_table;                    //id_table

struct list_head        h_list;

struct list_head        node;

};

2、一个具体的handler注册过程(这里以input自带的evdev为例子,分析一下一个handler是如何注册的。源码位置:kernel/input/evdev.c)

staticint __init evdev_init(void){

                  returninput_register_handler(&evdev_handler);

}

int input_register_handler(struct input_handler *handler){

     list_add_tail(&handler->node,&input_handler_list);      //把handler假如list

     list_for_each_entry(dev,&input_dev_list, node)              //与input_dev匹配(这里在input_dev注册的最后出现过的)

              input_attach_handler(dev,handler);

}

现在,来分析一下connect操作(上面未分析的),这里显示了handle把handler和input联系起来

static struct input_handler evdev_handler = {
    .event        = evdev_event,
    .events        = evdev_events,
    .connect    = evdev_connect,                            //这个操作
    .disconnect    = evdev_disconnect,
    .legacy_minors    = true,
    .minor        = EVDEV_MINOR_BASE,
    .name        = "evdev",
    .id_table    = evdev_ids,
};

static void evdev_disconnect(struct input_handle *handle){

       struct evdev *evdev;

       dev_set_name(&evdev->dev, "event%d", dev_no);     //设备的name   

       evdev->handle.dev = input_get_device(dev);              //已经匹配成功后,得到handle->dev(在handle定义 struct input_dev *dev;)

       evdev->handle.name = dev_name(&evdev->dev);

       evdev->handle.handler = handler;                                //handle->handler

       device_initialize(&evdev->dev);

        error =input_register_handle(&evdev->handle);     //注册handle(把handle加到一个list里面)

        error =device_add(&evdev->dev);                              //创建设备(上层访问的设备),它的主设备号在kernel/drivers/input/input.c中定义,并实现访问方法

}

到此,完成了input子系统分析。

        至于input如何把数据上报到userspace的,举个例子: 在触摸屏驱动中,通过i2c得到触摸坐标或按键,通过 input_report_abs、input_report_key便可以把数据传递到上层。具体的过程可以通过芯片厂的源码来分析。


input_dev事件支持类型

types对应于一个相同逻辑输入结构的一组Codes。每个type都有一组可用的codes用于产生输入事件。每个type可用的codes的详细信息请参考Codes一节的内容。

EV_SYN: 用于事件间的分割标志。事件可能按时间或空间进行分割,就像在多点触摸协议中的例子。

EV_KEY: 用来描述键盘,按键或者类似键盘设备的状态变化。

EV_REL:用来描述相对坐标轴上数值的变化,例如:鼠标向左方移动了5个单位。

EV_ABS:用来描述相对坐标轴上数值的变化,例如:描述触摸屏上坐标的值。

EV_MSC:当不能匹配现有的类型时,使用该类型进行描述。

EV_SW: 用来描述具备两种状态的输入开关。

EV_LED:用于控制设备上的LED灯的开和关。

EV_SND: 用来给设备输出提示声音。

EV_REP:用于可以自动重复的设备(autorepeating)。

EV_FF:用来给输入设备发送强制回馈命令

EV_PWR: 特别用于电源开关的输入。.

EV_FF_STATUS:用于接收设备的强制反馈状态。


正常情况下,用户空间基于设备发出的数据(比如事件的types)来建立一个输入设备,当两个设备都发出相同的事件types时,这时设备特性就可以提供额外的识别信息。

INPUT_PROP_DIRECT + INPUT_PROP_POINTER:

INPUT_PROP_DIRECT特性表明设备的坐标直接和屏幕坐标向对应(无需琐碎的转换操作,像缩放,反转,旋转等)。非直接输入设备则需要一些必要的变换,比如触摸板上绝对到相对的变换。典型的直接输入设备有:触摸屏,手写板;非直接输入设备有:触摸板,鼠标。

INPUT_PROP_POINTER特性表明设备不是利用屏幕来获取输入信息,从而需要一个屏幕上的指针来跟踪用户的移动。典型的指针设备有:触控板,鼠标;非指针设备:触摸屏。

如果INPUT_PROP_DIRECT或者 INPUT_PROP_POINTER都没有设置,设备将会被认为是未定义,它的类型需要按传统的方式利用事件的types推导出来。

INPUT_PROP_BUTTONPAD:

有些触摸板,它的按键位于触摸板的底部,这样按下触摸板会产生一次按键消息,对于这种设备,应该设置该特性。

0 0
原创粉丝点击