Chinaunix首页 | 论坛 | 博客
  • 博客访问: 58861
  • 博文数量: 8
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 155
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-07 15:22
文章分类
文章存档

2014年(3)

2013年(5)

我的朋友

分类: LINUX

2014-02-27 18:41:36

INPUT sub_system

Input子系统是linux内核中使用比较频繁的一个模块,因为信息的输入在交互式产品中占据了一个非常重要的地位,在各种各样的产品中如手机的按键和触摸屏,电脑的鼠标键盘等。对于各种类型的不同的输入方式的输入设备,linux设计了一整套的输入事件类型去支持。

Input子系统主要同三个部分组成,input_dev,input_handler,input_handle

下面首先来看一下在Linux中是如何定义这几个数据结构的。

Input_dev,顾名思义,就是代表一个输入设备,这也出现在输入设备驱动程序当中频率最高的结构之一,它一般作为一个具体设备的一个成员,表示这个设备是一个输入设备,比如在一个触摸屏驱动当中会定义一个触摸屏的结构:

struct goodix_ts_data {

    spinlock_t irq_lock;

    s32 irq_is_disable;

       int retry;

       int panel_type;

       uint8_t bad_data;

       char phys[32];           

       struct i2c_client *client;

       struct input_dev *input_dev;//定义输入设备

       uint8_t use_irq;

       uint8_t use_shutdown;

              ……

};

       从结构中就可知这是一个输入设备。

struct input_dev {

       const char *name;//设备名

       const char *phys;

       const char *uniq;

       struct input_id id;//设备的input_idic的总线类型,厂商,设备等信息

 

       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)];//设备支持的内核定义的具体的KEY/BUTTON事件

       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;

 

       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)];//反应当前KEY/BUTTON状态

       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;//关联该设备的input_handle

 

       spinlock_t event_lock;//事件锁

       struct mutex mutex;

 

       unsigned int users;//打开该设备的用户数量

       bool going_away;

 

       bool sync;

 

       struct device dev;//input_dev的设备节点

 

       struct list_head   h_list;//与该设备关联的input_handle的链表

       struct list_head   node;//该节点关联到input_dev_list

};

 

Input_handle:用于链接input_devinput_handlerinput_handle的成员不多,从成员的定义也可以看出,input_handle存在的意义在于为input_devinput_handler搭建一条康庄大道,方便input_devinput_handler勾搭。

struct input_handle {

       void *private;//结构的私有数据域

       int open;//使用设备的数量

       const char *name;//

 

       struct input_dev *dev;//指向所属的input_dev

       struct input_handler *handler;//指向所属的input_handler

 

       struct list_head   d_node;//链接所属的input_devinput_handle

       struct list_head   h_node;//链接所属的input_handlerinput_handle

};

 

       Input_handler,与input_handle长的很像,但它们的作用与内容差异就很大了,而它的作用也明确,就是处理输入设备产生数据。

struct input_handler {

 

       void *private;//存储私有数据

 

       void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//处理支持的input_dev提供的事件

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

       bool (*match)(struct input_handler *handler, struct input_dev *dev);//用于判断input_handler是否支持处理某input_dev

       int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);//建立input_devinput_handler之间的连接关系

       void (*disconnect)(struct input_handle *handle);//断开input_devinput_handler之间的连接关系

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

 

       const struct file_operations *fops;//设备文件支持的操作集

       int minor;//支持的32个设备的从设备号起始量

       const char *name;//input_handler名,可从/proc/bus/input/handlers中查看

 

       const struct input_device_id *id_table;//input_handler支持的input_devid集合,在match中用来判断是否支持某input_dev

 

       struct list_head   h_list;//用于链接input_handler关联的input_handle

       struct list_head   node;//input_handler加入input_handler_list

};

 

       从数据流程的角度来看,input_dev它是一个数据的生产者,它代表着一个具体的硬件设备,并且从硬件设备中取出数据来,然后把这些数据送出来。input_handler是一个数据的消耗者,它负责把input_dev提供的数据处理掉,比如将数据从系统空间运送到用户空间,然后用户空间再有专门的线程来取这些数据,再一个个分发给各个应用进程。Input_handle呢?它不生产也不消耗数据,而在一个系统当中不仅仅存在一个input_dev,也可能不仅仅存在一个input_handler,那么指定哪些input_dev生产出来的数据由哪个input_handler去处理呢,input_handle干的就是这个勾当。由此可以看出,这三者各司其职,相互配合,干的不亦乐乎。下面用一副图来粗略的表现下它们三个之间的关系:





       从上图可以看出,它们之间的关系并不复杂,但是只要知道了其中一个,就可以找到另外的两个。

       下面从具体的代码来分析input子系统的使用,以一个触摸屏驱动为例。

       当系统启动检测到一个触摸屏的存在的时候就会去初始化它,在触摸屏驱动的初始化代码当中会分配并且初始化好一个输入设备:

       ts->input_dev = input_allocate_device();//input_dev分配地址空间并初始化其总线类型,结构链表等。

       if (ts->input_dev == NULL)

       {

              ret = -ENOMEM;

              dev_dbg(&client->dev,"Failed to allocate input device\n");

              goto err_input_dev_alloc_failed;

       }

 

       ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;//设置这个输入设置支持的事件类型,有按键和绝对坐标事件类型。

       ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);//支持的是一件触摸类型的按键

       input_set_capability(ts->input_dev,EV_KEY,KEY_BACK);

       input_set_capability(ts->input_dev,EV_KEY,KEY_MENU);//设置支持按键的键值,有返回键和菜单键

