【北京迅为】i.MX6ULL终结者Linux INPUT子系统实验编写实验程序

在本实验中使用按键KEY0设备。所以设备树节点不用修改,直接使用前面章节创建的key节点即可。

1 编写驱动程序

本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/17_key_input
创建key_input.c文件,具体内容如下:

  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");

第 48 行,在设备结构体中定义一个 input_dev 指针变量。
第 83~93 行,在按键消抖定时器处理函数中上报输入事件,也就是使用 input_report_key函数上报按键事件以及按键值,最后使用 input_sync 函数上报一个同步事件,这是必须的一步!
第 147~172 行,使用 input_allocate_device 函数申请 input_dev,然后设置相应的事件以及事件码(也就是 KEY 模拟成那个按键,这里我们设置为 KEY_0)。最后使用 input_register_device函数向 Linux 内核注册 input_dev。
第 202、203 行,当注销 input 设备驱动的时候使用 input_unregister_device 函数注销掉前面注册的 input_dev,最后使用 input_free_device 函数释放掉前面申请的 input_dev。

2 应用测试程序

创建key_input_test.c文件,具体内容如下:

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 }

第17行,定义input_event结构体变量,然后使用这个变量获取按键的输入信息。
第56行,当Linux内核注册一个input_dev设备后,会在/dev/input目录下生成一个“eventX(X=0…n)”的文件,这个/dev/input/eventX 就是对应的 input 设备文件。我们读取这个文件就可以获取到输入事件信息,比如按键值什么的。使用read函数读取输入设备文件,也就是/dev/input/eventX,读取到的数据按照 input_event 结构体组织起来。获取到输入事件以后(input_event 结构体类型)使用 switch case 语句来判断事件类型。按键设备设置的事件类型是EV_KEY,然后根据事件值来判断按键是按下的还是松开的。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/BeiJingXunWei/article/details/112213040