现在的位置: 首页 > 综合 > 正文

input 子系统浅析

2013年10月10日 ⁄ 综合 ⁄ 共 14332字 ⁄ 字号 评论关闭

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)];

         unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];

         unsigned long relbit[BITS_TO_LONGS(REL_CNT)];

         unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];

         unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];

         unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];

         unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];

         unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];

         unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

 

         unsigned int hint_events_per_packet;

//这个函数接口是用于计算每个数据包的大小。好准备好大小合适的数据包buffer.

 

         unsigned int keycodemax;

         unsigned int keycodesize;

         void *keycode;

 

         int (*setkeycode)(struct input_dev *dev,

                              const struct input_keymap_entry *ke,

                              unsigned int *old_keycode);

         int (*getkeycode)(struct input_dev *dev,

                              struct input_keymap_entry *ke);

 

         struct ff_device *ff;

 

         unsigned int repeat_key;

         struct timer_list timer;

 

         int rep[REP_CNT];

 

         struct input_mt_slot *mt;

         int mtsize;

         int slot;

         int trkid;

 

         struct input_absinfo *absinfo;

 

         unsigned long key[BITS_TO_LONGS(KEY_CNT)];

         unsigned long led[BITS_TO_LONGS(LED_CNT)];

         unsigned long snd[BITS_TO_LONGS(SND_CNT)];

         unsigned long sw[BITS_TO_LONGS(SW_CNT)];

 

         int (*open)(struct input_dev *dev);

         void (*close)(struct input_dev *dev);

         int (*flush)(struct input_dev *dev, struct file *file);

         int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

 

         struct input_handle __rcu *grab;

 

         spinlock_t event_lock;

         struct mutex mutex;

 

         unsigned int users;

         bool going_away;

 

         bool sync;

 

         struct device dev;

 

         struct list_head        h_list;

         struct list_head        node;

};

 

 

struct input_handler {

    //存放的是此handler的私有变量

         void *private;

//进行时间传递的函数接口,此时中断disable,获取dev->event_lock spinlock锁,因

此此时不进入睡眠

         void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

    //对某些时间进行特殊处理

         bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

    //devicehandler的匹配函数

         bool (*match)(struct input_handler *handler, struct input_dev *dev);

    //devicehandler匹配成功后的connect函数,在此产生对应的次设备号和注册handle

         int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);

//取消devicehandler的链接

         void (*disconnect)(struct input_handle *handle);

    //通过给定的handle来启用对用的handler

         void (*start)(struct input_handle *handle);

 

         const struct file_operations *fops;//handler的文件操作接口

         int minor;//handler的次设备号基地址

    //handler的名字,通常显示在/proc/bus/input/handlers路径下

         const char *name;

//指向此handler能够处理的属性

/*通常属性包含以下几种:

struct input_device_id {

 

        
kernel_ulong_t flags;

 

        
__u16 bustype;

        
__u16 vendor;

        
__u16 product;

        
__u16 version;

 

        
kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];

        
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

 

        
kernel_ulong_t driver_info;

};

*/

         const struct input_device_id *id_table;

    //这是handler自己的handle链表管理

    struct list_head        h_list;

    //代表此handler挂在在input_handler_list链表上面

         struct list_head        node;

};

 

struct input_handle {

    //handle的私有数据

         void *private;

    //标识此handle是否被打开

         int open;

    //handle的名字

         const char *name;

    //handle相关连的device

         struct input_dev *dev;

    //handle相关连的handler

         struct input_handler *handler;

         //将此handle挂载到device对应的管理handle的链表中

struct list_head        d_node;

//将此handle挂载到handler对应的管理handle的链表中

         struct list_head        h_node;

};

Input子系统解析

属性文件和设备文件的创建。

相关的属性文件

首先类文件夹创建:

路劲:/sys/class/input/里面有所有的设备文件和所有的handle文件。

设备文件:就是调用input_register_device()函数注册的设备

这样的文件一般命名方式为:"input%ld"

最终调用device_add函数注册,并生成一些相关的属性文件

Handle文件:就是device与相对应的handler相匹配后,调用handlerconnect函数,创建的文件

这样的文件一般命名方式为:以eventhandler为例:"event%d"对应的handler名称加ID

最终调用device_add函数注册,并生成一些相关的属性文件

NOTE:在创建一些对应的属性文件的同时,也会创建一系列的软link和硬link

 

调用input_proc_init()函数在proc文件系统中生成一些必备的文件

路劲:/proc/bus/input/

下面有deviceshandlers两个文件

Devices文件记录所有的input设备

Handlers记录input中所有的handler

 

 

