问题

在学习到Linux内核input子系统时,产生了一个疑惑。可以看到,我们改造按键中断驱动程序(请见keyinputdriver.c(内核驱动代码)),通过检测按键的上升沿和下降沿,在中断处理函数(上半部内)通过mod_timer(&dev->timer, jiffies + msecs_to_jiffies(20))函数启动定时器。在定时器处理函数中上报和同步按键事件(EV_KEY和EV_REP)。那么问题来了,驱动中,按键中断是上升沿和下降沿触发,当连按的时候,中断只触发了一次,为什么会一直上报的呢?

keyinputdriver.c(内核驱动代码)点击查看代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.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/irq.h>
#include <linux/of_irq.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/input.h> #define KEYINPUT_CNT 1
#define KEYINPUT_NAME "keyinput"
#define KEY_NUM 1
#define KEY0_VALUE 0X01
#define INVAKEY 0XFF struct irq_keydesc
{
int gpio; /* io编号 */
int irqnum; /* 中断号 */
unsigned char value; /* 键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int, void*); /* 中断处理函数 */ }; struct keyinput_dev
{
struct device_node *nd;
struct irq_keydesc irqkey[KEY_NUM];
struct timer_list timer; struct input_dev *inputdev;
};
struct keyinput_dev keyinput; /* 按键处理函数 */
static irqreturn_t key0_handler(int irq, void *dev_id)
{
struct keyinput_dev *dev = dev_id; dev->timer.data = (volatile unsigned long)dev;
/* 开始定时 */
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(20)); return IRQ_HANDLED;
} /* 定时器处理函数 */
static void timer_func(unsigned long arg)
{
int value = 0;
struct keyinput_dev *dev = (struct keyinput_dev*)arg; value = gpio_get_value(dev->irqkey[0].gpio);
if (0 == value) /* 按下 */
{
// printk("KEY0 press!\r\n");
input_event(dev->inputdev, EV_KEY, KEY_0, 1); }
else if (1 == value) /* 释放 */
{
// printk("KEY0 release!\r\n");
input_event(dev->inputdev, EV_KEY, KEY_0, 0); }
input_sync(dev->inputdev);
} /* 按键初始化 */
static int keyio_init(struct keyinput_dev *dev)
{
int i = 0;
int ret = 0;
/* 按键初始化 */
dev->nd = of_find_node_by_path("/key");
if (NULL == dev->nd)
{
ret = -EINVAL;
goto fail_findnode;
} for (i = 0; i < KEY_NUM; i++)
{
dev->irqkey[i].gpio = of_get_named_gpio(dev->nd, "key-gpios", i);
} for (i = 0; i < KEY_NUM; i++)
{
memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));
sprintf(dev->irqkey[i].name, "KEY%d", i);
gpio_request(dev->irqkey[i].gpio, "key-gpios");
gpio_direction_input(dev->irqkey[i].gpio);
/* 按键中断初始化 */
dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
#if 0
dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd, i);
#endif // 0
} /* 按键中断初始化 */
dev->irqkey[0].handler = key0_handler;
dev->irqkey[0].value = KEY0_VALUE;
for (i = 0; i < KEY_NUM; i++)
{
ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev->irqkey[i].name,
&keyinput);
if (ret)
{
printk("irq %d request failed!\r\n", dev->irqkey[i].irqnum);
goto fail_irq;
}
} /* 定时器初始化 */
init_timer(&dev->timer);
dev->timer.function = timer_func; fail_irq:
for (i = 0; i < KEY_NUM; i++)
{
gpio_free(dev->irqkey[i].gpio);
}
fail_findnode:
return ret;
} static int __init keyinput_init(void)
{
int ret = 0; /* 1、初始化io key */
ret = keyio_init(&keyinput);
if (ret < 0)
{
goto fail_keyinit;
} /* 2、注册input_dev*/
keyinput.inputdev = input_allocate_device();
if (NULL == keyinput.inputdev)
{
ret = -EINVAL;
goto fail_keyinit;
} keyinput.inputdev->name = KEYINPUT_NAME;
__set_bit(EV_KEY, keyinput.inputdev->evbit); //设置按键事件
__set_bit(EV_REP, keyinput.inputdev->evbit); //设置重复事件
__set_bit(KEY_0, keyinput.inputdev->keybit); //设置key0 ret = input_register_device(keyinput.inputdev);
if (ret)
{
goto fail_input_register;
} return 0;
fail_input_register:
input_free_device(keyinput.inputdev);
fail_keyinit:
return ret;
} static void __exit keyinput_exit(void)
{
int i = 0;
/* 1、删除定时器 */
del_timer_sync(&keyinput.timer); /* 2、释放中断 */
for (i = 0; i < KEY_NUM; i++)
{
free_irq(keyinput.irqkey[i].irqnum, &keyinput);
}
/* 3、释放GPIO */
for (i = 0; i < KEY_NUM; i++)
{
gpio_free(keyinput.irqkey[i].gpio);
} /* 4、注销input_dev */
input_unregister_device(keyinput.inputdev);
input_free_device(keyinput.inputdev);
} module_init(keyinput_init);
module_exit(keyinput_exit); MODULE_AUTHOR("tyler");
MODULE_LICENSE("GPL");
keyinputAPP.c(测试APP代码)点击查看代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/input.h>
/*
* ./keyinputAPP <filename> <0/1>
* ./keyinputAPP /dev/input/event2
*
*/ static struct input_event inputevent; int main(int argc, char* argv[])
{
int fd, err;
char * filename; if (argc != 2)
{
printf("args num error!!\r\n");
return -1;
} filename = argv[1]; fd = open(filename, O_RDWR);
if (fd < 0)
{
printf("open error\r\n");
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");
} printf("EV_KEY事件\r\n");
break;
case EV_SYN: break;
case EV_REL:
break;
case EV_ABS:
break;
}; }
else
{
printf("读取失败!\r\n");
} } close(fd);
return 0;
}

