#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/ide.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/signal.h>
#include "chrdev_key.h"
#include "imx6ull_key.h"
static irqreturn_t imx6ull_key_interrupt(int irq, void *dev)
{
struct imx6ull_key_info *key_info = (struct imx6ull_key_info *)dev;
//启动消抖定时器
mod_timer(&key_info->timer_press, jiffies + msecs_to_jiffies(KEY_STABLE_INTERVAL_MS));
return IRQ_HANDLED;
}
static void key_press_timer_func(unsigned long arg)
{
int level;
struct imx6ull_key_info *key_info = (struct imx6ull_key_info *)arg;
//读取按键状态
level = gpiod_get_value(key_info->desc);
switch(key_info->pstatus)
{
case KEY_STATE_INITIAL: {
if(level)
key_info->pstatus = KEY_STATE_PRESSED;
else
key_info->pstatus = KEY_STATE_RELEASED;
}break;
case KEY_STATE_PRESSED: {
if(!level) {
key_info->pstatus = KEY_STATE_RELEASED;
key_info->lpstatus = 0;
//删除长按检测定时器
del_timer(&key_info->timer_lpress);
}
}break;
case KEY_STATE_RELEASED: {
if(level) {
key_info->pstatus = KEY_STATE_PRESSED;
key_info->pevents++;
wake_up_interruptible(&key_info->key_waitqueue);
kill_fasync(&key_info->key_async_queue, SIGIO, POLL_IN | POLLRDNORM);
//启动长按检测定时器
mod_timer(&key_info->timer_lpress, jiffies + msecs_to_jiffies(KEY_LONG_PRESS_INTERVAL_MS));
}
}break;
default: break;
}
}
static void key_long_press_timer_func(unsigned long arg)
{
struct imx6ull_key_info *key_info = (struct imx6ull_key_info *)arg;
key_info->lpstatus = 1;
wake_up_interruptible(&key_info->key_waitqueue);
kill_fasync(&key_info->key_async_queue, SIGIO, POLL_IN | POLLRDNORM);
}
static int imx6ull_key_probe(struct platform_device *pdev)
{
int ret;
struct imx6ull_key_info *key_info;
if(!of_device_is_available(pdev->dev.of_node))
return -ENODEV;
//申请内存用于存放KEY设备的资源
key_info = devm_kzalloc(&pdev->dev, sizeof(struct imx6ull_key_info), GFP_KERNEL);
if(!key_info) {
dev_err(&pdev->dev, "Failed to allocate memory for imx6ull key platform driver\n");
return -ENOMEM;
}
//将私有数据绑定到平台设备
platform_set_drvdata(pdev, key_info);
//获取按键使用的引脚信息
key_info->desc = devm_gpiod_get(&pdev->dev, "key", GPIOD_ASIS);
if(IS_ERR(key_info->desc)) {
dev_err(&pdev->dev, "Failed to get GPIO pin for key\n");
return PTR_ERR(key_info->desc);
}
//获取按键引脚对应的中断信息
key_info->irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(!key_info->irq) {
dev_err(&pdev->dev, "no IRQ specified\n");
return -ENXIO;
}
//初始化等待队列头
init_waitqueue_head(&key_info->key_waitqueue);
//初始化内核定时器
init_timer(&key_info->timer_press);
key_info->timer_press.function = key_press_timer_func;
key_info->timer_press.data = (unsigned long)key_info;
init_timer(&key_info->timer_lpress);
key_info->timer_lpress.function = key_long_press_timer_func;
key_info->timer_lpress.data = (unsigned long)key_info;
//获取按键使用的编号
ret = of_property_read_u32(pdev->dev.of_node, "id", &key_info->id);
if(ret)
return ret;
//往内核注册一个LED字符设备
return key_chrdev_register(&key_info->key_cdev, key_info->id, 1, "imx6ull key");
}
static int imx6ull_key_remove(struct platform_device *pdev)
{
struct imx6ull_key_info *key_info = platform_get_drvdata(pdev);
return key_chrdev_unregister(&key_info->key_cdev, key_info->id, 1, "imx6ull key");
}
static const struct of_device_id imx6ull_key_of_match[] = {
{ .compatible = "imx6ull-key" },
{}
};
static struct platform_driver imx6ull_key_driver = {
.probe = imx6ull_key_probe,
.remove = imx6ull_key_remove,
.driver = {
.name = "imx6ull-key",
.of_match_table = imx6ull_key_of_match,
},
};
//------------------------------------------------------
static struct key_drv_func func_list;
static int imx6ull_init_key_gpio(struct cdev *key_cdev, void **private)
{
int ret;
struct imx6ull_key_info *key_info = container_of(key_cdev, struct imx6ull_key_info, key_cdev);
//按键使用的引脚设置为输入
ret = gpiod_direction_input(key_info->desc);
if(ret)
return ret;
//申请使用按键中断
ret = request_irq(key_info->irq->start, imx6ull_key_interrupt, key_info->irq->flags & IRQF_TRIGGER_MASK, "imx6ull key", key_info);
if(ret)
return ret;
//初始化按键状态
key_info->pevents = 0;
key_info->pstatus = KEY_STATE_INITIAL;
key_info->lpstatus = KEY_STATE_INITIAL;
//先触发一次按键定时器(为了同步按键的状态)
add_timer(&key_info->timer_press);
*private = key_info;
return 0;
}
static int imx6ull_key_fasync(int fd, struct file *filp, int on)
{
struct imx6ull_key_info *key_info = (struct imx6ull_key_info *)filp->private_data;
return fasync_helper(fd, filp, on, &key_info->key_async_queue);
}
static unsigned int imx6ull_poll_key_status(struct file *filp, poll_table *wait)
{
struct imx6ull_key_info *key_info = (struct imx6ull_key_info *)filp->private_data;
poll_wait(filp, &key_info->key_waitqueue, wait);
if(key_info->pevents || key_info->lpstatus)
return POLLIN | POLLRDNORM;
return 0;
}
static int imx6ull_read_key_status(struct file *filp, char *status)
{
char retval;
struct imx6ull_key_info *key_info = (struct imx6ull_key_info *)filp->private_data;
if((!key_info->pevents && !key_info->lpstatus)) {
if(filp->f_flags & O_NONBLOCK)
return -1;
else
wait_event_interruptible(key_info->key_waitqueue, (key_info->pevents || key_info->lpstatus));
}
retval = 0;
if(key_info->pevents) {
retval |= (0x1 << 4);
key_info->pevents--;
}
if(key_info->lpstatus)
retval |= 0x1;
*status = retval;
return 0;
}
static int imx6ull_close_key_gpio(void *private)
{
struct imx6ull_key_info *key_info = (struct imx6ull_key_info *)private;
free_irq(key_info->irq->start, key_info);
del_timer_sync(&key_info->timer_press);
del_timer_sync(&key_info->timer_lpress);
return 0;
}
static int __init key_platform_driver_init(void)
{
func_list.func_init_key_gpio = imx6ull_init_key_gpio;
func_list.func_key_fasync = imx6ull_key_fasync;
func_list.func_poll_key_status = imx6ull_poll_key_status;
func_list.func_read_key_status = imx6ull_read_key_status;
func_list.func_close_key_gpio = imx6ull_close_key_gpio;
key_chrdev_register_func(&func_list);
return platform_driver_register(&imx6ull_key_driver);
}
static void __exit key_platform_driver_exit(void)
{
func_list.func_init_key_gpio = NULL;
func_list.func_read_key_status = NULL;
func_list.func_close_key_gpio = NULL;
key_chrdev_unregister_func();
platform_driver_unregister(&imx6ull_key_driver);
}
module_init(key_platform_driver_init);
module_exit(key_platform_driver_exit);
//-------------------------------------------------------
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David.Tang");
嵌入式Linux按键驱动,支持短按和长按检测
需积分: 11 10 浏览量
2023-03-15
16:45:30
上传
评论 1
收藏 12KB RAR 举报
乔碧萝成都分萝
- 粉丝: 190
- 资源: 7
最新资源
- 基于matlab开发的全面详解LTE:MATLAB建模、仿真与实现-simulink.rar
- 自动驾驶定位系列教程二:系统架构.pdf
- 整站程序8优技巧网-8ujq.rar
- 世界各个国家或地区国际域名缩写
- 基于matlab开发的根据rvm回归模型自己编的matlab程序.rar
- 基于matlab开发的该程序为国内一所大学编写的LTE链路层仿真程序,根据LTE标准协议编写的,很容易看懂.rar
- 高效C++学生成绩管理系统:教育技术+C++17编程+数据管理+教务自动化
- 搜索链接要广告分类系统 v2.0-yad20.rar
- 基于matlab开发的Tipping的相关向量机RVM的回归MATLAB程序,有英文注释,可以运行.rar
- 一个点击正反转程序实例,可实现案件电机正反转
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