#endif

       ts->input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);//支持XY,按下/弹起状态的绝对坐标类型事件

       input_set_abs_params(ts->input_dev, ABS_Y, 0, SCREEN_MAX_HEIGHT, 0, 0);

       input_set_abs_params(ts->input_dev, ABS_X, 0, SCREEN_MAX_WIDTH, 0, 0);

       input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0);

input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 5, 0, 0);//分别定义了绝对坐标,按下/弹起状态,轨迹id事件的数据域。

sprintf(ts->phys, "input/goodix-ts");

      ts->input_dev->name = f3x_ts_name;

      ts->input_dev->phys = ts->phys;

      ts->input_dev->id.bustype = BUS_I2C;

      ts->input_dev->id.vendor = 0xDEAD;

      ts->input_dev->id.product = 0xBEEF;

      ts->input_dev->id.version = 10427;      

//初始化input_devnamephys,总线类型,以及相关设备的硬件ID

      ret = input_register_device(ts->input_dev);//向系统注册一个input_dev

      if (ret) {

             dev_err(&client->dev,"Unable to register %s input device\n", ts->input_dev->name);

             goto err_input_register_device_failed;

      }

      初始化好了之后就向系统注册设备,注册了设备之后就等于告诉系统,我现在可以开始干活了。

       来看一下注册input_dev的流程是什么样的吧:

       int input_register_device(struct input_dev *dev)

{

       ……

       list_add_tail(&dev->node, &input_dev_list);//input_dev挂载在input_dev_list上,input_dev_list保存了所有的input_dev

 

       list_for_each_entry(handler, &input_handler_list, node)

             input_attach_handler(dev, handler);// 遍历input_handler_list链表,通过挂载在链表上的node结构找到对应input_handler的首地址,通过结构的某成员获取该结构的首地址是内核当中使用非常频率而又非常有效率的一种用法。然后为input_devinput_handler建立链接。

       因此我们来看一下input_attach_handler这个接口:

       static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)

{

       const struct input_device_id *id;

       int error;

 

       id = input_match_device(handler, dev);//查看该input_handler是否支持处理该input_dev

       if (!id)

              return -ENODEV;

 

       error = handler->connect(handler, dev, id);//如果支持,则为input_handlerinput_dev建立链接。

       if (error && error != -ENODEV)

              pr_err("failed to attach handler %s to device %s, error: %d\n",

                     handler->name, kobject_name(&dev->dev.kobj), error);

 

       return error;

}

       这里我们首先要了解一下,linux内核为我们实现了一个支持所有类型的输入设备的input_handler—>evdev,它的具体实现在driver/input/evdev.c文件当中。如果没有特殊的要求,一般情况下我们就使用这个evdev作为我们输入设备的input_handler

       static const struct input_device_id *input_match_device(struct input_handler *handler,

                                                 struct input_dev *dev)

{

       const struct input_device_id *id;

       int i;

 

       for (id = handler->id_table; id->flags || id->driver_info; id++) {

 

              if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)

                     if (id->bustype != dev->id.bustype)

                            continue;

 

              if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)

                     if (id->vendor != dev->id.vendor)

                            continue;

 

              if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)

                     if (id->product != dev->id.product)

                            continue;

 

              if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)

                     if (id->version != dev->id.version)

                            continue;

 

              MATCH_BIT(evbit,  EV_MAX);

              MATCH_BIT(keybit, KEY_MAX);

              MATCH_BIT(relbit, REL_MAX);

              MATCH_BIT(absbit, ABS_MAX);

              MATCH_BIT(mscbit, MSC_MAX);

              MATCH_BIT(ledbit, LED_MAX);

              MATCH_BIT(sndbit, SND_MAX);

              MATCH_BIT(ffbit,  FF_MAX);

              MATCH_BIT(swbit,  SW_MAX);

 

              if (!handler->match || handler->match(handler, dev))

                     return id;

       }

 

       return NULL;

}

       由以上代码可知,linuxinput子系统通过获取该input_handlerid_table表示其支持的input_dev,该id_table中定义了其支持的input_dev的总线类型bustype,设备厂商vendor,设备制造商product,设备版本号version。通过id_table设置的flags项来设置是判断是否支持的标准是总线类型或者设备vendor/ product/ version,在上面的input_dev的初始化当中有对input_devid的这几项进行初始化。而在evdev.c中有对该input_handler id_table的定义:

       static const struct input_device_id evdev_ids[] = {

       { .driver_info = 1 },     /* Matches all devices */

       { },                 /* Terminating zero entry */

};

       flags0,而又没有实现match接口,因此所有和它matchinput_dev都会成功,match成功之后就会调用input_handlerconnect接口:

       static int evdev_connect(struct input_handler *handler, struct input_dev *dev,

                      const struct input_device_id *id)

{

       ……

       evdev->handle.dev = input_get_device(dev);

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

       evdev->handle.handler = handler;

       evdev->handle.private = evdev;//我们关注的重点是这几行代码,在connect过程中会创建并初始化一个evdev类型的设备,然后在这里初始化evdevhandle,将传递进来的input_devinput_handler保存在这个input_handle上,因此从此以后就可以由handle找到其链接的input_devinput_handler了。

       ……

       error = input_register_handle(&evdev->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);

 

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

       ……

       input_register_handle当中,最主要的是上面几行代码,它分别通过handled_nodeh_node节点将handle保存在devhandlerh_list上面。由此,我们可以通过input_dev或者input_handlerh_list上挂载的d_node节点找到包含其的input_handle结构,进而找到其对应的input_handler或者input_dev

*/

       if (error)

              goto err_free_evdev;

       ……

}

       从这里以后,input_devinput_handlerinput_handle之间就相互连接起来了。

阅读(2998) | 评论(0) | 转发(0) |
0

上一篇:SPI系统分析

下一篇:Input子系统(二)

给主人留下些什么吧!~~