输入子系统

Posted 专注it

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了输入子系统相关的知识,希望对你有一定的参考价值。

Linux的输入子系统不仅支持鼠标、键盘等常规的输入设备,而且还支持蜂鸣器、触摸屏等设备

输入子系统又叫input子系统。其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现给应用程序

 

#define EV_SYN            0x00               //表示设备支持所有事件
#define EV_KEY            0x01               //键盘或者按键,表示一个键码
#define EV_REL            0x02                //鼠标设备,表示一个相对的光标位置结果
#define EV_ABS            0x03               //手写板产生的值,其是一个绝对整数值
#define EV_MSC            0x04              //其他类型
#define EV_SW             0x05               
#define EV_LED            0x11               //LED设备
#define EV_SND            0x12              //蜂鸣器,输入声音
#define EV_REP            0x14              //允许重复按键类型
#define EV_FF            0x15
#define EV_PWR            0x16             //电源管理事件
#define EV_FF_STATUS        0x17
#define EV_MAX            0x1f
#define EV_CNT            (EV_MAX+1)

input_handler是输入设备的事件处理接口,为处理事件提供一个统一的函数模板,程序员应该根据具体的需要实现其中的一些函数,并将其注册到输入子系统中。该结构体的定义如下:

  1. 01  struct input_handler {  
  2. 02      void *private;  
  3. 03      void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);  
  4. 04      int (*connect)(struct input_handler *handler, struct input_dev *dev,   const struct input_device_id *id);  
  5. 05      void (*disconnect)(struct input_handle *handle);  
  6. 06      void (*start)(struct input_handle *handle);  
  7. 07      const struct file_operations *fops;  
  8. 08      int minor;  
  9. 09      const char *name;  
  10. 10      const struct input_device_id *id_table;  
  11. 11      const struct input_device_id *blacklist;  
  12. 12      struct list_head    h_list;  
  13. 13      struct list_head    node;  
  14. 14  }; 

对该结构体简要分析如下。

第02行,定义了一个private指针,表示驱动特定的数据。这里的驱动指的就是handler处理器。

第03行,定义了一个event()处理函数,这个函数将被输入子系统调用去处理发送给设备的事件。例如将发送一个事件命令LED灯点亮,实际控制硬件的点亮操作就可以放在event()函数中实现。

第04行,定义了一个connect()函数,该函数用来连接handler和input_dev。在input_attach_handler()函数的第10行,就是回调的这个自定义函数。

第05行,定义了一个disconnect()函数,这个函数用来断开handler和input_dev之间的联系。

第07行,表示handler实现的文件操作集,这里不是很重要。

第08行,表示设备的次设备号。

第09行,定义了一个name,表示handler的名字,显示在/proc/bus/input/handlers目录中。

第10行,定义了一个id_table表,表示驱动能够处理的表。

第11行,指向一个input_device_id表,这个表包含handler应该忽略的设备。

第12行,定义了一个链表h_list,表示与这个input_handler相联系的下一个handler。

第13行,定义了一个链表node,将其连接到全局的input_handler_list链表中,所有的input_handler都连接在其上。

 

