首页 > 代码库 > linux输入子系统(input subsystem)之evdev.c事件处理过程

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 = http://www.mamicode.com/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!\n"); 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 =http://www.mamicode.com/= 0xAA) { 89             /* [cgw]: 点亮 */ 90             s3c2410_gpio_setpin(S3C2410_GPF4, 0); 91         } else if (value =http://www.mamicode.com/= 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 =http://www.mamicode.com/= 0xAA) {108                     /* [cgw]: 点亮 */109                     s3c2410_gpio_setpin(S3C2410_GPF4, 0);110                 } else if (value =http://www.mamicode.com/= 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!\n");154     //printk("USER_HZ: %d\n", 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!\n");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     else20     {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!\n");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 =http://www.mamicode.com/= 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 =http://www.mamicode.com/ 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 =http://www.mamicode.com/= 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 = http://www.mamicode.com/(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 =http://www.mamicode.com/= 0xAA) {12                     /* [cgw]: 点亮 */13                     s3c2410_gpio_setpin(S3C2410_GPF4, 0);14                 } else if (value =http://www.mamicode.com/= 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提供

 

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