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_CODE		KEY_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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值