struct input_dev {
 const char *name;//设备名
 const char *phys;
 const char *uniq;
 struct input_id id;//用于匹配事件处理层handler
 
 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)];//beep
 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
 unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
 
 unsigned int keycodemax;//支持的按键值的个数
 unsigned int keycodesize;//每个键值的字节数
 void *keycode;//存储按键值的数组首地址
 int (*setkeycode)(struct input_dev *dev,
     unsigned int scancode, unsigned int keycode);//修改键值的函数,可选
 int (*getkeycode)(struct input_dev *dev,
     unsigned int scancode, unsigned int *keycode);//获取扫描码的键值,可选
 
 struct ff_device *ff;
 
 unsigned int repeat_key;//最近一次按键值,用于连击
 struct timer_list timer;//自动连击计时器
 
 int sync;//最后一次同步后没有新的事件置1
 
 int abs[ABS_CNT];//当前各个坐标的值
 int rep[REP_MAX + 1];//自动连击的参数
 
 unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反映当前按键状态的位图
 unsigned long led[BITS_TO_LONGS(LED_CNT)];//反映当前led状态的位图
 unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反映当前beep状态的位图
 unsigned long sw[BITS_TO_LONGS(SW_CNT)];
 
   /*tp驱动代码里一般使用input_set_abs_params函数设置
     函数参数从右往左依次代表输入设备指针、坐标轴、最小值、最大值、分辨率、基准值。
      最后两个参数也可以填为0,代表设备非常精确并且总能精确的回到中心位置。*/
 int absmax[ABS_CNT];//记录各个坐标的最大值
 int absmin[ABS_CNT];//记录各个坐标的最小值
 int absfuzz[ABS_CNT];//记录各个坐标的分辨率
 int absflat[ABS_CNT];//记录各个坐标的基准值
 int absres[ABS_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 *grab;
 
 spinlock_t event_lock;
 struct mutex mutex;
 
 unsigned int users;
 bool going_away;
 
 struct device dev;
 
 struct list_head h_list;//handle链表
 struct list_head node;//input_dev链表
};

struct input_event是事件传送的载体,输入子系统的事件都是包装成struct input_event传给用户空间。各个成员如下所示:

/* include/linux/input.h */
struct input_event {
 struct timeval time;//时间戳
 __u16 type;//事件类型
 __u16 code;//事件代码
 __s32 value;//事件值,如坐标的偏移值
};
struct input_dev注册的时候需要跟匹配的hanlder建立连接,匹配的依据就是struct input_dev所包含的struct input_id。

/* include/linux/input.h */
struct input_id {
 __u16 bustype;//总线类型
 __u16 vendor;//生产商编号
 __u16 product;//产品编号
 __u16 version;//版本号
};
input_handler这个结构体是事件驱动的主体,每一种处理方式对应一个handler结构体。注册input_handler,其实就是将 input_hangler加入到input_handler_list当中。使用input_register_handler注册。

/* include/linux/input.h */
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和input_dev的函数指针
 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    //断开连接函数指针
 void (*disconnect)(struct input_handle *handle);
    //为给定的handle启动handler函数指针
 void (*start)(struct input_handle *handle);
 
    //文件操作结构体
 const struct file_operations *fops;
    //这个handler可以使用的32个次设备号的最小值
 int minor;
    //此handler的名字
 const char *name;
 
    //可以处理的input_device_ids列表
 const struct input_device_id *id_table;
    //需要被忽略的input_device_ids列表
        const struct input_device_id *blacklist;
 
    //用来连接handle的链表链表节点。每个与此handler相关的handle都放入此链表
 struct list_head h_list;
    //用来放入全局handler链表的节点
 struct list_head node;
};
input_handle这个结构体用来连接input_dev和input_handler。

/* include/linux/input.h */
struct input_handle {
 
 void *private;//私有数据指针
 
 int open;//记录本设备被打开的次数
 const char *name;//创建此handle的handler所赋予的名字
 
 struct input_dev *dev;//指向附着的input_dev
 struct input_handler *handler;//指向创建此handle的handler
 
 struct list_head d_node;//链表节点,用来加入附着的input_dev
 struct list_head h_node;//链表节点,用来加入附着的input_handler
};
input_dev和input_handler匹配过程中用到了input_device_id

/* include/linux/mod_devicetable.h */
struct input_device_id {
 
 kernel_ulong_t flags;//定义需要匹配input_id的哪些域
 
 __u16 bustype;//对应input_id的四个数据域
 __u16 vendor;
 __u16 product;
 __u16 version;
        //存储支持事件的位图,与input_dev中的同名数据成员功能一致
 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;//指示结构体中是否含有驱动信息
};

 


先看一个简单的输入设备驱动程序的例子:

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
 
#include <asm/irq.h>
#include <asm/io.h>
 
static struct input_dev  *button_dev;        //输入设备结构体
 
static irqreturn_t  button_interrupt(int irq, void *dummy, struct pt_regs *fp)             //中断处理函数
{
        input_report_key(&button_dev, BTN_0, inb(BUTTON_PORT) & 1);        //向输入子系统报告产生的按键事件
        input_sync(&button_dev);                                                                         //通知接收者,一个报告发送完毕

        return IRQ_HANDLED;

}
 
