zoukankan      html  css  js  c++  java
  • 输入子系统

    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 ", button_irq);                    //申请失败
                    return -EBUSY;
            }
            button_dev = input_allocate_device();                                           //分配一个设备结构体
            if(!button_dev)                                                                               //

            {

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

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

          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 ",
            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 ",
                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 ");
            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 ");
            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(),打开和读写输入设备

    对主设备号为INPUT_MAJOR的设备节点进行操作,会将操作集转换成handler的操作集:

    static const struct file_operations evdev_fops = {
        .owner        = THIS_MODULE,
        .read        = evdev_read,
        .write        = evdev_write,
        .poll        = evdev_poll,
        .open        = evdev_open,
        .release    = evdev_release,
        .unlocked_ioctl    = evdev_ioctl,
    #ifdef CONFIG_COMPAT
        .compat_ioctl    = evdev_ioctl_compat,
    #endif
        .fasync        = evdev_fasync,
        .flush        = evdev_flush
    };

    evdev_open()函数:

    用户可以这样调用open("/dev/input/event1",O_RDONLY)函数打开设备节点时,会调用evdev_fops中的evdev_open()函数:

    static int evdev_open(struct inode *inode, struct file *file)
    {
        struct evdev *evdev;
        struct evdev_client *client;
        int i = iminor(inode) - EVDEV_MINOR_BASE;              得到在evdev_table[]中的序号
        int error;

        if (i >= EVDEV_MINORS)
            return -ENODEV;

        error = mutex_lock_interruptible(&evdev_table_mutex);
        if (error)
            return error;
        evdev = evdev_table[i];
        if (evdev)
            get_device(&evdev->dev);
        mutex_unlock(&evdev_table_mutex);

        if (!evdev)
            return -ENODEV;

        client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);                    分配并初始化一个client结构体,并将它和evdev关联起来
        if (!client) {
            error = -ENOMEM;
            goto err_put_evdev;
        }

        spin_lock_init(&client->buffer_lock);
        client->evdev = evdev;                      
        evdev_attach_client(evdev, client);                   将client挂到evdev->client_list上

        error = evdev_open_device(evdev);                    打开输入设备
        if (error)
            goto err_free_client;

        file->private_data = client;
        return 0;

     err_free_client:
        evdev_detach_client(evdev, client);
        kfree(client);
     err_put_evdev:
        put_device(&evdev->dev);
        return error;
    }

    evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据:

    static int evdev_open_device(struct evdev *evdev)
    {
        int retval;

        retval = mutex_lock_interruptible(&evdev->mutex);
        if (retval)
            return retval;

        if (!evdev->exist)
            retval = -ENODEV;
        else if (!evdev->open++) {            如果是第一次打开,就会调用input_open_device()打开evdev对应的handle
            retval = input_open_device(&evdev->handle);
            if (retval)
                evdev->open--;
        }

        mutex_unlock(&evdev->mutex);
        return retval;
    }

    int input_open_device(struct input_handle *handle)
    {
        struct input_dev *dev = handle->dev;
        int retval;

        retval = mutex_lock_interruptible(&dev->mutex);
        if (retval)
            return retval;

        if (dev->going_away) {
            retval = -ENODEV;
            goto out;
        }

        handle->open++;

        if (!dev->users++ && dev->open)
            retval = dev->open(dev);

        if (retval) {
            dev->users--;
            if (!--handle->open) {
                /*
                 * Make sure we are not delivering any more events
                 * through this handle
                 */
                synchronize_rcu();
            }
        }

     out:
        mutex_unlock(&dev->mutex);
        return retval;
    }

    有一种落差是,你配不上自己的野心,也辜负了所受的苦难
  • 相关阅读:
    hibernate建表默认为UTF-8编码
    XML和JSON
    chrome 模拟发送请求的方法
    什么时候需要使用缓存?
    eclipse中查找类、方法及变量被引用的地方
    用户内容与商业
    2019第48周日
    ajax与重定向
    ifream
    Windows下找到JVM占用资源高的线程
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/7189766.html
Copyright © 2011-2022 走看看