Makefile文件:
1 TEST_APP=event_button
2
3
4 KERNEL_VER = linux-3.0
5 LINUX_SRC ?= /home/fanmaolin/fl2440/kernel/$(KERNEL_VER)
6
7
1 TEST_APP=event_button
2
3
4 KERNEL_VER = linux-3.0
5 LINUX_SRC ?= /home/fanmaolin/fl2440/kernel/$(KERNEL_VER)
6
7
Makefile
8 CROSS_COMPILE=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-
9
10
11 PWD := $(shell pwd)
12
13
14 obj-m += kbd_device.o
15 obj-m += kbd_driver.o
16
17
18 modules:
19 @make -C $(LINUX_SRC) M=$(PWD) modules
20 @make clear
21 @chmod a+x *.ko && cp *.ko /tftp
22 @make testapp
23
24
25 clear:
26 @rm -f *.o *.cmd *.mod.c
27 @rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
28 @rm -f .*ko.cmd .*.o.cmd .*.o.d
29
30
31 clean: clear
32 @rm -f *.ko ${TEST_APP}
33
34
35 testapp:
36 ${CROSS_COMPILE}gcc ${TEST_APP}.c -o ${TEST_APP}
测试程序文件:
+ event_button.c
1 /*********************************************************************************
2 * Copyright: (C) 2017 fanmaolin<fanmaolinn@gmail.com>
3 * All rights reserved.
4 *
5 * Filename: event_button.c
6 * Description: This file
7 *
8 * Version: 1.0.0(04/17/2017)
9 * Author: fanmaolin <fanmaolinn@gmail.com>
10 * ChangeLog: 1, Release initial version on "04/17/2017 05:37:17 AM"
11 *
12 ********************************************************************************/
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <libgen.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <linux/input.h>
26 #include <linux/kd.h>
27 #include <linux/keyboard.h>
28
29 #if 0 /* Just for comment here, Reference to linux-3.3/include/linux/input.h */
30 struct input_event
31 {
32 struct timeval time;
33 __u16 type; /* 0x00:EV_SYN 0x01:EV_KEY 0x04:EV_MSC 0x11:EV_LED*/
34 __u16 code; /* key value, which key */
35 __s32 value; /* 1: Pressed 0:Not pressed 2:Always Pressed */
36 };
37 #endif
38
39 #define TRUE 1
40 #define FALSE 0
41
42 #define EV_RELEASED 0
43 #define EV_PRESSED 1
44 #define EV_REPEAT 2
45
46 #define BUTTON_CNT 5
47
48 #define MODE_POLL 0x01
49 #define MODE_NORMAL 0x02
50
51 void usage(char *name);
52 void display_button_event(struct input_event *ev, int cnt);
53
54 int main(int argc, char **argv)
55 {
56 char *kbd_dev = NULL;
57 char kbd_name[256] = "Unknown";
58 int kbd_fd = -1;
59
60 int rv, opt;
61 int mode = MODE_NORMAL;
62 int size = sizeof (struct input_event);
63
64 struct input_event ev[BUTTON_CNT];
65
66 struct option long_options[] = {
67 {"device", required_argument, NULL, 'd'},
68 {"poll", no_argument, NULL, 'p'},
69 {"help", no_argument, NULL, 'h'},
70 {NULL, 0, NULL, 0}
71 };
72
73 while ((opt = getopt_long(argc, argv, "d:ph", long_options, NULL)) != -1)
74 {
75 switch (opt)
76 {
77 case 'd':
78 kbd_dev = optarg;
79 break;
80
81 case 'p':
82 mode = MODE_POLL;
83 break;
84
85 case 'h':
86 usage(argv[0]);
87 return 0;
88
89 default:
90 break;
91 }
92 }
93
94 if(NULL == kbd_dev)
95 {
96 usage(argv[0]);
97 return -1;
98 }
99
100 if ((getuid ()) != 0)
101 printf ("You are not root! This may not work...\n");
102
103
104 if ((kbd_fd = open(kbd_dev, O_RDONLY)) < 0)
105 {
106 printf("Open %s failure: %s", kbd_dev, strerror(errno));
107 return -1;
108 }
109
110 ioctl (kbd_fd, EVIOCGNAME (sizeof (kbd_name)), kbd_name);
111 printf ("Monitor input device %s (%s) event with %s mode:\n", kbd_dev, kbd_name, MODE_POLL==mode?"poll":"infilit loop");
112
113
114 #if 0 /* Not implement in the Linux GPIO button driver */
115 unsigned char key_b[BUTTON_CNT/8 + 1];
116 memset(key_b, 0, sizeof(key_b));
117 if(ioctl(kbd_fd, EVIOCGKEY(sizeof(key_b)), key_b) < 0)
118 {
119 printf("EVIOCGKEY ioctl get error: %s\n", strerror(errno));
120 return -1;
121 }
122 #endif
123
124 #if 0 /* Not implement in the Linux GPIO button driver */
125 /* rep[0]表示在按键重复出现之前 delay的时间,rep[1]表示按键重复出现的时间间隔。 */
126 int rep[2] ={2500, 1000} ;
127 if(ioctl(kbd_fd, EVIOCSREP, rep) < 0)
128 {
129 printf("EVIOCSREP ioctl get error: %s\n", strerror(errno));
130 return -1;
131 }
132
133 if(ioctl(kbd_fd, EVIOCGREP, rep) < 0)
134 {
135 printf("EVIOCGKEY ioctl get error: %s\n", strerror(errno));
136 return -1;
137 }
138 else
139 {
140 printf("repeate speed: [0]= %d, [1] = %d/n", rep[0], rep[1]);
141 }
142 #endif
143
144 while (1)
145 {
146 if(MODE_POLL==mode)
147 {
148 fd_set rds;
149 FD_ZERO(&rds);
150 FD_SET(kbd_fd, &rds);
151
152 rv = select(kbd_fd + 1, &rds, NULL, NULL, NULL);
153 if (rv < 0)
154 {
155 printf("Select() system call failure: %s\n", strerror(errno));
156 goto CleanUp;
157 }
158 else if (FD_ISSET(kbd_fd, &rds))
159 {
160 if ((rv = read (kbd_fd, ev, size*BUTTON_CNT )) < size)
161 {
162 printf("Reading data from kbd_fd failure: %s\n", strerror(errno));
163 break;
164 }
165 else
166 {
167 display_button_event(ev, rv/size);
168 }
169 }
170 }
171 else
172 {
173 if ((rv = read (kbd_fd, ev, size*BUTTON_CNT )) < size)
174 {
175 printf("Reading data from kbd_fd failure: %s\n", strerror(errno));
176 break;
177 }
178 else
179 {
180 display_button_event(ev, rv/size);
181 }
182 }
183 }
184
185 CleanUp:
186 close(kbd_fd);
187
188 return 0;
189 }
190
191 void usage(char *name)
192 {
193 char *progname = NULL;
194 char *ptr = NULL;
195
196 ptr = strdup(name);
197 progname = basename(ptr);
198
199 printf("Usage: %s [-p] -d <device>\n", progname);
200 printf(" -d[device ] button device name\n");
201 printf(" -p[poll ] Use poll mode, or default use infinit loop.\n");
202 printf(" -h[help ] Display this help information\n");
203
204 free(ptr);
205
206 return;
207 }
208
209 void display_button_event(struct input_event *ev, int cnt)
210 {
211 int i;
212 struct timeval pressed_time, duration_time;
213
214 for(i=0; i<cnt; i++)
215 {
216 //printf("type:%d code:%d value:%d\n", ev[i].type, ev[i].code, ev[i].value);
217 if(EV_KEY==ev[i].type && EV_PRESSED==ev[i].value)
218 {
219 if(BTN_1 == ev[i].code)
220 {
221 pressed_time = ev[i].time;
222 printf("S1 button key[%d] pressed time: %ld.%ld\n", ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec);
223 }
224 else if(BTN_2 == ev[i].code)
225 {
226 pressed_time = ev[i].time;
227 printf("S2 button key[%d] pressed time: %ld.%ld\n", ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec);
228 }
229 else if(BTN_3 == ev[i].code)
230 {
231 pressed_time = ev[i].time;
232 printf("S3 button key[%d] pressed time: %ld.%ld\n", ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec);
233 }
234 else if(BTN_4 == ev[i].code)
235 {
236 pressed_time = ev[i].time;
237 printf("S4 button key[%d] pressed time: %ld.%ld\n", ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec);
238 }
239 else
240 {
241 pressed_time = ev[i].time;
242 printf("button key[%d] pressed time: %ld.%ld\n", ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec);
243 }
244 }
245 if(EV_KEY==ev[i].type && EV_RELEASED==ev[i].value)
246 {
247 if(BTN_1 == ev[i].code)
248 {
249 timersub(&ev[i].time, &pressed_time, &duration_time);
250 printf("S1 button key[%d] released time: %ld.%ld\n", ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec);
event_button.c
251 printf("S1 button key[%d] duration time: %ld.%ld\n", ev[i].code, duration_time.tv_sec, duration_time.tv_usec);
252 }
253 else if(BTN_2 == ev[i].code)
254 {
255 timersub(&ev[i].time, &pressed_time, &duration_time);
256 printf("S2 button key[%d] released time: %ld.%ld\n", ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec);
257 printf("S2 button key[%d] duration time: %ld.%ld\n", ev[i].code, duration_time.tv_sec, duration_time.tv_usec);
258 }
259 else if(BTN_3 == ev[i].code)
260 {
261 timersub(&ev[i].time, &pressed_time, &duration_time);
262 printf("S3 button key[%d] released time: %ld.%ld\n", ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec);
263 printf("S3 button key[%d] duration time: %ld.%ld\n", ev[i].code, duration_time.tv_sec, duration_time.tv_usec);
264 }
265 else if(BTN_4 == ev[i].code)
266 {
267 timersub(&ev[i].time, &pressed_time, &duration_time);
268 printf("S4 button key[%d] released time: %ld.%ld\n", ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec);
269 printf("S4 button key[%d] duration time: %ld.%ld\n", ev[i].code, duration_time.tv_sec, duration_time.tv_usec);
270 }
271 else
272 {
273 timersub(&ev[i].time, &pressed_time, &duration_time);
274 printf("button key[%d] released time: %ld.%ld\n", ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec);
275 printf("button key[%d] duration time: %ld.%ld\n", ev[i].code, duration_time.tv_sec, duration_time.tv_usec);
276 }
277 }
278 } /* for(i=0; i<cnt; i++) */
279 }
[fanmaolin@Centeros button]$ make
make[1]: Entering directory `/home/fanmaolin/fl2440/kernel/linux-3.0'
CC [M] /home/fanmaolin/fl2440/driver/button/kbd_device.o
CC [M] /home/fanmaolin/fl2440/driver/button/kbd_driver.o
Building modules, stage 2.
MODPOST 2 modules
CC /home/fanmaolin/fl2440/driver/button/kbd_device.mod.o
LD [M] /home/fanmaolin/fl2440/driver/button/kbd_device.ko
CC /home/fanmaolin/fl2440/driver/button/kbd_driver.mod.o
LD [M] /home/fanmaolin/fl2440/driver/button/kbd_driver.ko
make[1]: Leaving directory `/home/fanmaolin/fl2440/kernel/linux-3.0'
make[1]: Entering directory `/home/fanmaolin/fl2440/driver/button'
make[1]: Leaving directory `/home/fanmaolin/fl2440/driver/button'
make[1]: Entering directory `/home/fanmaolin/fl2440/driver/button'
/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc event_button.c -o event_button
make[1]: Leaving directory `/home/fanmaolin/fl2440/driver/button'
[fanmaolin@Centeros button]$ /opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc event_button.c
[fanmaolin@Centeros button]$ ls
a.out event_button.c kbd_device.ko kbd_driver.h Makefile
event_button kbd_device.c kbd_driver.c kbd_driver.ko
在开发板上操作:
>: insmod kbd_device.ko
S3C keyboard platform device register ok
>: insmod kbd_driver.ko
input: s3c_kbd as /devices/platform/s3c_kbd.1/input/input0
s3c_kbd_probe ok
s3c keyboard platform driver register ok
>: ./a.out -p -d/dev/event0 //按键执行,检测按键
Monitor input device /dev/event0 (s3c_kbd) event with poll mode:
button key[2] pressed time: 65.990097
button key[2] released time: 66.190063
button key[2] duration time: 1091798394.190055
button key[3] pressed time: 68.800086
button key[3] released time: 69.5060
button key[3] duration time: 1091798397.5052
button key[4] pressed time: 70.645089
button key[4] released time: 70.755058
button key[4] duration time: 1091798398.755050
button key[5] pressed time: 73.760093
button key[5] released time: 74.63
button key[5] duration time: 1091798402.55
该测试文件可以显示按键时间
总结分析:
因为涉及到消抖处理,要注意该程序的select()函数及阻塞与非阻塞
if(MODE_POLL==mode)
{
fd_set rds;
FD_ZERO(&rds);
FD_SET(kbd_fd, &rds);
rv = select(kbd_fd + 1, &rds, NULL, NULL, NULL);
if (rv < 0)
{
printf("Select() system call failure: %s\n", strerror(errno));
goto CleanUp;
}
else if (FD_ISSET(kbd_fd, &rds))
{
if ((rv = read (kbd_fd, ev, size*BUTTON_CNT )) < size)
{
printf("Reading data from kbd_fd failure: %s\n", strerror(errno));
break;
}
else
{
display_button_event(ev, rv/size);
}
}
}
else
{
if ((rv = read (kbd_fd, ev, size*BUTTON_CNT )) < size)
{
printf("Reading data from kbd_fd failure: %s\n", strerror(errno));
break;
}
else
{
display_button_event(ev, rv/size);
}
}
我是这样理解阻塞与非阻塞的:
阻塞:当一个函数是阻塞的时,相当僵硬,你得等到事件发生,否则一直堵在这里
非阻塞:不管如何,执行完立刻返回,事件没来也不管。。。
所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高。可是使用Select就可以完成非阻塞方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
select()函数相当于一个监控辅助程序,如read()等阻塞函数,我们在调用read()时,程序会一直阻塞在这里,直到你按下按键,但是cpu时间宝贵,这个事件一直不发生就不能执行其他程序,效率太低,有了select()之后就相当于有个秘书,把阻塞丢给秘书,秘书会在这里判断事件是否发生,只有发生了才会扔给read处理。
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout);
第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set*),将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。
第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。
具体解释select的参数:
int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
fd_set * readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
fd_set * writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
fd_set * errorfds同上面两个参数的意图,用来监视文件错误异常。
struct timeval * timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
返回值:返回状态发生变化的描述符总数。
负值:select错误
正值:某些文件可读写或出错
0:等待超时,没有可读写或错误的文件