input输入系统实现按键重复的方式

简单来说,在input的子系统中,实现按键重复的方式是启动了一个内核定时器,通过保存按键值和上一次的值进行比较来检测按键是否按下。如果没检测到释放按键,那么就会不断地利用mod_timer来启动定时器,进行按键重复上报。当释放了按键,那么就不会重复启动定时器,即停止了按键重复事件。

那就清晰了。我们的驱动程序中,检测到了按键上升沿中断后,EV_REP事件在没用检测到按键键值改变(也就是下降沿中断),就默认开启内核定时器,便会一直上报重复事件EV_REP了。

参考资料:https://blog.51cto.com/assassinwu/1080111 (input输入系统中是如何实现按键重复 )

关于正点原子input子系统,驱动中按键中断只检测了上升或下降沿却可以实现连按(EV_REP)的原因的更多相关文章

  1. input子系统驱动学习之中的一个

        刚開始学习linux这门课就被分配编写一个设备的input子系统驱动.这对我的确有点困难.只是实际的操作中发现困难远比我想象的要大的多.本以为依照老师课上的步骤就行非常快的完毕这项任务.后来发 ...

  2. input子系统驱动

    input子系统驱动 框架分析 核心层 文件为:/drivers/input/input.c: 首先找到入口函数为**static int __init input_init(void)**,在该函数 ...

  3. Linux 驱动框架---驱动中的中断

    在单片机开发中中断就是执行过程中发生了一些事件需要及时处理,所以需要停止当前正在运行的处理的事情转而去执行中断服务函数,已完成必要的事件的处理.在Linux中断一样是如此使用但是基于常见的中断控制器的 ...

  4. STM32中按键中断分析

    在按键学习中,我们有用到查询的方法来判断按键事件是否发生,这种查询按键事件适用于程序工作量较少的情况下,一旦程序中工作量较大较多,则势必影响程序运行的效率,为了简化程序中控制的功能模块的执行时间,引入 ...

  5. input子系统

    input子系统:      像按键.键盘.鼠标.触摸屏.游戏摇杆等设备只有输入没有输出,而且在编程实现其对应的驱动程序时会有很多重复性的代码,内核的设计者将该部分代码抽象出来,驱动工程师只需要复用该 ...

  6. Input子系统(二)【转】

    转自:http://blog.chinaunix.net/uid-25047042-id-4192368.html 上一篇中粗略的分析了下input_dev,input_handle,input_ha ...

  7. 嵌入式C语言自我修养 02:Linux 内核驱动中的指定初始化

    2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: ] = {,,,,,,,,}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a ...

  8. input子系统学习笔记六 按键驱动实例分析下【转】

    转自:http://blog.chinaunix.net/uid-20776117-id-3212095.html 本文接着input子系统学习笔记五 按键驱动实例分析上接续分析这个按键驱动实例! i ...

  9. 【驱动】input子系统全面分析

    初识linux输入子系统 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputC ...

  10. 基于input子系统的sensor驱动调试(二)

    继上一篇:http://www.cnblogs.com/linhaostudy/p/8303628.html#_label1_1 一.驱动流程解析: 1.模块加载: static struct of_ ...

