自学内容网 自学内容网

fmql之Linux Input子系统

正点原子第43章

input子系统

 

驱动程序

/***************************************************************
 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
 文件名    : keyinput.c
 作者      : Skylar
 版本      : V1.0
 描述      : Input子系统
 其他      : key
 论坛      : www.openedv.com
 日志      : 初版V1.0 2024/10/9 创建
 ***************************************************************/

#include <linux/module.h>
#include <linux/of_gpio.h>
// #include <linux/cdev.h>
// #include <linux/uaccess.h>
#include <linux/platform_device.h>
// #include <linux/leds.h>
// #include <linux/miscdevice.h>
#include <linux/input.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input-event-codes.h>

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/kern_levels.h>

#define PS_KEY_CODEKEY_0

struct mykey_dev {
struct input_dev *idev;// input_dev指针
struct timer_list timer;// 消抖
int gpio;
int irq;
};

/*
* @description : 定时器服务函数,用于按键消抖,定时时间到了以后
* 再读取按键值,根据按键的状态上报相应的事件
* @param – arg : arg 参数可以在初始化定时器的时候进行配置
* @return : 无
*/
static void key_timer_function(struct timer_list *arg)
{
int val = 0;
struct mykey_dev *key = from_timer(key, arg, timer);

val = gpio_get_value(key->gpio);
input_report_key(key->idev, PS_KEY_CODE, !val);// 上报按键事件
input_sync(key->idev);// 同步事件

enable_irq(key->irq);// 使能按键中断
}

/*
 * @description : 按键中断服务函数
 * @param – irq : 触发该中断事件对应的中断号
 * @param – arg : arg 参数可以在申请中断的时候进行配置
 * @return : 中断执行结果
 */
static irqreturn_t mykey_interrupt(int irq, void *arg)
{
struct mykey_dev *key = (struct mykey_dev *)arg;

/* 判断触发中断的中断号是否是按键对应的中断号 */
if(key->irq != irq)
return IRQ_NONE;

/* 按键防抖处理,开启定时器延时 15ms */
disable_irq_nosync(irq);// 禁止按键中断
mod_timer(&key->timer, jiffies + msecs_to_jiffies(15));

return IRQ_HANDLED;
}

/*
 * @description : 按键初始化函数
 * @param – pdev : platform 设备指针
 * @return : 成功返回 0,失败返回负数
 */
static int mykey_init(struct platform_device *pdev)
{
struct mykey_dev *key = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
unsigned long irq_flags=0;
int ret;

key->gpio = of_get_named_gpio(dev->of_node, "key-gpio", 0);
if(!gpio_is_valid(key->gpio)){
dev_err(dev, "Failed to get gpio");
return -EINVAL;
}

ret = devm_gpio_request(dev, key->gpio, "Key Gpio");
if(ret){
dev_err(dev, "Failde to request gpio");
return ret;
}

gpio_direction_input(key->gpio);

/* 获取 GPIO 对应的中断号 */
// key->irq = irq_of_parse_and_map(dev->of_node, 0);
key->irq=gpio_to_irq(key->gpio);
if (!key->irq)
return -EINVAL;

/* 获取设备树中指定的中断触发类型 */
irq_flags = irq_get_trigger_type(key->irq);
if (IRQF_TRIGGER_NONE == irq_flags)
irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;

/* 申请中断 */
return devm_request_irq(dev, key->irq, mykey_interrupt,
irq_flags, "PS_Key0 IRQ", key);
}

/*
 * @description : platform 驱动的 probe 函数,当驱动与设备
 * 匹配成功以后此函数会被执行
 * @param – pdev : platform 设备指针
 * @return : 0,成功;其他负值,失败
 */