static int __init button_init(void)
{

      int error; 

      if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {                         //申请中断处理函数
                printk(KERN_ERR "button.c: Can\'t allocate irq %d\\n", button_irq);                    //申请失败
                return -EBUSY;
        }
        button_dev = input_allocate_device();                                           //分配一个设备结构体
        if(!button_dev)                                                                               //

        {

                printk(KERN_ERR"button.c:Not enougu memory\\n");

                error = -ENOMEM;

                goto  err_free_irq;

         }

        button_dev.evbit[0] = BIT(EV_KEY);                                            //设置按键信息
        button_dev.keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
 
        error = input_register_device(&button_dev);             //注册一个输入设备

   if(error)

   {

      printk("KERN_ERR"button.c: Failed to register device\\n");  

      goto err_free_dev;

   }

   return 0;

err_free_dev:                                                           //下面是错误处理

   input_free_device(button_dev);

err_free_irq:

   free_irq(BUTTON_IRQ,button_interrupt);

  return error;

}
 
static void __exit button_exit(void)                                         //卸载函数
{
        input_unregister_device(&button_dev);                             //注销按键设备
        free_irq(BUTTON_IRQ, button_interrupt);                          //释放按键占用的中断线
}
 
module_init(button_init);
module_exit(button_exit);

 

input_allocate_device()函数在内存中为输入设备结构体分配一个空间,并对其主要成员进行了初始化。

struct input_dev *input_allocate_device(void)
{
 struct input_dev *dev;

 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);              //分配一个input_dev结构体,并且初始化为0

 if (dev) {
    dev->dev.type = &input_dev_type;                           //初始化设备的类型
    dev->dev.class = &input_class;                                //设置为输入设备类
    device_initialize(&dev->dev);                                    //初始化device结构
    mutex_init(&dev->mutex);                                      //初始化互斥锁
    spin_lock_init(&dev->event_lock);                            //初始化事件自旋锁
    INIT_LIST_HEAD(&dev->h_list);                              //初始化链表
    INIT_LIST_HEAD(&dev->node);                               //初始化链表

    __module_get(THIS_MODULE);                               //增加模块引用计数
 }

 return dev;
}

该函数返回一个指向input_dev类型的指针,该结构体是一个输入设备结构体,包含输入设备的一些相关信息,如设备支持的按键码、设备的名字、设备支持的事件等

 

input_register_device()函数注册输入子系统设备结构体 调用失败用input_allocate_deivce()函数释放input_allocate_device()分配的空间。卸载用input_unregister_device()

int input_register_device(struct input_dev *dev)
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_handler *handler;
    const char *path;
    int error;

    __set_bit(EV_SYN, dev->evbit);                       //设置input_dev所支持的事件,这里表示支持所有事件,一个设备可以支持一种或多种时间类型


    /*
     * If delay and period are pre-set by the driver, then autorepeating
     * is handled by the driver itself and we don\'t do it in input.c.
     */

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

    if (!dev->getkeycode)                     //没定义使用默认的
        dev->getkeycode = input_default_getkeycode;

    if (!dev->setkeycode)                      //没定义使用默认的
        dev->setkeycode = input_default_setkeycode;

    dev_set_name(&dev->dev, "input%ld",
             (unsigned long) atomic_inc_return(&input_no) - 1);                     //设置input_dev中的device的名字,出现在sysfs系统中

    error = device_add(&dev->dev);                                         //将input_dev包含的device结构注册到Linux设备模型中,并可以以sysfs文件系统表现出来
    if (error)
        return error;

    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);                     
    printk(KERN_INFO "input: %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;
    }

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

    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    mutex_unlock(&input_mutex);

    return 0;
}

 

input_attach_handler()函数用于匹配input_dev和handler,只有匹配成功,才能进行下一步的关联操作

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;                       //输入设备指针
    int error;

    if (handler->blacklist && input_match_device(handler->blacklist, dev))                                         //设备和处理函数之间的匹配
        return -ENODEV;

    id = input_match_device(handler->id_table, dev);
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id);
    if (error && error != -ENODEV)
        printk(KERN_ERR
            "input: failed to attach handler %s to device %s, "
            "error: %d\\n",
            handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}

 

 