Input子系统与hander:

设备文件:分主设备号和此设备号:

路劲:

/sys/dev/char  主设备号为13

次设备号对应各个handerl的设备

一共有256个次设备号,设计上每个handler占用32个次设备号。故最多有8handler,

1.EventHub.h中的

// Epoll FD list size hint.

static const int EPOLL_SIZE_HINT = 8;

也可以推得此信息。

2.input_register_handler()函数中input_table[handler->minor >> 5] = handler;

也可以推得此信息

3.static struct input_handler *input_table[8];数组也可以推得此信息

常见的几种handler:

Joydev

#define JOYDEV_MINOR_BASE       0

#define JOYDEV_MINORS                 16

Evdev

#define EVDEV_MINOR_BASE         64

#define EVDEV_MINORS                   32

Mousedev

#define MOUSEDEV_MINOR_BASE         32

#define MOUSEDEV_MINORS                   32

一般内核中形形色色的handler有很多,但是上层看到到一般最多只有八种,那么那些特征的handler上层可见呢?

Input子系统管理上层可见的handler是通过一个8 handler容量的数组

static struct input_handler *input_table[8];此数组是全局变量

此时我们看看hander的注册函数,什么条件的handler
才会存进此数组中。

int input_register_handler(struct input_handler *handler)

{

if (handler->fops != NULL) {

                   if (input_table[handler->minor >> 5]) {

                            retval = -EBUSY;

                            goto out;

                   }

                   input_table[handler->minor >> 5] = handler;

         }

}

由此可知:只有满足带有文件操作接口的hander才会存进此全局数组中。

其他情况的handler只在内核里面进行链表似的管理。

Handerdevices:

在注册设备的时候会从链表中与handler进行匹配,匹配成功后会调用对应handlerconnect函数。在此函数中产生对应段的次设备号。和注册一个关联deviceshandlerhandle.

 

通常情况下:每一个handler也有一个全局的数组用来存放此handler关联成功的devices

通常都是调用xxxdev_install_chrdev()函数

Evdevstatic struct evdev *evdev_table[EVDEV_MINORS];

Mousedevstatic struct mousedev *mousedev_table[MOUSEDEV_MINORS];

Joydevstatic struct joydev *joydev_table[JOYDEV_MINORS];

 

Handler特性:

每一个handler都对应着多个client。也就是说上层可以有多个实例同时关注此handler,此handler对应的设备有时间产生的时候,会通报相关的所有client.

Client通常都是通过链表来管理的,机制是RCU

Client都是在对应handleropen函数中调用对应的XXXdev_attach_client函数进行注册。

 

Handler结构体通常需要填充的属性:

eventhandler为例:

static struct input_handler evdev_handler = {

         .event                = evdev_event,

         .connect  = evdev_connect,

         .disconnect      = evdev_disconnect,

         .fops                   = &evdev_fops,

         .minor                = EVDEV_MINOR_BASE,

         .name                = "evdev",

         .id_table  = evdev_ids,

};

 

 

几种术语:

Device:input设备

Handler:处理设备时间的handler

Handle:关联devicehandler

下面从几个方面来了解input子系统。

注册流程

注销流程

消息上报流程

Handlerdevice匹配流程

生成handle流程

Device

device注册流程

/*

**1.device必须是函数input_allocate_device()申请的结构体

**2.devicecapabilities必须在注册之前就设置好

**3.如果注册device失败必须调用函数input_free_device()释放之前申请的device

**  的空间

**4.如果注册成功,需要注销的时候必须调用函数input_unregister_device()

**5.

**

**

*/

int input_register_device(struct input_dev *dev)

{

//首先是填充input_dev结构体的相关属性

//所有的device都得支持EV_SYN事件

__set_bit(EV_SYN, dev->evbit);

//所有的device都不支持KEY_RESERVED事件

__clear_bit(KEY_RESERVED, dev->keybit);

//将不支持的事件占有位都清零

input_cleanse_bitmasks(dev);

//计算每个数据包的事件数目

if (!dev->hint_events_per_packet)

                   dev->hint_events_per_packet =

                                     input_estimate_events_per_packet(dev);

//此处是处理重复按键的

//如果在驱动中设置了延迟事件和周期,在inputcore中就不进行处理了,由驱动//自己控制

init_timer(&dev->timer);

         if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {

                   dev->timer.data = (long) dev;

                   dev->timer.function = input_repeat_key;//处理重复按键的函数,上报事件

                   dev->rep[REP_DELAY] = 250;

                   dev->rep[REP_PERIOD] = 33;

}

//填充设置和获取keycode的函数

if (!dev->getkeycode)

                   dev->getkeycode = input_default_getkeycode;

 

         if (!dev->setkeycode)

                   dev->setkeycode = input_default_setkeycode;

 

//该函数对原子类型的变量V原子的增加1并且返回指向V的指针

//deviceinput+number的方式进行命名

dev_set_name(&dev->dev, "input%ld",

                        (unsigned long)
atomic_inc_return(&input_no) - 1);

//将此device添加进经典模型中,在sys文件系统下面生成对应的文件

error = device_add(&dev->dev);

         if (error)

                   return error;

//将注册成功后产生的路劲获取打印出来

path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);

         pr_info("%s as %s\n",

                   dev->name ? dev->name : "Unspecified device",

                   path ? path : "N/A");

         kfree(path);

