[Beijing Xunwei] i.MX6ULL Terminator Linux INPUT subsystem experiment writing experiment program

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.

Insert picture description here

Guess you like

Origin blog.csdn.net/BeiJingXunWei/article/details/112213040