static int mykey_probe(struct platform_device *pdev)
{
struct mykey_dev *key;
struct input_dev *idev;
int ret;

dev_info(&pdev->dev, "Key driver and device have been matched\n");

/* 为key指针分配内存 */
key = devm_kzalloc(&pdev->dev, sizeof(struct mykey_dev), GFP_KERNEL);
if(!key)
return -ENOMEM;

platform_set_drvdata(pdev, key);

/* 初始化key */
ret = mykep_init(pdev);
if(ret){
return ret;
}

/* 定时器初始化 */
timer_setup(&key->timer, key_timer_function, 0);

/* input_dev 初始化 */
idev = devm_input_allocate_device(&pdev->dev);
if(!idev)
return -ENOMEM;
key->idev = idev;
idev->name = "mykey";

    __set_bit(EV_KEY, idev->evbit); // 可产生按键事件
__set_bit(EV_REP, idev->evbit); // 可产生重复事件
__set_bit(PS_KEY_CODE, idev->keybit); // 可产生 KEY_0 按键事件

/* 注册key input设备 */
return input_register_devive(idev);
}

/*
 * @description : platform 驱动的 remove 函数,当 platform 驱动模块
 * 卸载时此函数会被执行
 * @param – dev : platform 设备指针
 * @return : 0,成功;其他负值,失败
 */
static int mykey_remove(struct platform_device *pdev)
{
struct mykey_dev *key = platform_get_drvdata(pdev);

/* 删除定时器 */
del_timer_sync(&key->timer);

/* 注销key设备驱动 */
input_unregister_device(key->idev);

dev_info(&pdev->dev, "Key device was removed\n");

return 0;
}

/* 匹配列表 */
static const struct of_device_id key_of_match[] = {
{.compatible = "fmql,key"},
{/* Sentinel */}
};

static struct platform_driver mykey_driver = {
.driver = {
.name= "mykey",/* platform_driver name*/
.of_match_table= key_of_match,
},
.probe = mykey_probe,
.remove = mykey_remove,
};


module_platform_driver(mykey_driver);

MODULE_AUTHOR("Skylar <Skylar@33.com>");
MODULE_DESCRIPTION("Key Driver, Input Subsystem");
MODULE_LICENSE("GPL");

测试程序

/***************************************************************
 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
 文件名                 : keyinputAPP.c
 作者                   : Skylar
 版本                   : V1.0
 描述                   : key Input_Subsystem
 其他                   : 
 使用方法                : ./keyinputApp /dev/input/eventX
 论坛                   : www.openedv.com
 日志                   : 初版V1.0 2024/10/9 创建
 ***************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
// #include <sys/ioctl.h>
// #include <signal.h>
#include <linux/input.h>

/*
 * @description : main 主程序
 * @param – argc : argv 数组元素个数
 * @param – argv : 具体参数
 * @return : 0 成功;其他 失败
 */
int main(int argc, char *argv[]
{
    int fd, ret;
    struct input_event ev;

    if(argc != 2){
        printf("Usage:\n"
            "\t./keyinputApp /dev/input/eventX  @ Open Key\n"
        );
    return -1;
    }

    fd = open(argv[1], O_RDWR);
    if(fd < 0){
        printf("Erroe: file %s open failed\r\n", argv[1]);
        return -1;
    }

    /* 读取按键数据 */
    for ( ; ; ){
        ret = read(fd, &ev, sizeof(struct input_event));
        if(ret){
            switch(ev.type){
                case EV_KEY:        // 按键事件
                    if(KEY_0 == ev.code){       // 判断是不是 KEY_0 按键
                        if(ev.value)            // 按键按下
                            printf("PS Key0 Press\n");
                        else    
                            printf("PS Key0 Release\n");
                    }
                    break;
                
                /* 其他类型的事件,自行处理 */
                case EV_REL:
                    break;
                case EV_ABS:
                    break;
                case EV_MSC:
                    break;
                case EV_SW:
                    break;
            }
        } else {
            printf("Error: file %s read failed!\r\n", argv[1]);
            goto out;
        }
    }
    
out:
    close(fd);
    return 0;
})

运行

Linux自带按键驱动

dts中要写成compatible = “gpio-keys”         

 

 

 修改dts


原文地址:https://blog.csdn.net/qq_41656020/article/details/142786074

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!