1 前言
前面把按键中断做了之后, 目前要添加到输入子系统中, 这样就可以制作矩阵键盘之类的
https://blog.csdn.net/a2267542848/article/details/119669903
2 驱动代码
需要注意的是一定要设置input_set_capability(imx6uirq.inputdev, EV_KEY, key_value[4]);这个代表输入子系统会上报的按键事件, 如果没有设置这个. 那么上报的按键是无效的
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define KEY_NUM 5 /* 按键数量 */
#define IMX6UIRQ_NAME "/input_keys" /* 名字 */
#define IMX6UIRQ_PROPETY_NAME "gpios"
#define INVAKEY 0XFF /* 无效的按键值 */
char key_value[] = {KEY_0,KEY_1,KEY_2,KEY_3,KEY_4};
/* 中断IO描述结构体 */
struct irq_keydesc {
int gpio; /* gpio */
int irqnum; /* 中断号 */
unsigned char value; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
};
/* imx6uirq设备结构体 */
struct imx6uirq_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
struct timer_list timer;/* 定义一个定时器*/
struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键init述数组 */
unsigned char curkeynum; /* 当前init按键号 */
struct input_dev *inputdev; /* input结构体 */
};
struct imx6uirq_dev imx6uirq; /* irq设备 */
/* @description : 中断服务函数,开启定时器
* 定时器用于按键消抖。
* @param - irq : 中断号
* @param - dev_id : 设备结构。
* @return : 中断执行结果
*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{
struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;
dev->curkeynum = 0;
dev->timer.data = (volatile long)dev_id;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t key1_handler(int irq, void *dev_id)
{
struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;
dev->curkeynum = 1;
dev->timer.data = (volatile long)dev_id;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t key2_handler(int irq, void *dev_id)
{
struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;
dev->curkeynum = 2;
dev->timer.data = (volatile long)dev_id;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t key3_handler(int irq, void *dev_id)
{
struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;
dev->curkeynum = 3;
dev->timer.data = (volatile long)dev_id;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t key4_handler(int irq, void *dev_id)
{
struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;
dev->curkeynum = 4;
dev->timer.data = (volatile long)dev_id;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
return IRQ_RETVAL(IRQ_HANDLED);
}
/* @description : 定时器服务函数,用于按键消抖,定时器到了以后
* 再次读取按键值,如果按键还是处于按下状态就表示按键有效。
* @param - arg : 设备结构变量
* @return : 无
*/
void timer_function(unsigned long arg)
{
unsigned char value;
unsigned char num;
struct irq_keydesc *keydesc;
struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;
num = dev->curkeynum;
keydesc = &dev->irqkeydesc[num];
value = gpio_get_value(keydesc->gpio); /* 读取IO值 */
printk("input:%d %d\r\n",keydesc->gpio,keydesc->value);
if(value == 0){ /* 按下按键 */
/* 上报按键值 */
//input_event(dev->inputdev, EV_KEY, keydesc->value, 1);
input_report_key(dev->inputdev, keydesc->value, 1);/* 最后一个参数表示按下还是松开,1为按下,0为松开 */
input_sync(dev->inputdev);
} else { /* 按键松开 */
//input_event(dev->inputdev, EV_KEY, keydesc->value, 0);
input_report_key(dev->inputdev, keydesc->value, 0);
input_sync(dev->inputdev);
}
}
/*
* @description : 按键IO初始化
* @param : 无
* @return : 无
*/
static int keyio_init(void)
{
unsigned char i = 0;
char name[10];
int ret = 0;
// 找节点
imx6uirq.nd = of_find_node_by_path(IMX6UIRQ_NAME);
if (imx6uirq.nd== NULL){
printk("key node not find!\r\n");
return -EINVAL;
}
// 中断函数
imx6uirq.irqkeydesc[0].handler = key0_handler;
imx6uirq.irqkeydesc[1].handler = key1_handler;
imx6uirq.irqkeydesc[2].handler = key2_handler;
imx6uirq.irqkeydesc[3].handler = key3_handler;
imx6uirq.irqkeydesc[4].handler = key4_handler;
/* 提取GPIO */
struct device_node *cnp;
struct device_node *prev = NULL;
for(i=0;i<KEY_NUM;i++){
cnp = of_get_next_child(imx6uirq.nd, prev);
if(cnp==NULL){
break;
}
prev = cnp;
// 获取GPIO
imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(cnp, IMX6UIRQ_PROPETY_NAME, 0);
memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(name)); /* 缓冲区清零 */
sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i); /* 组合名字 */
gpio_request(imx6uirq.irqkeydesc[i].gpio, name);
gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);
// 获取中断号
imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(cnp, 0);
// 设置中段值
imx6uirq.irqkeydesc[i].value = key_value[i];
/* 申请中断 */
ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, &imx6uirq);
if(ret < 0){
printk("irq ret: %d %d request failed!\r\n", ret,imx6uirq.irqkeydesc[i].irqnum);
return -EFAULT;
}
}
// not calc all
if(i!=KEY_NUM){
printk("node find not enought!\r\n");
return -1;
}
return 0;
}
/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init imx6uirq_init(void)
{
int ret = 0;
keyio_init();
/* 创建定时器 */
init_timer(&imx6uirq.timer);
imx6uirq.timer.function = timer_function;
/* 申请input_dev */
imx6uirq.inputdev = input_allocate_device();
imx6uirq.inputdev->name = "keyinput";
//设置输入事件类型
imx6uirq.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
// 设置设备能触发的事件按键
input_set_capability(imx6uirq.inputdev, EV_KEY, key_value[0]);
input_set_capability(imx6uirq.inputdev, EV_KEY, key_value[1]);
input_set_capability(imx6uirq.inputdev, EV_KEY, key_value[2]);
input_set_capability(imx6uirq.inputdev, EV_KEY, key_value[3]);
input_set_capability(imx6uirq.inputdev, EV_KEY, key_value[4]);
/* 注册输入设备 */
ret = input_register_device(imx6uirq.inputdev);
if (ret) {
printk("register input device failed!\r\n");
return ret;
}
return 0;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit imx6uirq_exit(void)
{
unsigned i = 0;
/* 删除定时器 */
del_timer_sync(&imx6uirq.timer); /* 删除定时器 */
/* 释放中断 */
for (i = 0; i < KEY_NUM; i++) {
free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);
}
/* 释放input_dev */
input_unregister_device(imx6uirq.inputdev);
input_free_device(imx6uirq.inputdev);
}
module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
3 应用
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/input.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : keyinputApp.c
作者 : 左忠凯
版本 : V1.0
描述 : input子系统测试APP。
其他 : 无
使用方法 :./keyinputApp /dev/input/event1
论坛 : www.openedv.com
日志 : 初版V1.0 2019/8/26 左忠凯创建
***************************************************************/
/* 定义一个input_event变量,存放输入事件信息 */
static struct input_event inputevent;
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd;
int err = 0;
char *filename;
filename = argv[1];
if(argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
while (1) {
err = read(fd, &inputevent, sizeof(inputevent));
if (err > 0) { /* 读取数据成功 */
switch (inputevent.type) {
case EV_KEY:
if (inputevent.code < BTN_MISC) { /* 键盘键值 */
printf("key %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
} else {
printf("button %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
}
break;
/* 其他类型的事件,自行处理 */
case EV_REL:
break;
case EV_ABS:
break;
case EV_MSC:
break;
case EV_SW:
break;
}
} else {
printf("读取数据失败\r\n");
}
}
return 0;
}
4 测试