linux输入子系统(input subsystem)之evdev.c事件处理过程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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!\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 == 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!\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 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!\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 == 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提供
以上是关于linux输入子系统(input subsystem)之evdev.c事件处理过程的主要内容,如果未能解决你的问题,请参考以下文章
linux输入子系统(input subsystem)之evdev.c事件处理过程
Linux 输入设备调试详解(零基础开发)Rotary_Encoder旋转编码器模块(EC11)通用GPIO为例 挂载input输入子系统