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



  • 相关阅读:
    2015531 网络攻防 Exp1 PC平台逆向破解(5)M
    2017-2018-1 20155331 嵌入式C语言
    20155330 《网络对抗》 Exp9 web安全基础实践
    20155330 《网络对抗》 Exp8 Web基础
    20155330 《网络对抗》 Exp7 网络欺诈防范
    20155330 《网络对抗》 Exp6 信息搜集与漏洞扫描
    20155330 《网络对抗》 Exp5 MSF基础应用
    20155330 《网络攻防》 Exp4 恶意代码分析
    20155330 《网络攻防》 Exp3 免杀原理与实践
    20155330 《网络对抗》 Exp2 后门原理与实践
  • 原文地址:https://www.cnblogs.com/oracleloyal/p/5416901.html
Copyright © 2011-2022 走看看