Article Directory
In this experiment, use the KEY0 device. Therefore, the device tree node does not need to be modified, just use the key node created in the previous chapter.
1 Write the driver
The path of the experiment routine: i.MX6UL Terminator CD-ROM data/06_Linux driver routine/17_key_input to
create the key_input.c file, the specific content is as follows:
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <linux/of_gpio.h>
14 #include <linux/input.h>
15 #include <linux/semaphore.h>
16 #include <linux/timer.h>
17 #include <linux/of_irq.h>
18 #include <linux/irq.h>
19 #include <asm/mach/map.h>
20 #include <asm/uaccess.h>
21 #include <asm/io.h>
22
23 #define KEYINPUT_CNT 1 /* 设备号个数 */
24 #define KEYINPUT_NAME "keyinput" /* 名字 */
25 #define KEY0VALUE 0X01 /* KEY0按键值 */
26 #define INVAKEY 0XFF /* 无效的按键值 */
27 #define KEY_NUM 1 /* 按键数量 */
28
29 /* 中断IO描述结构体 */
30 struct irq_keydesc {
31 int gpio; /* gpio */
32 int irqnum; /* 中断号*/
33 unsigned char value; /* 按键对应的键值 */
34 char name[10]; /* 名字 */
35 irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
36 };
37
38 /* keyinput设备结构体 */
39 struct keyinput_dev{
40 dev_t devid; /* 设备号*/
41 struct cdev cdev; /* cdev*/
42 struct class *class; /* 类 */
43 struct device *device; /* 设备 */
44 struct device_node *nd; /* 设备节点 */
45 struct timer_list timer;/* 定义一个定时器*/
46 struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键描述数组 */
47 unsigned char curkeynum; /* 当前的按键号 */
48 struct input_dev *inputdev; /* input结构体 */
49 };
50
51 struct keyinput_dev keyinputdev; /* key input设备 */
52
53 /* @description : 中断服务函数,开启定时器,延时10ms,
54 * 定时器用于按键消抖。
55 * @param - irq : 中断号
56 * @param - dev_id : 设备结构。
57 * @return : 中断执行结果
58 */
59 static irqreturn_t key0_handler(int irq, void *dev_id)
60 {
61 struct keyinput_dev *dev = (struct keyinput_dev *)dev_id;
62
63 dev->curkeynum = 0;
64 dev->timer.data = (volatile long)dev_id;
65 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
66 return IRQ_RETVAL(IRQ_HANDLED);
67 }
68
69 /* @description : 定时器服务函数,用于按键消抖,定时器到了以后
70 * 再次读取按键值,如果按键还是处于按下状态就表示按键有效。
71 * @param - arg : 设备结构变量
72 * @return : 无
73 */
74 void timer_function(unsigned long arg)
75 {
76 unsigned char value;
77 unsigned char num;
78 struct irq_keydesc *keydesc;
79 struct keyinput_dev *dev = (struct keyinput_dev *)arg;
80
81 num = dev->curkeynum;
82 keydesc = &dev->irqkeydesc[num];
83 value = gpio_get_value(keydesc->gpio); /* 读取IO值 */
84 if(value == 0){
/* 按下按键 */
85 /* 上报按键值 */
86 //input_event(dev->inputdev, EV_KEY, keydesc->value, 1);
87 input_report_key(dev->inputdev, keydesc->value, 1);/* 最后一个
参数表示按下还是松开,1为按下,0为松开 */
88 input_sync(dev->inputdev);
89 } else {
/* 按键松开 */
90 //input_event(dev->inputdev, EV_KEY, keydesc->value, 0);
91 input_report_key(dev->inputdev, keydesc->value, 0);
92 input_sync(dev->inputdev);
93 }
94 }
95
96 /*
97 * @description : 按键IO初始化
98 * @param : 无
99 * @return : 无
100 */
101 static int keyio_init(void)
102 {
103 unsigned char i = 0;
104 char name[10];
105 int ret = 0;
106
107 keyinputdev.nd = of_find_node_by_path("/key");
108 if (keyinputdev.nd== NULL){
109 printk("key node not find!\r\n");
110 return -EINVAL;
111 }
112
113 /* 提取GPIO */
114 for (i = 0; i < KEY_NUM; i++) {
115 keyinputdev.irqkeydesc[i].gpio = of_get_named_gpio(keyinputdev.nd ,
"key-gpio", i);
116 if (keyinputdev.irqkeydesc[i].gpio < 0) {
117 printk("can't get key%d\r\n", i);
118 }
119 }
120
121 /* 初始化key所使用的IO,并且设置成中断模式 */
122 for (i = 0; i < KEY_NUM; i++) {
123 memset(keyinputdev.irqkeydesc[i].name, 0,
sizeof(name)); /* 缓冲区清零 */
124 sprintf(keyinputdev.irqkeydesc[i].name,
"KEY%d", i); /* 组合名字 */
125 gpio_request(keyinputdev.irqkeydesc[i].gpio, name);
126 gpio_direction_input(keyinputdev.irqkeydesc[i].gpio);
127 keyinputdev.irqkeydesc[i].irqnum = irq_of_parse_and_map(keyinputdev.nd, i);
128 }
129 /* 申请中断 */
130 keyinputdev.irqkeydesc[0].handler = key0_handler;
131 keyinputdev.irqkeydesc[0].value = KEY_0;
132
133 for (i = 0; i < KEY_NUM; i++) {
134 ret = request_irq(keyinputdev.irqkeydesc[i].irqnum,
keyinputdev.irqkeydesc[i].handler,
135 IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
keyinputdev.irqkeydesc[i].name, &keyinputdev);
136 if(ret < 0){
137 printk("irq %d request failed!\r\n",
keyinputdev.irqkeydesc[i].irqnum);
138 return -EFAULT;
139 }
140 }
141
142 /* 创建定时器 */
143 init_timer(&keyinputdev.timer);
144 keyinputdev.timer.function = timer_function;
145
146 /* 申请input_dev */
147 keyinputdev.inputdev = input_allocate_device();
148 keyinputdev.inputdev->name = KEYINPUT_NAME;
149 #if 0
150 /* 初始化input_dev,设置产生哪些事件 */
151 __set_bit(EV_KEY, keyinputdev.inputdev->evbit); /* 设置产生按键事件*/
152 __set_bit(EV_REP, keyinputdev.inputdev->evbit); /* 重复事件,比如按
下去不放开,就会一直输出信息 */
153
154 /* 初始化input_dev,设置产生哪些按键 */
155 __set_bit(KEY_0, keyinputdev.inputdev->keybit);
156 #endif
157
158 #if 0
159 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
160 keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
161 #endif
162
163 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
164 input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
165
166 /* 注册输入设备 */
167 ret = input_register_device(keyinputdev.inputdev);
168 if (ret) {
169 printk("register input device failed!\r\n");
170 return ret;
171 }
172 return 0;
173 }
174
175 /*
176 * @description : 驱动入口函数
177 * @param : 无
178 * @return : 无
179 */
180 static int __init keyinput_init(void)
181 {
182 keyio_init();
183 return 0;
184 }
185
186 /*
187 * @description : 驱动出口函数
188 * @param : 无
189 * @return : 无
190 */
191 static void __exit keyinput_exit(void)
192 {
193 unsigned int i = 0;
194 /* 删除定时器 */
195 del_timer_sync(&keyinputdev.timer); /* 删除定时器 */
196
197 /* 释放中断 */
198 for (i = 0; i < KEY_NUM; i++) {
199 free_irq(keyinputdev.irqkeydesc[i].irqnum, &keyinputdev);
200 }
201 /* 释放input_dev */
202 input_unregister_device(keyinputdev.inputdev);
203 input_free_device(keyinputdev.inputdev);
204 }
205
206 module_init(keyinput_init);
207 module_exit(keyinput_exit);
208 MODULE_LICENSE("GPL");
209 MODULE_AUTHOR("topeet");
In line 48, an input_dev pointer variable is defined in the device structure.
In lines 83~93, the input event is reported in the key debounce timer processing function, that is, the input_report_key function is used to report the key event and key value, and finally the input_sync function is used to report a synchronization event. This is a necessary step!
Lines 147~172, use the input_allocate_device function to apply for input_dev, and then set the corresponding event and event code (that is, KEY is simulated as that button, here we set it to KEY_0). Finally, use the input_register_device function to register input_dev with the Linux kernel.
On lines 202 and 203, when unregistering the input device driver, use the input_unregister_device function to unregister the previously registered input_dev, and finally use the input_free_device function to release the previously applied input_dev.
2 Application test program
Create the key_input_test.c file, the specific content is as follows:
1 #include "stdio.h"
2 #include "unistd.h"
3 #include "sys/types.h"
4 #include "sys/stat.h"
5 #include "sys/ioctl.h"
6 #include "fcntl.h"
7 #include "stdlib.h"
8 #include "string.h"
9 #include <poll.h>
10 #include <sys/select.h>
11 #include <sys/time.h>
12 #include <signal.h>
13 #include <fcntl.h>
14 #include <linux/input.h>
15
16 /* 定义一个input_event变量,存放输入事件信息 */
17 static struct input_event inputevent;
18
19 /*
20 * @description : main主程序
21 * @param - argc : argv数组元素个数
22 * @param - argv : 具体参数
23 * @return : 0 成功;其他 失败
24 */
25 int main(int argc, char *argv[])
26 {
27 int fd;
28 int err = 0;
29 char *filename;
30
31 filename = argv[1];
32
33 if(argc != 2) {
34 printf("Error Usage!\r\n");
35 return -1;
36 }
37
38 fd = open(filename, O_RDWR);
39 if (fd < 0) {
40 printf("Can't open file %s\r\n", filename);
41 return -1;
42 }
43
44 while (1) {
45 err = read(fd, &inputevent, sizeof(inputevent));
46 if (err > 0) {
/* 读取数据成功 */
47 switch (inputevent.type) {
48
49
50 case EV_KEY:
51 if (inputevent.code < BTN_MISC) {
/* 键盘键值 */
52 printf("key %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
53 } else {
54 printf("button %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
55 }
56 break;
57
58 /* 其他类型的事件,自行处理 */
59 case EV_REL:
60 break;
61 case EV_ABS:
62 break;
63 case EV_MSC:
64 break;
65 case EV_SW:
66 break;
67 }
68 } else {
69 printf("读取数据失败\r\n");
70 }
71 }
72 return 0;
73 }
In line 17, define the input_event structure variable, and then use this variable to get the input information of the key.
On line 56, when the Linux kernel registers an input_dev device, it will generate an "eventX(X=0...n)" file in the /dev/input directory. This /dev/input/eventX is the corresponding input device file. We can read this file to get input event information, such as key values. Use the read function to read the input device file, which is /dev/input/eventX, and the read data is organized according to the input_event structure. After obtaining the input event (input_event structure type), use the switch case statement to determine the event type. The event type set by the key device is EV_KEY, and then it is judged whether the key is pressed or released according to the event value.