input_match_device()函数用来与input_dev和handler进行匹配。handler的id_table表中定义了其支持的input_dev设备:

static const struct input_device_id *input_match_device(const struct input_device_id *id,
                            struct input_dev *dev)
{
    int i;

    for (; 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);

        return id;
    }

    return NULL;
}

 


#define MATCH_BIT(bit, max) \\
        for (i = 0; i < BITS_TO_LONGS(max); i++) \\
            if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \\
                break; \\
        if (i != BITS_TO_LONGS(max)) \\
            continue;

只有当input_device和input_handler的ID成员在evbit,keybit,...,swbit项相同才会匹配成功。而且匹配顺序是从evbit,keybit,到swbit。只要有一项不同就会循环到ID下一项比较

 

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);
}
第二个参数是产生的事件,可以去BTN_0,BTN_1,BTN_LEFT,BTN_RIGHT等值

 

input_event() 向输入子系统报告输入设备产生的事件:

void input_event(struct input_dev *dev,
         unsigned int type, unsigned int code, int value)           //type是事件的类型,EV_KEY,EV_REL等等
{
    unsigned long flags;

    if (is_event_supported(type, dev->evbit, EV_MAX)) {

        spin_lock_irqsave(&dev->event_lock, flags);
        add_input_randomness(type, code, value);            //对随机数熵池增加一些贡献,因为按键输入是一种随机事件,所以对熵池是有贡献的
        input_handle_event(dev, type, code, value);          //继续输入子系统的相关模块发送数据
        spin_unlock_irqrestore(&dev->event_lock, flags);
    }
}

is_event_supported函数检查输入设备是否支持该事件:

static inline int is_event_supported(unsigned int code,
                     unsigned long *bm, unsigned int max)
{
    return code <= max && test_bit(code, bm);
}

 

 

 

input_handle_event()函数向输入子系统传达事件信息:

参数1,输入设备            参数2,事件类型       参数3,键码         参数4,键值

 

static void input_handle_event(struct input_dev *dev,
                   unsigned int type, unsigned int code, int value)
{
    int disposition = INPUT_IGNORE_EVENT;                      //处理方式,默认:忽略这个事件

    switch (type) {

    case EV_SYN:
        switch (code) {
        case SYN_CONFIG:
            disposition = INPUT_PASS_TO_ALL;
            break;

        case SYN_REPORT:
            if (!dev->sync) {
                dev->sync = 1;
                disposition = INPUT_PASS_TO_HANDLERS;
            }
            break;
        }
        break;

    case EV_KEY:
        if (is_event_supported(code, dev->keybit, KEY_MAX) &&            //判断是否支持该键
            !!test_bit(code, dev->key) != value) {                                          //按键状态是否改变

            if (value != 2) {
                __change_bit(code, dev->key);                            //改变键的状态
                if (value)
                    input_start_autorepeat(dev, code);                        //处理重复按键的情况
            }

            disposition = INPUT_PASS_TO_HANDLERS;                //事件需要handler来处理,交给handler处理
        }
        break;

    case EV_SW:
        if (is_event_supported(code, dev->swbit, SW_MAX) &&
            !!test_bit(code, dev->sw) != value) {

            __change_bit(code, dev->sw);
            disposition = INPUT_PASS_TO_HANDLERS;
        }
        break;

    case EV_ABS:
        if (is_event_supported(code, dev->absbit, ABS_MAX)) {

            value = input_defuzz_abs_event(value,
                    dev->abs[code], dev->absfuzz[code]);

            if (dev->abs[code] != value) {
                dev->abs[code] = value;
                disposition = INPUT_PASS_TO_HANDLERS;
            }
        }
        break;

    case EV_REL:
        if (is_event_supported(code, dev->relbit, REL_MAX) && value)
            disposition = INPUT_PASS_TO_HANDLERS;

        break;

    case EV_MSC:
        if (is_event_supported(code, dev->mscbit, MSC_MAX))
            disposition = INPUT_PASS_TO_ALL;

        break;

    case EV_LED:
        if (is_event_supported(code, dev->ledbit, LED_MAX) &&
            !!test_bit(code, dev->led) != value) {

            __change_bit(code, dev->led);
            disposition = INPUT_PASS_TO_ALL;
        }
        break;

    case EV_SND:
        if (is_event_supported(code, dev->sndbit, SND_MAX)) {

            if (!!test_bit(code, dev->snd) != !!value)
                __change_bit(code, dev->snd);
            disposition = INPUT_PASS_TO_ALL;
        }
        break;

    case EV_REP:
        if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
            dev->rep[code] = value;
            disposition = INPUT_PASS_TO_ALL;
        }
        break;

    case EV_FF:
        if (value >= 0)
            disposition = INPUT_PASS_TO_ALL;
        break;

    case EV_PWR:
        disposition = INPUT_PASS_TO_ALL;
        break;
    }

    if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)                    //处理EV_SYN事件
        dev->sync = 0;

    if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)                   //交给input_dev处理,并且处理事件存在event()用来想输入子系统报告一个将要发送给设备的事件
        dev->event(dev, type, code, value);

    if (disposition & INPUT_PASS_TO_HANDLERS)        //交给handler处理,调用input_pass_event()处理
        input_pass_event(dev, type, code, value);
}

 