//加锁互斥变量,但是可被中断打断(这是我个人的了解,不一定正确)

error = mutex_lock_interruptible(&input_mutex);

         if (error) {

                   device_del(&dev->dev);

                   return error;

         }

//将此device添加进input-core全局的dev链表中

list_add_tail(&dev->node, &input_dev_list);

//遍历handler链表,调用input_attach_handler函数与handler进行匹配

list_for_each_entry(handler, &input_handler_list, node)

                   input_attach_handler(dev, handler);

 

//

//这个函数就是唤醒poll的等待队列,poll函数会返回return POLLIN | //POLLRDNORM;

//标识此设备有消息可读,这样就可以对此device进行读操作获取相关的信息了。

//此处就是一个poll模型的应用不多说,需要详细了解的关注下此模型

input_wakeup_procfs_readers();

//去锁互斥变量

mutex_unlock(&input_mutex);

 

Device注销流程

/*

**注销掉先前注册的设备

**

**

**

**

*/

void input_unregister_device(struct input_dev *dev)

{

//注销前的准备工作,详细信息见下面函数单独分析

input_disconnect_device(dev);

//互斥变量上锁

mutex_lock(&input_mutex);

//在注册的时候,会和handler匹配,如果匹配成功就会调用handlerconnect

//函数,在这里与前面相对应的是,注销的时候就会调用相应的disconnect//

list_for_each_entry_safe(handle, next, &dev->h_list, d_node)

handle->handler->disconnect(handle);

         WARN_ON(!list_empty(&dev->h_list));

//将设备的timer删除

del_timer_sync(&dev->timer);

//将设备从全局设备链表中删除

         list_del_init(&dev->node);

//此函数的功能已经在注册函数中描述清楚,这里就不再重复了

input_wakeup_procfs_readers();

//互斥变量解锁

mutex_unlock(&input_mutex);

            //之前的注册的时候有调用device_add()函数,这里就调用对应的device注销//函数,具体细节不描述,如果有兴趣,可详细研究设备驱动模型

         device_unregister(&dev->dev);

}

 

static void input_disconnect_device(struct input_dev *dev)

{

//添加此标识,标识设备已经离开

mutex_lock(&dev->mutex);

dev->going_away = true;

         mutex_unlock(&dev->mutex);

 

//加锁

spin_lock_irq(&dev->event_lock);

//在将设备移除之前模拟一个keyup消息,防止出现某些那件一直处于按下状//态,

 input_dev_release_keys(dev);

//遍历与device匹配成功的handler链表,将生成的handleopen属性置为0

//标识此handle没有被引用,引用计数为0device产生的事件不再传送到//handler中了,进行处理之后或许设备还有有事件产生,但是此时的事件已经//不传递到//handler中去了

list_for_each_entry(handle, &dev->h_list, d_node)

handle->open = 0;

//解锁

spin_unlock_irq(&dev->event_lock);

}

 二.Handler

     1.handler注册流程

     /*

     **1.注册一个新的handler,且与所有的devices进行适配

     */

     int input_register_handler(struct input_handler *handler)

     {

     //加锁互斥变量,但是可被中断打断(这是我个人的了解,不一定正确)

     retval = mutex_lock_interruptible(&input_mutex);

             if (retval)

                   return retval;

     //初始化handle链表,与此handler相关联的handle,都挂载在此链表上面

     INIT_LIST_HEAD(&handler->h_list);

     //前面已经提到过,handler分为带文件操作接口和不带文件操作接口,而且带文

//件操作接口的handler,最多只能有8个。

//input_table数组是全局的,大小为8,存放贷文件接口的handler.索引为次设备号//偏移5,这里与每个带文件操作接口handler,最多关联32个设备相对应,对应//32个次设备号

     if (handler->fops != NULL) {

                   if (input_table[handler->minor >> 5]) {

                            retval = -EBUSY;

                            goto out;

                   }

                   input_table[handler->minor >> 5] = handler;

         }

   //将此handler挂载进全局的handler管理链表中去

   list_add_tail(&handler->node, &input_handler_list);

   //遍历device全局链表,调用input_attach_handler进行匹配

   list_for_each_entry(dev, &input_dev_list, node)

                   input_attach_handler(dev, handler);

   //此函数在之前已经详细描述过,这里面不重复

         input_wakeup_procfs_readers();

     }

     2.handler注销流程

       /*

       **1.将之前注册的handler进行撤销

       */

