commit | author | age
|
1e563e
|
1 |
/********************************************************************************* |
G |
2 |
* Copyright: (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292> |
|
3 |
* All rights reserved. |
|
4 |
* |
|
5 |
* Filename: keypad.c |
|
6 |
* Description: This file used to test GPIO button driver builtin Linux kernel |
|
7 |
* |
|
8 |
* Version: 1.0.0(11/17/2021~) |
|
9 |
* Author: Guo Wenxue <guowenxue@gmail.com> |
|
10 |
* ChangeLog: 1, Release initial version on "11/17/2021 02:46:18 PM" |
|
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 EV_RELEASED 0 |
|
40 |
#define EV_PRESSED 1 |
|
41 |
|
|
42 |
#define BUTTON_CNT 10 |
|
43 |
|
|
44 |
/* 在C语言编程中,函数应该先定义再使用,如果函数的定义在函数调用后面,应该前向声明。*/ |
|
45 |
void usage(char *name); |
|
46 |
|
|
47 |
void display_button_event(struct input_event *ev, int cnt); |
|
48 |
|
|
49 |
int main(int argc, char **argv) |
|
50 |
{ |
|
51 |
char *kbd_dev = "/dev/input/event1"; //默认监听按键设备; |
|
52 |
char kbd_name[256] = "Unknown"; //用于保存获取到的设备名称 |
|
53 |
int kbd_fd = -1; //open()打开文件的文件描述符 |
|
54 |
int rv=0; // 函数返回值,默认返回0; |
|
55 |
int opt; // getopt_long 解析命令行参数返回值; |
|
56 |
int size = sizeof (struct input_event); |
|
57 |
fd_set rds; //用于监听的事件的集合 |
|
58 |
|
|
59 |
struct input_event ev[BUTTON_CNT]; |
|
60 |
|
|
61 |
/* getopt_long参数函数第四个参数的定义,二维数组,每个成员由四个元素组成 */ |
|
62 |
struct option long_options[] = { |
|
63 |
/* { 参数名称,是否带参数,flags指针(NULL时将val的数值从getopt_long的返回值返回出去), |
|
64 |
函数找到该选项时的返回值(字符)} |
|
65 |
*/ |
|
66 |
{"device", required_argument, NULL, 'd'}, |
|
67 |
{"help", no_argument, NULL, 'h'}, |
|
68 |
{NULL, 0, NULL, 0} |
|
69 |
}; |
|
70 |
|
|
71 |
//获取命令行参数的解析返回值 |
|
72 |
while ((opt = getopt_long(argc, argv, "d:h", long_options, NULL)) != -1) |
|
73 |
{ |
|
74 |
switch (opt) |
|
75 |
{ |
|
76 |
case 'd': |
|
77 |
kbd_dev = optarg; |
|
78 |
break; |
|
79 |
|
|
80 |
case 'h': |
|
81 |
usage(argv[0]); |
|
82 |
return 0; |
|
83 |
|
|
84 |
default: |
|
85 |
break; |
|
86 |
} |
|
87 |
} |
|
88 |
|
|
89 |
if(NULL == kbd_dev) |
|
90 |
{ |
|
91 |
/* 命令行argv[0]是输入的命令,如 ./keypad */ |
|
92 |
usage(argv[0]); |
|
93 |
return -1; |
|
94 |
} |
|
95 |
|
|
96 |
/* 获取uid 建议以root权限运行确保可以正常运行 */ |
|
97 |
if ((getuid ()) != 0) |
|
98 |
printf ("You are not root! This may not work...\n"); |
|
99 |
|
|
100 |
/* 打开按键对应的设备节点,如果错误则返回负数 */ |
|
101 |
if ((kbd_fd = open(kbd_dev, O_RDONLY)) < 0) |
|
102 |
{ |
|
103 |
printf("Open %s failure: %s", kbd_dev, strerror(errno)); |
|
104 |
return -1; |
|
105 |
} |
|
106 |
|
|
107 |
/* 使用ioctl获取 /dev/input/event*对应的设备名字 */ |
|
108 |
ioctl (kbd_fd, EVIOCGNAME (sizeof (kbd_name)), kbd_name); |
|
109 |
printf ("Monitor input device %s (%s) event on poll mode:\n", kbd_dev, kbd_name); |
|
110 |
|
|
111 |
/* 循环使用 select() 多路复用监听按键事件 */ |
|
112 |
while (1) |
|
113 |
{ |
|
114 |
FD_ZERO(&rds); /* 清空 select() 的读事件集合 */ |
|
115 |
FD_SET(kbd_fd, &rds); /* 将按键设备的文件描述符加入到读事件集合中*/ |
|
116 |
|
|
117 |
/* 使用select开启监听并等待多个描述符发生变化,第一个参数最大描述符+1, |
|
118 |
2、3、4参数分别是要监听读、写、异常三个事件的文军描述符集合; |
|
119 |
最后一个参数是超时时间(NULL-->永不超时,会一直阻塞住) |
|
120 |
|
|
121 |
如果按键没有按下,则程序一直阻塞在这里。一旦按键按下,则按键设备有数据 |
|
122 |
可读,此时函数将返回。 |
|
123 |
*/ |
|
124 |
rv = select(kbd_fd + 1, &rds, NULL, NULL, NULL); |
|
125 |
if (rv < 0) |
|
126 |
{ |
|
127 |
printf("Select() system call failure: %s\n", strerror(errno)); |
|
128 |
goto CleanUp; |
|
129 |
} |
|
130 |
else if (FD_ISSET(kbd_fd, &rds)) /* 是按键设备发生了事件 */ |
|
131 |
{ |
|
132 |
//read读取input设备的数据包,数据包为input_event结构体类型。 |
|
133 |
if ((rv = read (kbd_fd, ev, size*BUTTON_CNT )) < size) |
|
134 |
{ |
|
135 |
printf("Reading data from kbd_fd failure: %s\n", strerror(errno)); |
|
136 |
break; |
|
137 |
} |
|
138 |
else |
|
139 |
{ |
|
140 |
display_button_event(ev, rv/size); |
|
141 |
} |
|
142 |
} |
|
143 |
} |
|
144 |
|
|
145 |
CleanUp: |
|
146 |
close(kbd_fd); |
|
147 |
|
|
148 |
return 0; |
|
149 |
} |
|
150 |
|
|
151 |
/* 该函数用来打印程序的使用方法 */ |
|
152 |
void usage(char *name) |
|
153 |
{ |
|
154 |
char *progname = NULL; |
|
155 |
char *ptr = NULL; |
|
156 |
|
|
157 |
/* 字符串拷贝函数,该函数内部将调用malloc()来动态分配内存,然后将$name |
|
158 |
字符串内容拷贝到malloc分配的内存中,这样使用完之后需要free释放内存. */ |
|
159 |
ptr = strdup(name); |
|
160 |
progname = basename(ptr); //去除该可执行文件的路径名,获取其自身名称(即keypad) |
|
161 |
|
|
162 |
printf("Usage: %s [-p] -d <device>\n", progname); |
|
163 |
printf(" -d[device ] button device name\n"); |
|
164 |
printf(" -p[poll ] Use poll mode, or default use infinit loop.\n"); |
|
165 |
printf(" -h[help ] Display this help information\n"); |
|
166 |
|
|
167 |
free(ptr); //和strdup对应,释放该内存 |
|
168 |
return; |
|
169 |
} |
|
170 |
|
|
171 |
/* 该函数用来解析按键设备上报的数据,并答应按键按下的相关信息 */ |
|
172 |
void display_button_event(struct input_event *ev, int cnt) |
|
173 |
{ |
|
174 |
int i; |
|
175 |
static struct timeval pressed_time; //该变量用来存放按键按下的时间,注意static的使用。 |
|
176 |
struct timeval duration_time; //该变量用来存放按键按下持续时间 |
|
177 |
|
|
178 |
for(i=0; i<cnt; i++) |
|
179 |
{ |
|
180 |
/* 当上报的时间type为EV_KEY时候并且,value值为1或0 (1为按下,0为释放) */ |
|
181 |
if(EV_KEY==ev[i].type && EV_PRESSED==ev[i].value) |
|
182 |
{ |
|
183 |
pressed_time = ev[i].time; |
|
184 |
printf("Keypad[%d] pressed time: %ld.%ld\n", |
|
185 |
ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec); |
|
186 |
} |
|
187 |
if(EV_KEY==ev[i].type && EV_RELEASED==ev[i].value) |
|
188 |
{ |
|
189 |
/* 计算时间差函数 将第一个参数减去第二个参数的值的结果 放到第三个参数之中 */ |
|
190 |
timersub(&ev[i].time, &pressed_time, &duration_time); |
|
191 |
printf("keypad[%d] released time: %ld.%ld\n", |
|
192 |
ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec); |
|
193 |
printf("keypad[%d] duration time: %ld.%ld\n", |
|
194 |
ev[i].code, duration_time.tv_sec, duration_time.tv_usec); |
|
195 |
} |
|
196 |
} |
|
197 |
} |