input子系统框架、核心层、输入事件驱动层详解

来源:互联网 发布:英雄无敌2 for mac 编辑:程序博客网 时间:2024/05/16 00:30

一、input子系统架构总览
1、input子系统分为三层
(1)最上层:输入事件驱动层,evdev.c和mousedev.c和joydev.c属于这一层
(2)中间层:输入核心层,input.c属于这一层
(3)最下层:输入设备驱动层,drivers/input/xxx 文件夹下

2、input类设备驱动开发方法
(1)输入事件驱动层和输入核心层不需要动,只需要编写设备驱动层
(2)设备驱动层编写的接口和调用模式已定义好,驱动工程师的核心工作量是对具体输入设备硬件的操作和性能调优。
(3)input子系统不算复杂,学习时要注意“标准模式”四个字。

二、输入核心层源码input.c分析
1、核心模块注册input_init
(1)class_register
(2)input_proc_init
(3)register_chrdev

2、核心层给驱动提供的接口函数是:
(1)input_allocate_device
(2)input_set_capability
(3)input_register_device

3、核心层给输入事件驱动层提供的接口函数是
(1)input_register_handler
(2)input_register_handle

上面对应的主要结构体分别是

Input子系统的三个重要结构体:
input_dev 是硬件驱动层,代表一个input设备。
input_handler 是事件处理层,代表一个事件处理器。
input_handle 个人认为属于核心层,代表一个配对的input设备与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)];//LED事件的子事件      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)];      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;      struct list_head    h_list;//链表头链接该设备关联的input_handle      struct list_head    node;//链接到全局input_dev_list  };  
struct input_handler {      void *private;      /*处理事件函数接口*/      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);      bool (*match)(struct input_handler *handler, struct input_dev *dev);      /*建立handler和device的联系*/      int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);      void (*disconnect)(struct input_handle *handle);      void (*start)(struct input_handle *handle);      const struct file_operations *fops;      int minor;      const char *name;      const struct input_device_id *id_table; //该handler所支持的input dev id      struct list_head    h_list;//链表头,指向它所支持的设备链表      struct list_head    node;   //链接到全局input_handler_list  };  
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; //通过d_node把input_handler链接到input_dev上的h_list链表头      struct list_head    h_node;//通过h_node把input_dev链接到input_handler的h_list链表头  };  

在(1)int input_register_device(struct input_dev *dev)接口函数主要做的事情有:

这里写图片描述

设置一些能力

这里写图片描述

这里最为重要,把要注册的设备添加到链表里去(讲白了就是去注册这个input_dev结构体),以便人家在 input_register_handler的时候可以在input_dev_list里面去找,然后配对上,执行error = handler->connect(handler, dev, id)函数。但是在这里呢是,注册设备,所以是到input_handler_list里面去找,有没有一个handler是和它相配对,配对上了之后呢就会去执行error = handler->connect(handler, dev, id);最后去唤醒,不再阻塞。

注:handler和device的匹配
(1)input_attach_handler
input_match_device 匹配device和handler
handler->connect(handler, dev, id) 连接device和handler

(2)int input_register_handler(struct input_handler *handler)里面做的事情和上面是一样的,
**input_table[handler->minor >> 5] = handler;
真正往内核里面注册一个handler,最大支持8个。
static struct input_handler input_table[8];*

(3)int input_register_handle(struct input_handle *handle)

handle结构体里面有两个变量,
struct list_head d_node;
struct list_head h_node;
注册函数里面就是填充这两个变量,
list_add_rcu(&handle->d_node, &dev->h_list);
list_add_tail_rcu(&handle->h_node, &handler->h_list);
这样,将handle对象关联到dev和handler对象。关联的细节如下:
在驱动端利用dev对象采集数据,通过dev的h_list列表找到对应的handle对象,再通过handle对象找到handler对象,这个过程就是从驱动端到处理端的全过程。

将handle挂到所对应input device的h_list链表上.还将handle挂到对应的handler的hlist链表上.如果handler定
义了start函数,将调用之. 到这里,我们已经看到了input device, handler和handle是怎么关联起来的了

三、输入事件驱动层evdev.c
1、static int __init evdev_init(void)
直接注册了一个handler,input_register_handler(&evdev_handler);

**

**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,};**

这里重点是evdev_event,evdev_connect;

对应用层封装的结构体是struct input_event {    struct timeval time;    __u16 type;          //类型    __u16 code;          //什么样的一个事件、按键事件呢还是触摸屏事件再或者鼠标移动事件    __s32 value;         //这个值是多少};

注:驱动层向应用层汇报事件调用的接口函数是 input_report_key(),位于核心层input.c

input_report_key()函数向输入子系统报告发生的事件,该函数的代码如下: static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}

该函数的第 1 个参数是产生事件的输入设备, 2 个参数是产生的事件, 3 个参数是事件的值。需要注意的是, 第2 个参数可以取类似 BTN_0、 BTN_1、BTN_LEFT、BTN_RIGHT 等值,这些键值被定义在 include/linux/input.h 文件中。
当第 2 个参数为按键时,第 3 个参数表示按键的状态,value 值为 0 表示按键释放,非 0 表示按键按下。

0 0
原创粉丝点击