歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> 學習Linux >> linux輸入子系統(input subsystem)之evdev.c事件處理過程,subsystemevdev.c

linux輸入子系統(input subsystem)之evdev.c事件處理過程,subsystemevdev.c

日期:2017/3/3 18:10:37   编辑:學習Linux

linux輸入子系統(input subsystem)之evdev.c事件處理過程,subsystemevdev.c

linux輸入子系統(input subsystem)之evdev.c事件處理過程,subsystemevdev.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提供

http://xxxxxx/Linuxjc/1162992.html TechArticle

Copyright © Linux教程網 All Rights Reserved