随机推荐

  1. 有分类有tag

    1 2

  2. 库卡KUKA机器人KRC2示教器维修常见方法

    库卡KUKA机器人以稳定性而备受赞誉.作为其重要组成部分,KRC2示教器在机器人的编程.监控和调试过程中发挥着至关重要的作用.然而,就像其他任何电子设备一样,KRC2示教器在长期使用过程中也可能会遇到 ...

  3. Deepseek深度求索教程:从入门到精通,免费获取清华大学新闻学院104页完整指南

    在当今信息爆炸的时代,如何高效地获取和利用知识成为了每个人面临的挑战.Deepseek深度求索作为一款强大的信息检索工具,正逐渐成为学术界和专业人士的首选.为了帮助大家更好地掌握Deepseek的使用 ...

  4. Hadoop - HDFS 概述

    什么是HDFS HDFS的优缺点 HDFS的文件块大小 HDFS的写数据流程 HDFS的副本配置策略 HDFS读数据的流程 什么是HDFS HDFS(Hadoop Distributed File S ...

  5. 【渗透测试】Vulnhub GROTESQUE 1.0.1

    渗透环境 攻击机:   IP: 192.168.10.18(Kali) 靶机:     IP:192.168.10.9 靶机下载地址:https://www.vulnhub.com/entry/gro ...

  6. MySQL索引最左原则:从原理到实战的深度解析

    MySQL索引最左原则:从原理到实战的深度解析 一.什么是索引最左原则? 索引最左原则是MySQL复合索引使用的核心规则,简单来说: "当使用复合索引(多列索引)时,查询条件必须从索引的最左 ...

  7. Ctfhub-SSRF部分做题记录

    Ctfhub-SSRF部分做题记录 上传文件 提示:这次需要上传一个文件到flag.php了.祝你好运 进入flag.php 发现没有提交按钮 修改源代码,加个提交按钮 抓包 修改host为127.0 ...

  8. JavaScript与jQuery基础入门到放弃

    JavaScript与jQuery基础入门到放弃 引言: - BOM 操作 - DOM 操作 - jQuery 类库 BOM 操作 BOM (Browser Object Model) 指浏览器对象模 ...

  9. 关于普通程序员该如何参与AI学习的三个建议以及自己的实践

    大部分程序员在学习大语言模型的时候都比较痛苦,感觉AI是如此之近又如此之远,仿佛能搞明白一点,又好像什么也没明白.就像我们在很远的地方看珠穆拉玛峰,感觉它就像一个不大的山包,感觉只要自己做足准备咬咬牙 ...

  10. Shell语言编程(炼气)

    1. Shell脚本执行方式 执行方式 应用及场景 通过sh或bash 书写脚本后,最常用的方式,在其他非红帽系统中,建议使用bash运行脚本 通过.点或source 加载/生效配置文件(环境变量,别 ...