zoukankan      html  css  js  c++  java
  • input子系统


    /*
       一:input_dev代表一个输入设备
       二:input_event事件传送的载体,输入子系统的事件通过这个结构体包装传送给用户空间
       三:input_handler事件驱动的主体,每一种处理方式对应一个handler结构体
       四:input_handle用来连接input_dev和input_handler
    */

    /*
     input_dev 结构体剖析
    成员说明:
    char *name;
           //设备名字,如键盘名字。
    char *phys;
           //设备文件节点名,如input/kbd0。
    char *uniq;
           //全球唯一的ID号。
    struct input_id id;
           //后文作详细介绍。用于匹配事件处理层handler
    unsigned long evbit[NBITS(EV_MAX);]
          //该设备驱动所能支持的事件。
          //EV_SYN      同步事件
           //EV_KEY       键盘事件
           //EV_REL       相对坐标事件,用于鼠标
           //EV_ABS       绝对坐标事件,用于摇杆
           //EV_MSC      其他事件
           //EV_LED       LED灯事件
           //EV_SND      声音事件
           //EV_REP       重复按键事件
           //EV_FF         受力事件
           //EV_PWR      电源事件
           //EV_FF_STATUS  受力状态事件
    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//用于记录支持的事件类型的位图
    unsigned long keybit[NBITS(KEY_MAX)];
           //键值存放表(位图)
    unsigned long relbit[NBITS(REL_MAX)];
          //用于存放相对坐标值等
    unsigned long absbit[NBITS(ABS_MAX)];
           //用于存放绝对坐标值等
    unsigned long mscbit[NBITS(MSC_MAX)];
           //存放其他事件类型
    unsigned long ledbit[NBITS(LED_MAX)];
           //存放表示各种状态的LED值
    unsigned long sndbit[NBITS(SND_MAX)];
          //存放各种事件的声音
    unsigned long ffbit[NBITS(FF_MAX)];
           //存放受力设备的属性
    int ff_effects_max;
           //显然与受力效果有关,具体作用还不大清楚。

    unsigned int keycodemax;//支持的按键值的个数

    unsigned int keycodesize;//每个键值的字节数

    void * keycode;//存储按键值的数组首地址
          
    unsigned int repeat_key;
           //存放重复按键时的键值(最近一次的按键值,可用于连击)
    struct timer_list timer;//(自动连击计时器)
           //定时器
    struct pm_dev *pm_dev;
           //考虑到有些设备可能有电源管理
    struct pt_regs *regs;
          //不清楚
    int state;
           //显然是表示一个状态,但不清楚具体是谁的状态
    int sync;
           //最后一次同步后没有新的事件置1
    int abs[ABS_MAX + 1];//当前各个坐标的值
           //显然是与绝对坐标有关的,但具体的作用不清楚。
    int rep[REP_MAX + 1];//自动连击的参数
           //存放重复按键时的延时,系统依靠这个延时时间来判断重复按键
           //rep[0]表示开始要重复按键时的延时时间,即第1个键与第2个键(开始重复按键)之间的延时
           //rep[1]此后重复按键之前的延时时间,直到按键抬起
           //通俗解释就是,假如我按了一个“a”,并且一直按着,那么在显示出来的第一个a与第二个a之间的时间延时为rep[0],而此后的相邻两个a之间的延时为rep[1]
    unsigned long key[NBITS(KEY_MAX)];//反映当前按键状态的位图

    unsigned long led[NBITS(LED_MAX)];//反映当前led状态的位图

    unsigned long snd[NBITS(SND_MAX)];//反映当前beep状态的位图
           
    int absmax[ABS_MAX + 1];//记录各个坐标的最大值

    int absmin[ABS_MAX + 1];//记录各个坐标的最小值

    int absfuzz[ABS_MAX + 1];//记录各个坐标的分辨率

    int absflat[ABS_MAX + 1];//记录各个坐标的基准值
           
    int (*open)(struct input_dev *dev);//打开函数

    void (*close)(struct input_dev *dev);//关闭函数

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

    int (*flush)(struct input_dev *dev, struct file *file);//断开连接时冲洗数据

    int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
    //回调函数

    int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);

    int (*erase_effect)(struct input_dev *dev, int effect_id);

           //底层与硬件相关的一组操作,若有具体定义,则会在input core层被调用,具体看input.c。
    struct input_handle *grab;
           //该结构会在后文做具体介绍,这个指针用于占用输入设备用,如键盘
    struct list_head h_list;
    struct list_head node;
           //h_list链表用于与input_handler相联系
           //node链表:设备向输入子系统(input subsystem)注册后,会将该链表添加到系统维护的一个链表中去,从而系统可以管理这个设备
    */

    /*
       other element
    2. input_handler

    说明:事件处理层(event handler)的核心结构。

    头文件:include/linux/input.h

    成员说明:

    void *private;

      //私有数据指针


    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value)
    //事件处理函数指针。设备驱动报告的事件最终由这个函数来处理。

    struct input_handle * (*connect)(struct input_handler *handler,struct input_dev *dev,struct input_device_id *id);//连接handler和input_dev的函数指针

    void (*disconnect)(struct input_handle *handle);//断开链接的函数指针
           //event handler层与input core层之间一组接口
    struct file_operation *fops;//文件操作结构体
           //给应用程序所调用的一组接口
    int minor;
           //这个handler可以使用的32个次设备号的最小值
    char *name;
           //input_handler的名字

    struct input_device_id *id_table;//可以处理的input_device_ids列表

    struct input_device_id *blacklist;//需要被忽略的列表

      //该结构体是对input_id结构体的扩展,从表面上看blacklist为被系统列为黑名单的输入设备列表

    struct list_head h_list;
    //用来存放全局handler链表的节点

    struct list_head node;

           //h_list链表用于与input_dev相联系

           //node链表:事件处理程序向输入子系统(input subsystem)注册后,会将该链表添加到系统维护的一个链表中去,从而系统可以管理这类事件

     3. input_handle

    说明:是一个用于关联驱动层input_dev和事件处理层input_handler的中间结构。

    头文件:include/linux/input.h

    成员说明:

    void *private;
           //私有数据指针
    int open;
           //记录本设备被打开的次数
    char *name;

           //input_handle的名字

    struct input_dev *dev;//指着附着的input_dev

    struct input_handler *handler;

           //这两个就不解释了,前面都有具体介绍

    struct list_head d_node;

    struct list_head h_node;

           //d_node链表用于input_dev链,h_node链表用于input_handler链,有了input_handle,就把相关dev和handler联系起来,相互能容易找到。

     

     4. input_id

    说明:输入设备的一些属性。

    头文件:include/linux/input.h
    成员说明:
    __u16 bustype;
           //总线类型,如BUS_PCI、BUS_USB等
    __u16 vendor;

          //设备生产商
    __u16 product;
           //产品名字
    __u16 version;
           //版本号

     5. input_event
    (这个结构体是事件传送的载体,输入子系统的事件都是包装成struct input_event传给用户空间)
    说明:应用程序可通过此结构体获取输入设备事件信息,也就是说,比如在写键盘测试程序时,我们可用这个结构体,再结合ioctl系统调用来获取来自键盘的信息。

    头文件:include/linux/input.h

    成员说明:

    struct timeval time;
           //time是一个时间戳(timestamp),储存着事件发生时的时间记录
    __u16 type;

           //事件的类型,如EV_KEY,则表示输入事件为键盘事件

    __u16 code;

           //事件的代码,如果为KEY_1,则表示键盘输入为“1”
    __s32 value;//事件值,如坐标的偏移

           //用于键盘时,value为0表示按键松开,value为1表示按键按下,value为2表示重复按键
    #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
            #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
    */

    /*
    1:input_allocate_device()//该函数返回一个指向 input_dev 类型的指针,该结构体是一个输入设备结构体,包含了输入设备的一些相关信息,如设备支持的按键码、设备的名称、设备支持的事件等。


    2:input_register_device()
    input_register_device()函数是输入子系统核心(input core)提供的函数。该函数将 input_dev结构体注册到输入子系统核心中,input_dev 结构体必须由前面讲的 input_allocate_device()函数来分配。input_register_device()函数如果注册失败,必须调用 input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用 input_unregister_device()函数来注销输入设备结构体。简而言之,注册 input device 的过程就是为 input device 设置默认值,并将其挂以 input_dev_list。与挂载在 input_handler_list 中的 handler 相匹配。如果匹配成功,就会调用 handler 的 connect函数。

    3:input_attach_handler()
    4:input_match_device ()
    input_match_device()函数匹配 handle->>id_table 和 dev->id 中的数据。
     如果不成功则返回。handle->id_table 也是一个 input_device_id 类型的指针,其表示驱动支持的设备列表。input_match_device ()函数用来与 input_dev 和 handler 进行匹配。handler 的 id_table 表中定义了其支持的 input_dev 设备。该函数的代码如下:

    */
    /*
       1: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);/*模块引用技术加 1*/  
                }  
                return dev;  
        }  
    */
    /*
        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;  
          
                /* Every input device generates EV_SYN/SYN_REPORT events. */  
                __set_bit(EV_SYN, dev->evbit);/*调用__set_bit()函数设置 input_dev 所支持的事件类型。事件类型由 input_dev 的evbit 成员来表示,在这里将其 EV_SYN 置位,表示设备支持所有的事件。一个设备可以支持一种或者多种事件类型。*/  
          
                /* KEY_RESERVED is not supposed to be transmitted to userspace. */  
                __clear_bit(KEY_RESERVED, dev->keybit);  
          
                /* Make sure that bitmasks not mentioned in dev->evbit are clean. */  
                input_cleanse_bitmasks(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);/*初始化一个 timer 定时器,这个定时器是为处理重复击键而定义的。*/  
          
                /*如果 dev->rep[REP_DELAY]和 dev->rep[REP_PERIOD]没有设值,则将其赋默认值,这主要是为自动处理重复按键定义的。*/  
                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;  
                }  
                /*检查 getkeycode()函数和 setkeycode()函数是否被定义,如果没定义,则使用默认的处理函数,这两个函数为 input_default_getkeycode()和
                input_default_setkeycode()。input_default_getkeycode()函数用来得到指定位置的键值。input_default_setkeycode()函数用来设置键值。*/  
                if (!dev->getkeycode)  
                        dev->getkeycode = input_default_getkeycode;  
          
                if (!dev->setkeycode)  
                        dev->setkeycode = input_default_setkeycode;  
                /*设置 input_dev 中的 device 的名字,名字以 input0、input1、input2、input3、input4等的形式出现在 sysfs 文件系统中。*/  
                dev_set_name(&dev->dev, "input%ld",  
                        (unsigned long) atomic_inc_return(&input_no) - 1);  
          
                error = device_add(&dev->dev);  
                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_add_tail()函数将 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_attach_handler()函数用来匹配 input_dev 和 handler,只有匹配成功,才能进行下一步的关联操作。*/  
          
                input_wakeup_procfs_readers();  
          
                mutex_unlock(&input_mutex);  
          
                return 0;  
        }  
    */

    /*
        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))/*首先判断 handle 的 blacklist 是否被赋值,如果被赋值,则匹配 blacklist 中的数据跟 dev->id 的数据是否匹配。blacklist 是一个 input_device_id*的类型,其指向 input_device_id的一个表,这个表中存放了驱动程序应该忽略的设备。即使在 id_table 中找到支持的项,也应该忽略这种设备。*/  
                return -ENODEV;  
          
                id = input_match_device(handler, 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_device_id
    C++代码

        struct input_device_id {  
          
                kernel_ulong_t flags;/*标志信息*/  
                __u16 bustype; /*总线类型*/  
                __u16 vendor;/*制造商 ID*/  
                __u16 product;/*产品 ID*/  
                __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;/*驱动额外的信息*/  
          
        };  
    */

    /*
    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;//声明一个局部变量 i,用于循环。  
     
            /*是一个 for 循环,用来匹配 id 和 dev->id 中的信息,只要有一项相同则返回。*/  
            for (id = handler->id_table; id->flags || id->driver_info; id++) {  
                    /*用 来 匹 配 总 线 类 型 。 id->flags 中 定 义 了 要 匹 配 的 项 , 其 中INPUT_DEVICE_ID_MATCH_BUS 如果没有设置,则比较 input device 和 input handler 的总线类型。*/  
                    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 匹配项。如果 id->flags 定义的类型匹配成功,或者 id->flags没有定义,才会进入到 MATCH_BIT 的匹配项。*/  
                    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;  
    }
    */




     #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)
    {
         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 enough memory ");
             error = -ENOMEM;
            goto err_free_irq;
      }

         button_dev->evbit[0] = BIT_MASK(EV_KEY); /*设置按键信息*/
         button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
    //分别用来设置设备所产生的事件以及上报的按键值。Struct iput_dev中有两个成员,一个是evbit.一个是keybit.分别用

           //表示设备所支持的动作和键值。
         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);



  • 相关阅读:
    使用 linux kernel +busybox 定制linux系统
    记一次golang的内存泄露
    关于Queries_per_sec 性能计数器
    NUMA导致的MySQL服务器SWAP问题分析
    Drop Table对MySQL的性能影响分析
    当MySQL数据库遇到Syn Flooding
    tcp_tw_recycle参数引发的数据库连接异常
    一例数据同步异常问题分析
    MySQL大量线程处于Opening tables的问题分析
    MySQL DeadLock故障排查过程
  • 原文地址:https://www.cnblogs.com/oracleloyal/p/5416901.html
Copyright © 2011-2022 走看看