input_pass_event()函数将事件传递到合适的函数,然后对其进行处理:

static void input_pass_event(struct input_dev *dev,
                 unsigned int type, unsigned int code, int value)
{
    struct input_handle *handle;                 分配一个handle结构的指针

    rcu_read_lock();

    handle = rcu_dereference(dev->grab);         得到dev->grab的指针,grab是强制为input device的handler,这时要调用handler的event函数
    if (handle)
        handle->handler->event(handle, type, code, value);
    else             如果没有为input device强制指定handler,即为grap赋值,就遍历input device->h_list上的handle成员,如果该handle被打开,表示该设备已经被一个用户进程使用
        list_for_each_entry_rcu(handle, &dev->h_list, d_node)              就会调用与输入设备对应的handler的event()函数。注意,只有在handle被打开的情况下才会收到事件,这
            if (handle->open)                                                                     就是说,只有设备被用户程序使用时,才有必要向用户导出信息。
                handle->handler->event(handle,
                            type, code, value);
    rcu_read_unlock();
}

事件处理过程如下:

 

 

input_handler是输入子系统的主要数据结构,一般将其称为handler处理器,表示对输入事件的具体处理。input_handler为输入设备的功能实现了一个接口,输入事件最终传递到handler处理器,handler处理器根据一定的规则,然后对事件进行处理。

struct      input_dev                 物理输入设备的基本数据结构,包含设备相关的一些信息

struct      input_handler           事件处理结构体,定义怎么处理事件的逻辑

struct      input_handle            用来创建input_dev和input_handler之间的关系的结构体

 

struct input_handler {

    void *private;              表示驱动特定的数据。这里的驱动指的就是handler处理器

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);          被输入子系统调用去处理发送给设备的事件
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);  连接handler和input_dev在input_attach_handler中调用
    void (*disconnect)(struct input_handle *handle);           断开handler和input_dev之间的联系
    void (*start)(struct input_handle *handle);                

    const struct file_operations *fops;                handler实现的文件操作集
    int minor;                                                       次设备号
    const char *name;                                      表示handler的名字,显示在/proc/bus/input/handlers/目录中

    const struct input_device_id *id_table;                           表示驱动能够处理的表
    const struct input_device_id *blacklist;                          应该忽略的设备

    struct list_head    h_list;                                            与这个handler相联系的下一个handler
    struct list_head    node;                                           将其连接到全局的input_handler_list链表中,所有的input_handler都连接在其上
};

 

input_register_handler()函数注册一个新的input handler处理器。这个handler将为输入设备使用,一个handler可以添加到多个支持它的设备中,也就是一个handler可以处理多个输入设备的事件:

