首页 > 代码库 > 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事件处理过程