linux 驱动开发之平台设备驱动设备树 input子系统的使用:按键中断驱动

这一章我们linux input 子系统

在linux中,input子系统有三部分组成:驱动层,输入子系统核心,事件处理层.一个输入时间,比如按键按下通过:驱动->input core->event handler->user space的顺序到达用户空间的应用程序

驱动层:将底层的硬件输入转化为统一的事件形式,向输入核心(input core)汇报

核心层:承上启下,为驱动层提供输入设备接口与操作借口;通知事件处理层进行时间处理;在/proc下产生相应的设备信息.

事件处理层:树要是和用户空间打交道,(Linux中一切皆是文件,由于input驱动设备有fops接口,在/dev下会产生相应的设备文件nod,访问这些文件通过应用程序来完成).

实现input驱动的核心工作就是:向系统报告按键等输入事件(event,通过input_event结构描述),不再关心文件操作接口(不用手动的完成file_operations结构体的实例化).报告事件通过核心层和事件处理层直接到达用户.

一些input驱动相关的函数:

设备注册

注册 int input_regesiter_device(struct input_dev *dev)
注销 void input_unregister_device(struct input_dev *dev)

设置事件
set_bit()告诉input输入系统支持那些设备,哪些按键等
比如
set_bit(EV_KEY,button_dev.evbit)(其中button_dev是struct input_dev类型)

报告事件:

用于报告EV_KEY,EV_REL,EV_ABS事件的函数分别为:

报告按键事件void input_report_key(struct input_dev *dev,unsigned int code,int value)
报告重复事件void input_report_rel (struct input_dev *dev, unsigned int code, int value)
报告绝对坐标事件void input_report_abs (struct input_dev *dev, unsigned int code, int value)

报告结束后,同步事件
input_sync()同步用于告诉input core 子系统报告结束

实例 ,我们做一个按键input驱动,该驱动首先在设备树文件里查找按键的节点,获取按键的属性值,通过中断里开启定时器定时10ms进行按键机械消抖,在定时器里将按键事件汇报给input核,然后编写应用程序实现按键事件的读取

在设备树根/下建立一个key节点,在iomux节点下建立一个pinctl节点,包括compatible属性,按键对应的gpio口,中断,名称等等
设备树文件:

	key-myz {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "myz-key";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_key>;
		interrupt-parent = <&gpio5>;
		interrupts = <1 IRQ_TYPE_EDGE_BOTH>;//both_edge
		key-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;
		status = "okay";
	};



pinctrl_key: keygrp {
		fsl,pins = <
				//MX6UL_PAD_NAND_CE1_B__GPIO4_IO14		0xF080/* KEY0 */		
				MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01		0xF080/* KEY0 */
			>;
		};

驱动程序:

#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_gpio.h>
#include <linux/input.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/spi/spi.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

struct key_input
{                                        //key属性
    struct input_dev *input_dev;         //input设备
    struct device_node *nd;              //设备节点
    struct timer_list timer;             //定时器句柄
    irqreturn_t (*handler)(int, void *); //定时器回调函数
    int pin;                             //key的引脚编号
    int irqnum;                          //key对应的中断
    int value;                           //key的值
};

static struct key_input key; //声明一个实例

static irqreturn_t key_handler(int irq, void *dev_id)
{ //中断回调函数
    struct key_input *dev = (struct key_input *)dev_id;
    dev->timer.data = (volatile long)dev_id;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); //主要实现了一个10ms的定时任务,用于按键机械防抖
    return IRQ_RETVAL(IRQ_HANDLED);
}

static void timer_fun(unsigned long arg)
{ //定时器回调函数
    unsigned char value = 0;
    struct key_input *dev = (struct key_input *)arg;
    value = gpio_get_value(dev->pin); //获取按键值
    if (value == 0)
    {
        input_report_key(dev->input_dev, dev->value, 1); //上报按键的值,如果value == 0的时候就上报1,否则上报0;
        input_sync(dev->input_dev);                      //同步上报的信息
    }
    else
    {
        input_report_key(dev->input_dev, dev->value, 0);
        input_sync(dev->input_dev);
    }
}

