你的位置:首页 > 操作系统

[操作系统]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   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 == 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提供