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提供

时间: 2024-10-13 16:21:47

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

Linux输入子系统(Input Subsystem)

Linux输入子系统(Input Subsystem) http://blog.csdn.net/lbmygf/article/details/7360084 input子系统分析  http://blog.chinaunix.net/uid-27717694-id-3758334.html

Linux输入子系统(转)

Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中断(或驱动通过timer定时查询),然后cpu通过SPI,I2C或者外部存储器总线读取键值,坐标等数据,放一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值,坐标等数据. 在Linux中,输入子系统是由输入子系统设备驱动层.输入子系统核心层(Input Core)和输入

Linux 输入子系统

Technorati 标签: Kernel 输入子系统 Input      在Linux中,输入设备(如按键.键盘.触摸屏.鼠标等)是典型的字符设备,其一般的工作机理,是底层在按键.触摸时,触发一个中断,或者驱动通过定时器定时查询,通过这两种方式通知CPU,CPU然后通过SPI.I2C或I/O接口读取键值.坐标等数据,放入缓冲区,字符设备驱动管理该缓冲区,向上提供read接口供应用程序使用.      在上述的工作流程中,只有终端.读取数值是根具体硬件设备相关,而输入事件的缓冲区管理以及字符设

Linux输入子系统框架分析(1)

在Linux下的输入设备键盘.触摸屏.鼠标等都可以用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层,事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备驱动层将输入事件上报给核心层input.c,核心层找到匹配的事件层,将事件交给事件层处理,事件层处理完后传递到用户空间. 我们最终要搞清楚的是在用户空间调用open和read最终在内核中是怎样处理的,向内核上报的事件又是谁处理的,处理完后是怎样传递到用户空间的? 上面两个图是输入子系统的框架. 下面

Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. http://blog.csdn.net/kangear/article/details/40072707 在调试一下红外遥控器input驱动时,直接採用的是一个半成品的驱动在上边实现的自己的设备的匹配,但同一时候遇到了一些关于input输入子系统的疑惑. 按键一般有「按下和抬起」两个状态一般使用0和1

Android底层开发之Linux输入子系统要不要判断系统休眠状态上报键值

Android底层开发之Linux输入子系统要不要判断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是无用的,甚至是错误的.重点在最后,前边仅仅是一些假想推测. http://blog.csdn.net/kangear/article/details/40072707 在调试一下红外遥控器input驱动时,直接采用的是一个半成品的驱动在上边实现的自己的设备的匹配,但同时遇到了一些关于input输入子系统的疑惑. 按键一般有「按下和抬起」两个状态一般使用0和1来分

Linux 输入子系统原理理解(原创)

linux    输入子系统原理理解(原创) 以前学了单独的按键设备驱动以及鼠标驱动,实际上,在linux中实现这些设备驱动,有一种更为推荐的方法,就是input输入子系统.平常我们的按键,触摸屏,鼠标等输入型设备都可以利用input接口来简化驱动程序并实现设备驱动. 输入子系统原理 linux输入子系统的体系结构可以分为三个层面,分别为:驱动层.输入核心层.事件处理层,三个有点类似PHP的MVC模式,意思就是每个层次只是负责单独的一个功能,无需参与其他的功能,有点类似函数的封装,好了,废话不多

Linux学习:输入子系统 input

一.输入子系统 针对输入设备设计:触摸屏.键盘.按键.传感器.鼠标...... 二.每种设备都属于字符设备驱动,程序的写法步骤也相同 1.实现入口函数 xxx_init() 和卸载函数 xxx_exit() 2.申请设备号 register_chrdev() --- 与内核相关 3.创建设备文件(节点) class_create() 和 device_create() --- 与内核相关 4.硬件初始化 GPIO操作 --- 与硬件相关 注册中断 --- 与硬件相关 初始化等待队列 --- 与内

linux输入子系统(6)-input子系统介绍及结构图

注:本系列转自: http://www.ourunix.org/post/290.html input子系统介绍         输入设备(如按键,键盘,触摸屏,鼠标,蜂鸣器等)是典型的字符设备,其一般的工作机制是底层在按键,触摸等动作发生时产生一个中断(或驱动通过timer定时查询),然后cpu通过SPI,I2C或者外部存储器总线读取键值,坐标等数据,放一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值,坐标等数据.         在Linux中,输入子系统是由