static int __init mkey_init(void)
{ //insmod 的时候调用
    int res = 0;
    printk("key _init\n");
    key.input_dev = input_allocate_device(); //申请一个input设备
    key.input_dev->name = "key-myz";
    key.nd = of_find_node_by_path("/key-myz"); //在设备树根/下找key-myz节点
    if (key.nd == NULL)
    {
        printk("key node is not find \n");
        return -EINVAL;
    }
    else
    {
        printk("key node is success\n");
    }
    key.pin = of_get_named_gpio(key.nd, "key-gpio",0); //通过key-myz节点找出""key-gpio"的引脚编号
    if (key.pin < 0)
    {
        printk("can not get key pin\n");
    }
    else
    {
        printk("get key pin success ! pin  = %d \n",key.pin);
    }
    res = gpio_request(key.pin, "KEY0"); //通过按键引脚编号申请一个gpio
    if(res){
        printk("gpio_requested failed res = %d\n",res);
    }else{
        printk("gpio_requested successed !\n");
    }
    gpio_direction_input(key.pin); //设置按键引脚为输入

    key.irqnum = irq_of_parse_and_map(key.nd, 0); //根据key-myz节点解析一个中断号

    printk("irqnumber is %d\n",key.irqnum);

    key.handler = key_handler; //中断函数赋值
    key.value = KEY_0;
    //request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
    res = request_irq(key.irqnum, key.handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key-mayunzhi", &key); //根据中断号申请中断并且注册中断回调函数,上升下降沿产生中断
    if (res < 0)
    {
        printk("irq request failed ! res = %d\n",res);
        gpio_free(key.pin );
        return -EINVAL;
    }
    else
    {
        printk("irq request successed\n");
    }

    init_timer(&key.timer); //初始化定时器

    key.timer.function = timer_fun; //???定时器中断函数

    key.input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); //按键事件和重复事件赋值

    input_set_capability(key.input_dev, EV_KEY, KEY_0); //注册按键事件和重复事件

    res = input_register_device(key.input_dev); //注册一个input设备
    if (res)
    {
        printk("input device register failed\n");
        return res;
    }
    else
    {
        printk("input device register successed\n");
    }
    return 0;
}
static void __exit mkey_exit(void)
{ //rmmod 的时候调用
    printk("key _exit\n");
    del_timer_sync(&key.timer);
    free_irq(key.irqnum, &key);
    input_unregister_device(key.input_dev); //卸载input设备
    input_free_device(key.input_dev);
}
module_init(mkey_init);

module_exit(mkey_exit);

MODULE_LICENSE("GPL");

应用程序的编写:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/ioctl.h>

static struct input_event inputevent;
int main(int argc,char *argv[]){
    int fd;
    int res;
    char *filename;
    if(argc != 2){
        printf("err usage \n");
    }
    filename = argv[1];
    fd = open(filename ,O_RDWR);
    if(fd<0){
        printf("can not open file :%s\n",filename);
        return -1;
    }
    while(1){
        res = read(fd ,&inputevent,sizeof(inputevent));
        if(res >0){
            switch(inputevent.type){
                case EV_KEY:
                    if(inputevent.code <BTN_MISC){
                        printf("key  %d  is  %s\n",inputevent.code,inputevent.value?"pressed":"released");
                    }else{
                        printf("key  %d  is  %s\n",inputevent.code,inputevent.value?"pressed":"released");
                    }
                    break;
                case EV_REL:
                    printf("it is a rel event\n");
                    break;
            }
        }else{
            printf("read file %s  failed\n",filename);
        }
    }
    return 0;
}

makefile 文件内容

KERNELDIR 		:=/home/mayunzhi/linux/100ask_imx6ull-sdk/Linux-4.9.88
CURRENT_PATH 	:=$(shell pwd)
ARM-CC			:=arm-linux-gnueabihf-gcc

APP				:=app
DRV				:=key
obj-m 			:=$(DRV).o


build:kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	rm -f ./app
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
	
app:
	$(ARM-CC) $(APP).c -o $(APP)

mv:
	sudo mv ./app *.ko /home/mayunzhi/linux/nfs/rootfs/lib/modules/4.1.15

在驱动文件夹下依次执行make ,make app ,make mv

在kernel根文件目录下 执行 make dtbs ,并且拷贝生成的xxx.dtb文件到tftp文件夹,以便通过tftp启动内核和设备树可执行文件

重启设备,加载设备树,将key.ko app文件放在开发板文件系统中/lib/module/4.1.15目录中

cd /lib/module/4.1.15 

执行insmod key.ko没有错误的话说就说明加载成功了,然后执行./app,通过应用程序进行解析/dev/input/event2中的按键事件,也可以执行hexdump /dev/input/event2来实现原始数据的读取

实验现象在这里插入图片描述
在这里插入图片描述
卸载驱动 rmmod key.ko

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值