int input_register_handler(struct input_handler *handler)
{
    struct input_dev *dev;
    int retval;

    retval = mutex_lock_interruptible(&input_mutex);
    if (retval)
        return retval;

    INIT_LIST_HEAD(&handler->h_list);

    if (handler->fops != NULL) {
        if (input_table[handler->minor >> 5]) {           次设备号右移5位作为索引值插入到input_table[]中
            retval = -EBUSY;
            goto out;
        }
        input_table[handler->minor >> 5] = handler;
    }

    list_add_tail(&handler->node, &input_handler_list);              插入到全局链表

    list_for_each_entry(dev, &input_dev_list, node)                
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

 out:
    mutex_unlock(&input_mutex);
    return retval;
}

 

struct input_handle {

    void *private;                     handle特定的数据

    int open;                             表示handle是否正在被使用,当使用时,会将事件分发给设备处理
    const char *name;              handle的名字

    struct input_dev *dev;        表示该handle依附的input_dev设备
    struct input_handler *handler;        与设备相关的handler处理器

    struct list_head    d_node;      将handle放到设备相关的链表中,也就是放到input_dev->h_list表示的链表中
    struct list_head    h_node;      将handle放到input_handler相关的链表中,也就是handler->h_list表示的链表中
};

 

input_handle是用来连接input_dev和input_handler的一个中间结构体。事件通过input_handle从input_dev发送到input_handler,或者从input_handler发送到input_dev进行处理。在使用input_handle之前,需要对其进行注册,注册函数input_register_handle():

该函数接受一个注册前初始化的input_handle类型指针来注册一个新的handle到输入子系统

int input_register_handle(struct input_handle *handle)
{
    struct input_handler *handler = handle->handler;
    struct input_dev *dev = handle->dev;
    int error;

    /*
     * We take dev->mutex here to prevent race with
     * input_release_device().
     */
    error = mutex_lock_interruptible(&dev->mutex);
    if (error)
        return error;
    list_add_tail_rcu(&handle->d_node, &dev->h_list);             将handle加入到dev->h_list链表中
    mutex_unlock(&dev->mutex);
    synchronize_rcu();

    /*
     * Since we are supposed to be called from ->connect()
     * which is mutually exclusive with ->disconnect()
     * we can\'t be racing with input_unregister_handle()
     * and so separate lock is not needed here.
     */
    list_add_tail(&handle->h_node, &handler->h_list);                     将handle加入到handler->h_list链表中

    if (handler->start)
        handler->start(handle);

    return 0;
}

 

 

input_dev、input_handler和input_handle的关系:

 

 

input子系统:

在Linux中,输入子系统作为一个模板存在,向上,为用户提供接口函数,向下,为驱动层程序提供统一的接口函数。这样,就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户程序也可以通过输入子系统通知驱动程序完成某项功能

 

初始化函数input_init():

先看几个结构的定义:

struct class input_class = {                 结构体的定义
    .name        = "input",
};

static const struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,
};

static int __init input_init(void)
{
    int err;

    err = class_register(&input_class);           注册input类,所有input device都属于这个类,在sysfs中表现就是,所有input device所代表的目录都位于/dev/class/input下面:
    if (err) {
        printk(KERN_ERR "input: unable to register input_dev class\\n");
        return err;
    }

    err = input_proc_init();                  在proc下面建立相关的交互文件
    if (err)
        goto fail1;

    err = register_chrdev(INPUT_MAJOR, "input", &input_fops);               注册主设备号为INPUT_MAJOR(13)次设备号为0~255的字符设备。它的操作指针为input_fops,所有主设备号为13的字符设备的操作,都会转入到input_fops中
    if (err) {
        printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
        goto fail2;
    }

    return 0;

 fail2:    input_proc_exit();
 fail1:    class_unregister(&input_class);
    return err;
}

 

input_open_file()函数,该函数将控制input_handler中定义的fops文件指针的open()函数。该函数在input_handler中实现,这样就使不同的handler处理器对应了不同的文件打开方法:

static struct input_handler *input_table[8];

 

