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