void input_unregister_handler(struct input_handler *handler)

       {

        //互斥变量加锁

        mutex_lock(&input_mutex);

        //在注册的时候会和所有的device进行适配,如果适配成功就会调用对应//handlerconnect函数,这里面要调用对应的disconnect函数

        list_for_each_entry_safe(handle, next, &handler->h_list, h_node)

                      handler->disconnect(handle);

                WARN_ON(!list_empty(&handler->h_list));

        //将此handlerhandler的全局链表中删除

list_del_init(&handler->node);    

//前面注册的时候会填充input_table对应索引,此处应该将对应索引值赋为空

if (handler->fops != NULL)

                   input_table[handler->minor >> 5] = NULL;

//此函数不多解释,前面已经进行过详细的描述

input_wakeup_procfs_readers();

//互斥变量解锁

mutex_unlock(&input_mutex);

}

 三.Handle

    综述:handle的注册于注销都是在对应handlerconnectdisconnect函数中进行

         

 1.handle注册流程

     /*

     **注册一个handle进入对应handlerdevice链表中,用于关联二者

     **所以只要handleopen属性不为0,对应设备产生的事件就会通过它被传送到**上层去,此函数被handlerconnect函数调用

     */

      int input_register_handle(struct input_handle *handle)

      {

       //首先提取出与此handle相关联的handlerdevice

       struct input_handler *handler = handle->handler;

               struct input_dev *dev = handle->dev;

      //给互斥变量上锁(可被中断打断),主要为了防止input_release_device().在此期间//被调用

      error = mutex_lock_interruptible(&dev->mutex);

      //如果此handler带有filter功能则将handle加到设备链表头,否则加到设备链表//

      if (handler->filter)

                   list_add_rcu(&handle->d_node, &dev->h_list);

         else

                   list_add_tail_rcu(&handle->d_node, &dev->h_list);

      //互斥变量解锁

      mutex_unlock(&dev->mutex);

      //由于此函数是被connect函数调用,与disconnect函数式互斥的,不可能同时调//input_unregister_handle()函数,所以不需要独立的互斥变量

      //handle添加到handler链表的尾

      list_add_tail_rcu(&handle->h_node, &handler->h_list);

      //通过给定的handle,开始handler,(此处目前我也不怎么理解)

      //此函数通常被调用刚好在connect函数之后,也可以在一个进程grabbed
一个设备然后释放他的时候调用例如函数:__input_release_device

      if (handler->start)

                   handler->start(handle);

      }

 2.handle注销流程

  /*

 **注销函数主要就是将handle的节点从handlerdevice的链表中删除

 */

void input_unregister_handle(struct input_handle *handle)

{

//handlehandler的链表中删除

list_del_rcu(&handle->h_node);

         /*

          * Take dev->mutex to prevent race with input_release_device().

          */

         mutex_lock(&dev->mutex);

 //handledevice的链表中删除

         list_del_rcu(&handle->d_node);

         mutex_unlock(&dev->mutex);

 //同步RCURCU小解:

       //RCU机制是Linux2.6之后提供的一种数据一致性访问的机制,从RCU//(read-copy-update)的名称上看,我们就能对他的实现机制有一个大//概的了解,在修改数据的时候,首先需要读取数据,然后生成一个副本,//对副本进行修改,修改完成之后再将老数据update成新的数据,此所

//谓RCU

         synchronize_rcu();

}

Handlerconnectdisconnect函数

这里面evdev这个handler进行举例分析

五:消息上报流程

首先从驱动中的上报消息函数开始开流程

input_report_key()àinput_event()àinput_handle_event()àdev->event()

【设备要监听消息】

                                                  input_pass_event()

                                                 
handler监听消息】

下面主要看handler的消息流程:

input_pass_event()àhandler->event()会调用相应handlerevent函数

下面来显示看看这些函数流程

/*

**根据支持的事件传递事件类型,调用input-core的事件传递函数

*/

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)

{

    //事件类型是EV_KEY,,其他的类似

抱歉!评论已关闭.