• linux输入子系统(input subsystem)之evdev.c事件处理过程


    1.代码

    input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变。

    input_subsys.drv.c

      1 #include <linux/module.h>
      2 #include <linux/version.h>
      3 
      4 #include <linux/init.h>
      5 #include <linux/fs.h>
      6 #include <linux/interrupt.h>
      7 #include <linux/irq.h>
      8 #include <linux/sched.h>
      9 #include <linux/pm.h>
     10 #include <linux/sysctl.h>
     11 #include <linux/proc_fs.h>
     12 #include <linux/delay.h>
     13 #include <linux/platform_device.h>
     14 #include <linux/input.h>
     15 #include <linux/irq.h>
     16 
     17 #include <asm/gpio.h>
     18 #include <asm/io.h>
     19 #include <asm/arch/regs-gpio.h>
     20 
     21 
     22 struct pin_desc{
     23     int irq;
     24     char *name;
     25     unsigned int pin;
     26     unsigned int key_val;
     27 };
     28 
     29 struct pin_desc pins_desc[4] = {
     30     {IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},
     31     {IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},
     32     {IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},
     33     {IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},
     34 };
     35 
     36 static struct input_dev *input_subsys_dev;
     37 static struct pin_desc *irq_pd;
     38 static struct timer_list buttons_timer;
     39 
     40 static irqreturn_t buttons_irq(int irq, void *dev_id)
     41 {
     42     /* [cgw]: 按键IO发生边沿中断时重新设置定时间隔
     43      * 用于按键消抖
     44      */
     45     irq_pd = (struct pin_desc *)dev_id;
     46     buttons_timer.data = irq_pd->pin;
     47     mod_timer(&buttons_timer, jiffies+USER_HZ/10);
     48     return IRQ_RETVAL(IRQ_HANDLED);
     49 }
     50 
     51 static void buttons_timer_function(unsigned long data)
     52 {
     53     struct pin_desc * pindesc = irq_pd;
     54     unsigned int pinval;
     55 
     56     if (!pindesc)
     57         return;
     58         
     59     /* [cgw]: 获取按键IO状态 */
     60     pinval = s3c2410_gpio_getpin((unsigned int)data);
     61 
     62     /* [cgw]: 根据按键IO状态上报按键事件 */
     63     if (pinval)
     64     {
     65         /* [cgw]: 上报按键弹起 */
     66         input_report_key(input_subsys_dev, pindesc->key_val, 0);
     67         //input_sync(input_subsys_dev);
     68     }
     69     else
     70     {
     71         /* [cgw]: 上报按键按下 */
     72         input_report_key(input_subsys_dev, pindesc->key_val, 1);
     73         //input_sync(input_subsys_dev);
     74     }
     75 
     76     //printk("timer occur!
    ");
     77 }
     78 
     79 
     80 static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value)
     81 {
     82     #if 0
     83     /* [cgw]: 根据应用程序下发的LED控制事件
     84      * 亮灭LED
     85      */
     86     //if (code == SND_BELL) {
     87     if (code == LED_MUTE) {
     88         if (value == 0xAA) {
     89             /* [cgw]: 点亮 */
     90             s3c2410_gpio_setpin(S3C2410_GPF4, 0);
     91         } else if (value == 0xEE) {
     92             /* [cgw]: 熄灭 */
     93             s3c2410_gpio_setpin(S3C2410_GPF4, 1);
     94         }
     95         
     96         return 0;
     97     }
     98     #endif
     99 
    100     switch (type) {
    101         case EV_REP:
    102             return 0;
    103             //break;
    104 
    105         case EV_LED:
    106             if (code == LED_MUTE) {
    107                 if (value == 0xAA) {
    108                     /* [cgw]: 点亮 */
    109                     s3c2410_gpio_setpin(S3C2410_GPF4, 0);
    110                 } else if (value == 0xEE) {
    111                     /* [cgw]: 熄灭 */
    112                     s3c2410_gpio_setpin(S3C2410_GPF4, 1);
    113                 }
    114                 
    115                 return 0;
    116             }
    117             //break;
    118 
    119         case EV_SND:
    120             return 0;
    121             //break;
    122     }
    123     
    124     return -1;
    125 }
    126 
    127 int input_subsys_open(struct input_dev *dev)
    128 { 
    129     int i, retval;
    130     
    131     /* [cgw]: 设置按键IO为中断输入 */
    132     s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_EINT0);
    133     s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
    134     s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_EINT11);
    135     s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_EINT19);
    136 
    137     /* [cgw]: 设置LED IO为输出,初始为熄灭LED */
    138     s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
    139     s3c2410_gpio_setpin(S3C2410_GPF4, 1);
    140 
    141     s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
    142     s3c2410_gpio_setpin(S3C2410_GPF5, 1);
    143 
    144     s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
    145     s3c2410_gpio_setpin(S3C2410_GPF5, 1);
    146 
    147     /* [cgw]: 为按键IO分配中断线 */
    148     for (i = 0; i < 4; i++)
    149     {
    150         retval = request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
    151     }
    152 
    153     printk("input subsys open!
    ");
    154     //printk("USER_HZ: %d
    ", USER_HZ);
    155 
    156     return 0;
    157 }
    158 
    159 
    160 
    161 static int input_subsys_init(void)
    162 {
    163     /* [cgw]: 分配一个输入设备 */
    164     input_subsys_dev = input_allocate_device();
    165     input_subsys_dev->name = "input_subsys_dev";
    166 
    167     /* [cgw]: 设置支持的事件类型 */
    168     set_bit(EV_KEY, input_subsys_dev->evbit);
    169     set_bit(EV_REP, input_subsys_dev->evbit);
    170     
    171     set_bit(EV_LED, input_subsys_dev->evbit);
    172     //set_bit(EV_SND, input_subsys_dev->evbit);
    173 
    174     /* [cgw]: 设置支持的事件码 */
    175     set_bit(KEY_L, input_subsys_dev->keybit);
    176     set_bit(KEY_S, input_subsys_dev->keybit);
    177     set_bit(KEY_ENTER, input_subsys_dev->keybit);
    178     set_bit(KEY_LEFTSHIFT, input_subsys_dev->keybit);
    179 
    180     set_bit(LED_MUTE, input_subsys_dev->ledbit);
    181     //set_bit(SND_BELL, input_subsys_dev->sndbit);
    182 
    183     /* [cgw]: 分配输入设备的open方法 */
    184     input_subsys_dev->open = input_subsys_open;
    185     /* [cgw]: 分配输入设备的event方法,用户在应用程序write()时 */
    186     input_subsys_dev->event = event_handler;
    187 
    188     /* [cgw]: 注册输入设备 */
    189     input_register_device(input_subsys_dev);
    190 
    191     //input_subsys_dev->rep[REP_DELAY] = 250;
    192     //input_subsys_dev->rep[REP_PERIOD] = 100;
    193 
    194     /* [cgw]: 初始化定时器,用于按键消抖 */
    195     init_timer(&buttons_timer);
    196     buttons_timer.function = buttons_timer_function;
    197     add_timer(&buttons_timer);
    198 
    199     printk("input subsys init!
    ");
    200     
    201     return 0;
    202 }
    203 
    204 static void input_subsys_exit(void)
    205 {
    206     int i;
    207 
    208     /* [cgw]: 释放按键IO中断 */
    209     for (i = 0; i < 4; i++)
    210     {
    211         free_irq(pins_desc[i].irq, &pins_desc[i]);
    212     }
    213 
    214     /* [cgw]: 删除定时器 */
    215     del_timer(&buttons_timer);
    216     /* [cgw]: 注销输入设备 */
    217     input_unregister_device(input_subsys_dev);
    218     /* [cgw]: 释放输入设备内存空间 */
    219     input_free_device(input_subsys_dev);    
    220 }
    221 
    222 module_init(input_subsys_init);
    223 
    224 module_exit(input_subsys_exit);
    225 
    226 MODULE_LICENSE("GPL");


    2. input_subsys_drv.c, input.c, evdev.c 三者之间的关系:

    input_subsys_drv.c: 负责获取底层硬件产生的事件,如:中断,按键输入等,收集到这些事件传递给input.c, 并通过设置evdev.c可以支持的事件类型,和evdev.c建立连接

    input.c: 输入子系统内核,收集底层硬件发来的(如:如中断,按键输入)和用户空间发来的(如:write,ioctl)事件,传递给evdev.c

    evdev.c: 收集从input.c传递过来的事件,存储到一个环形缓冲队列,并产生一个异步通知,通知用户空间读取事件

    3. 按键输入(底层硬件)和LED(用户空间)事件处理过程

    3.1 按键输入事件处理过程:

    input_subsys_drv.c 同过外部中断获得按键的状态,经过消抖之后,向input.c上报:

     1 static void buttons_timer_function(unsigned long data)
     2 {
     3     struct pin_desc * pindesc = irq_pd;
     4     unsigned int pinval;
     5 
     6     if (!pindesc)
     7         return;
     8         
     9     /* [cgw]: 获取按键IO状态 */
    10     pinval = s3c2410_gpio_getpin((unsigned int)data);
    11 
    12     /* [cgw]: 根据按键IO状态上报按键事件 */
    13     if (pinval)
    14     {
    15         /* [cgw]: 上报按键弹起 */
    16         input_report_key(input_subsys_dev, pindesc->key_val, 0);
    17         //input_sync(input_subsys_dev);
    18     }
    19     else
    20     {
    21         /* [cgw]: 上报按键按下 */
    22         input_report_key(input_subsys_dev, pindesc->key_val, 1);
    23         //input_sync(input_subsys_dev);
    24     }
    25 
    26     //printk("timer occur!
    ");
    27 }

    input_report_key():

    1 static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
    2 {
    3     input_event(dev, EV_KEY, code, !!value);
    4 }


    input_event()

     1 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
     2 {
     3     ...
     4 
     5     switch (type) {
     6         ...
     7 
     8         case EV_KEY:
     9 
    10             if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
    11                 return;
    12 
    13             if (value == 2)
    14                 break;
    15 
    16             change_bit(code, dev->key);
    17 
    18             if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
    19                 dev->repeat_key = code;
    20                 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
    21             }
    22 
    23             break;
    24 
    25             ...
    26 
    27     }
    28 
    29     ....
    30     handle->handler->event(handle, type, code, value);
    31 }


    其中:case EV_KEY中,对按键连发做初步检测,即检测是否有按键的按下和弹起这两个状态,缺一个都不行(后面解析)。

    接着就调用handle->handler->event(),实际上是调用了evdev_event();

    因为

    1 static struct input_handler evdev_handler = {
    2     .event =    evdev_event,
    3     ...
    4 };


    evdev_event():

     1 static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
     2 {
     3     ...
     4     /* [cgw]: 把接收到的事件加入到一个环形队列 */
     5     do_gettimeofday(&client->buffer[client->head].time);
     6     client->buffer[client->head].type = type;
     7     client->buffer[client->head].code = code;
     8     client->buffer[client->head].value = value;
     9     client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
    10 
    11     /* [cgw]: 发送一个异步通知 */
    12     kill_fasync(&client->fasync, SIGIO, POLL_IN);
    13 
    14     /* [cgw]: 唤醒正在等待这个事件的进程 */
    15     wake_up_interruptible(&evdev->wait);
    16 }

    在evdev_event中发送了异步通知并唤醒了再睡眠的进程,所以在应用程序调用read时,就会获得这个事件。

    1 /* [cgw]: 异步通知产生时返回的数据 */
    2     read(fd, &buttons_event, sizeof(struct input_event));


    3.1.1 按键连发的处理过程

    首先在input_subsys_init() 使能EV_REP按键连发功能

     1 static int input_subsys_init(void)
     2 {
     3     /* [cgw]: 分配一个输入设备 */
     4     input_subsys_dev = input_allocate_device();
     5     input_subsys_dev->name = "input_subsys_dev";
     6 
     7     /* [cgw]: 设置支持的事件类型 */
     8     set_bit(EV_KEY, input_subsys_dev->evbit);
     9     set_bit(EV_REP, input_subsys_dev->evbit);
    10     
    11     set_bit(EV_LED, input_subsys_dev->evbit);
    12     //set_bit(EV_SND, input_subsys_dev->evbit);
    13 
    14     /* [cgw]: 设置支持的事件码 */
    15     set_bit(KEY_L, input_subsys_dev->keybit);
    16     set_bit(KEY_S, input_subsys_dev->keybit);
    17     set_bit(KEY_ENTER, input_subsys_dev->keybit);
    18     set_bit(KEY_LEFTSHIFT, input_subsys_dev->keybit);
    19 
    20     set_bit(LED_MUTE, input_subsys_dev->ledbit);
    21     //set_bit(SND_BELL, input_subsys_dev->sndbit);
    22 
    23     /* [cgw]: 分配输入设备的open方法 */
    24     input_subsys_dev->open = input_subsys_open;
    25     /* [cgw]: 分配输入设备的event方法,用户在应用程序write()时 */
    26     input_subsys_dev->event = event_handler;
    27 
    28     /* [cgw]: 注册输入设备 */
    29     input_register_device(input_subsys_dev);
    30 
    31     ...
    32     
    33     return 0;
    34 }
     1 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
     2 {
     3     ...
     4 
     5     switch (type) {
     6         ...
     7 
     8         case EV_KEY:
     9 
    10             if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
    11                 return;
    12 
    13             /* [cgw]: 收到连发按键的事件,返回 */
    14             if (value == 2)  
    15                 break;
    16 
    17             /* [cgw]: 这个函数的设置,用于上面!!test_bit(code, dev->key) == value判断
    18              * 是否为按下弹起操作
    19              */
    20             change_bit(code, dev->key);
    21 
    22             /* [cgw]: 如果当前操作为按下,并且连发功能使能,则设置连发的触发时间 */
    23             if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
    24                 dev->repeat_key = code;
    25                 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
    26             }
    27 
    28             break;
    29 
    30             ...
    31 
    32     }
    33 
    34     ....
    35     handle->handler->event(handle, type, code, value);
    36 }


    在input_event()中如果检测到按键按下,一直到连发功能触发,则定时器超时调用超时处理函数:

    因为在注册输入设备时,就分配了定时器的超时处理函数input_repeat_key()

     1 int input_register_device(struct input_dev *dev)
     2 {
     3     ...
     4     /*
     5      * If delay and period are pre-set by the driver, then autorepeating
     6      * is handled by the driver itself and we don't do it in input.c.
     7      */
     8 
     9     init_timer(&dev->timer);
    10     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
    11         dev->timer.data = (long) dev;
    12         dev->timer.function = input_repeat_key;
    13         dev->rep[REP_DELAY] = 250;
    14         dev->rep[REP_PERIOD] = 33;
    15     }
    16 
    17     ...
    18 }


    在input_repeat_key()中

     1 static void input_repeat_key(unsigned long data)
     2 {
     3     struct input_dev *dev = (void *) data;
     4 
     5     /* [cgw]: 是否分配了连发的键值 */
     6     if (!test_bit(dev->repeat_key, dev->key))
     7         return;
     8 
     9     /* [cgw]: 发送连发事件 */
    10     input_event(dev, EV_KEY, dev->repeat_key, 2);
    11     input_sync(dev);
    12 
    13     /* [cgw]: 设置连发间隔 */
    14     if (dev->rep[REP_PERIOD])
    15         mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
    16 }


    3.2 led控制事件处理过程

    用户在应用程序中操作write时

    1 /* [cgw]: 发送LED控制事件 */
    2             write(fd, &leds_event, sizeof(struct input_event));


    对应的是操作了evdev_write()

     1 static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
     2 {
     3     ...
     4     /* [cgw]: 收到来自用户空间的事件 */
     5     if (evdev_event_from_user(buffer + retval, &event))
     6         return -EFAULT;
     7     /* [cgw]: 调用input_event() */
     8     input_inject_event(&evdev->handle, event.type, event.code, event.value);
     9 
    10     return retval;
    11 }


    input_event()

     1 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
     2 {
     3     ...
     4 
     5     switch (type) {
     6         ...
     7 
     8         case EV_LED:
     9             
    10             if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
    11                 return;
    12 
    13             /* [cgw]: 这个函数用于上面!!test_bit(code, dev->led) == value是否为不同的LED状态(亮,灭) */
    14             change_bit(code, dev->led);
    15 
    16             /* [cgw]: 调用事件处理,这个事件处理需要驱动提供,做一些特别的处理 
    17              * 本例提供了
    18              * static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value)
    19              * 不影响调用evdev_event()
    20              */
    21             if (dev->event)
    22                 dev->event(dev, type, code, value);
    23 
    24             break;
    25 
    26             ...
    27 
    28     }
    29 
    30     ....
    31     handle->handler->event(handle, type, code, value);
    32 }


    event_handler()

     1 static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value)
     2 {
     3 
     4     switch (type) {
     5         case EV_REP:
     6             return 0;
     7             //break;
     8 
     9         case EV_LED:
    10             if (code == LED_MUTE) {
    11                 if (value == 0xAA) {
    12                     /* [cgw]: 点亮 */
    13                     s3c2410_gpio_setpin(S3C2410_GPF4, 0);
    14                 } else if (value == 0xEE) {
    15                     /* [cgw]: 熄灭 */
    16                     s3c2410_gpio_setpin(S3C2410_GPF4, 1);
    17                 }
    18                 
    19                 return 0;
    20             }
    21             //break;
    22 
    23         case EV_SND:
    24             return 0;
    25             //break;
    26     }
    27     
    28     return -1;
    29 }


    因此用户空间发下来的事件,分两个路径处理

    1.dev->event()  即:event_handler,由驱动程序提供

    2.handler-event()  即:evdev_event(), 由evdev.c提供

  • 相关阅读:
    C#事件(event)解析
    dll加入到GAC后,如何方便的调试
    『C程序设计』读书笔记系列文章之第四章 逻辑运算和判断选取控制
    C#委托之个人理解
    虚方法(virtual)和抽象方法(abstract)的区别
    『C程序设计』读书笔记系列文章之第二章 数据类型、运算符与表达式
    SOA概览(转)
    今天学的几个有用的句型
    【老孙随笔】PPT高手的启示
    【项目经理之修炼(11)】《初级篇》什么样的项目经理才可能成功??
  • 原文地址:https://www.cnblogs.com/hackfun/p/5935694.html
Copyright © 2020-2023  润新知