static int input_open_file(struct inode *inode, struct file *file)
{
    struct input_handler *handler;
    const struct file_operations *old_fops, *new_fops = NULL;
    int err;

    lock_kernel();
    /* No load-on-demand here? */
    handler = input_table[iminor(inode) >> 5];        取出handler处理器,原理是前面赋值的时候,也可看出一个handler代表32个设备节点             
    if (!handler || !(new_fops = fops_get(handler->fops))) {
        err = -ENODEV;
        goto out;
    }

    /*
     * That\'s _really_ odd. Usually NULL ->open means "nothing special",
     * not "no device". Oh, well...
     */
    if (!new_fops->open) {             判断new_fops->open()函数是否定义,如果没有定义,则表示设备不存在
        fops_put(new_fops);             减少引用计数
        err = -ENODEV;
        goto out;
    }
    old_fops = file->f_op;
    file->f_op = new_fops;

    err = new_fops->open(inode, file);               使用新的open()函数,重新打开设备

    if (err) {
        fops_put(file->f_op);
        file->f_op = fops_get(old_fops);
    }
    fops_put(old_fops);
out:
    unlock_kernel();
    return err;
}

 

 

evdev输入事件驱动:

evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev输入事件驱动从底层接收事件信息,将其反映到sys文件系统中,用户程序通过对sys文件系统的操作,就能够达到处理事件的能力

 

evdev以模块的方式被组织在内核中

初始化函数evdev_init():

static int __init evdev_init(void)
{
    return input_register_handler(&evdev_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,
};

#define EVDEV_MINOR_BASE    64

因为handler可以处理32个设备,所以evdev_handler所能处理的设备文件范围为(13,64)~(13,64+32),其中13是所有输入设备的主设备号

 

static const struct input_device_id evdev_ids[] = {
    { .driver_info = 1 },    /* Matches all devices */
    { },            /* Terminating zero entry */
};

evdev_ids没有定义flags,也没有定义匹配属性值。这个evdev_ids的意思就是:evdev_handler可以匹配所有input_dev设备,也就是所有input_dev发出的事件,都可以由evdev_handler来处理。匹配成功后会调用handler->connect()函数

 

evdev_connect()函数主要用来连接input_dev和input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后向谁返回结果:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;
    int minor;
    int error;

    for (minor = 0; minor < EVDEV_MINORS; minor++)      宏为32,表示32个设备文件。找到evdev_table为空的那一项,这时,for结束时minor就是数组第一项为空的那一项
        if (!evdev_table[minor])
            break;

    if (minor == EVDEV_MINORS) {            没有空闲的表项
        printk(KERN_ERR "evdev: no more free evdev devices\\n");
        return -ENFILE;
    }

    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);               分配一个struct evdev空间
    if (!evdev)
        return -ENOMEM;

    INIT_LIST_HEAD(&evdev->client_list);                     初始化evdev
    spin_lock_init(&evdev->client_lock);
    mutex_init(&evdev->mutex);
    init_waitqueue_head(&evdev->wait);

    snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);             event1,event2命名
    evdev->exist = 1;
    evdev->minor = minor;

    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = evdev->name;
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;

    dev_set_name(&evdev->dev, evdev->name);
    evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);

    error = input_register_handle(&evdev->handle);                  注册一个input_handle结构体
    if (error)
        goto err_free_evdev;

    error = evdev_install_chrdev(evdev);                将evdev_table的minor项指向evdev
    if (error)
        goto err_unregister_handle;

    error = device_add(&evdev->dev);     将evdev->device注册到sysfs文件系统中
    if (error)
        goto err_cleanup_evdev;

    return 0;

 err_cleanup_evdev:
    evdev_cleanup(evdev);
 err_unregister_handle:
    input_unregister_handle(&evdev->handle);
 err_free_evdev:
    put_device(&evdev->dev);
    return error;
}

 

用户程序通过输入子系统创建的设备节点函数open(),read()和write(

以上是关于输入子系统的主要内容,如果未能解决你的问题,请参考以下文章

linux输入子系统

OpenHarmony 源码解析之多模输入子系统(事件派发流程)

Linux 输入子系统原理理解(原创)

输入子系统

Linux驱动开发之输入子系统

linux 的 输入子系统 与 平台设备系统个人理解