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 子系统详解

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

Linux驱动开发input子系统

Linux驱动开发input子系统

Linux 输入设备调试详解(零基础开发)Rotary_Encoder旋转编码器模块(EC11)通用GPIO为例 挂载input输入子系统

linux输入子系统(input subsystem)之按键输入和LED控制