LINUX 基于设备树编写按键中断驱动程序

Linux内核版本:4.14.2

本文基于itop4412开发板,编写驱动程序响应HOME按键中断,编写这个按键驱动程序需要做如下几个工作:

1. 在原理图中确定HOME按键的引脚

2. 在设备树文件中添加节点描述HOME引脚

3. 重新编译烧写设备树

4. 编写驱动程序,调用设备树接口函数获取HOME引脚的中断号,使用中断号注册按键中断处理程序

  1. 在原理图中确定HOME按键的引脚

  2. 在这里插入图片描述
    在原理图中找到HOME按键对应的引脚是GPX1_1

  3. 在设备树文件中添加节点描述HOME引脚

itop4412开发板中将GPIO分成多个组,GPX1_1在gpx1组中,设备树文件exynos4412-pinctrl.dtsi中用如下节点描述gpx1组:在这里插入图片描述我们需要在设备树文件exynos4412-itop-elite.dts中,根据exynos4412-pinctrl.dtsi文件对gpx1组的描述添加HOME引脚的描述节点,如下
在这里插入图片描述3. 重新编译烧写设备树

  1. 编写驱动程序,调用设备树接口函数获取HOME引脚的中断号,使用中断号注册按键中断处理程序

驱动程序按照平台总线的架构编写,用platform_driver_register注册驱动;

4.1 设备和驱动匹配实现过程

     在设备树文件中添加了HOME按键的描述节点后,内核在解析设备树时,就会将上述节点转换成一个平台设备platform_device;在struct platform_driver结构体中,将compatible属性设置为”irq-keys”,那么,加载驱动后,就会和内核生成的这个平台设备platform_device匹配,从而进入probe函数中

驱动程序中指定compatible的程序如下:

static const struct of_device_id irq_test_of_match[] = {

    { .compatible = "irq-keys", },

    { },

};

MODULE_DEVICE_TABLE(of, irq_test_of_match);

 

static struct platform_driver irq_test_device_driver = {

    .probe      = irq_test_probe,

    .driver     = {

        .name   = "irqtest_keys",

        .of_match_table = irq_test_of_match,

    }
};

4.2 HOME引脚信息读取过程

上述驱动和设备匹配后,程序就进入probe函数中,在probe函数中,可以使用设备树相关的接口读取HOME引脚的信息:

       读取子节点的个数,irqtest_keys只有一个子节点key-home

              device_get_child_node_count

       获取每个子节点的结构体

              device_for_each_child_node(dev, child)

       获取子节点中引脚的虚拟中断号,这个虚拟中断号是内核自动生成的

              irq_of_parse_and_map(to_of_node(child), 0)

       获取子节点gpio描述符gpiod

              devm_fwnode_get_gpiod_from_child

       使用虚拟中断号注册中断处理函数

              devm_request_any_context_irq
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>

/* 设备树节点 */
#if 0
irqtest_keys {
    compatible = "irq-keys";
    key-home {
        label = "GPIO key-home";
        gpios = <&gpx1 1 GPIO_ACTIVE_LOW>;
              pinctrl-0 = <&my_irq_key>;
              pinctrl-names = "default";
    };
};

my_irq_key: my-irq-key {
    samsung,pins = "gpx1-1";
    samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
//    samsung,pin-drv = <EXYNOS4_PIN_DRV_LV4>;
};
#endif

struct my_gpio_keys_button {
    unsigned int code;
    int gpio;
    int active_low;
    const char *desc;
    unsigned int type;
    int wakeup;
    int debounce_interval;
    bool can_disable;
    int value;
    unsigned int irq;
    struct gpio_desc *gpiod;
};

static char *label[2];
static struct device *dev;
static struct my_gpio_keys_button *button;

static irqreturn_t irq_test_irq_isr(int irq, void *dev_id)
{
    printk(KERN_INFO "get irq --> irq_test_irq_isr.\n");

    return IRQ_HANDLED;
}

static int irq_test_probe(struct platform_device *pdev)
{
    /* 获取节点信息,注册中断 */
    dev = &pdev->dev;
    struct fwnode_handle *child = NULL;
    int nbuttons;
    int irq, error;
    irq_handler_t isr;
    unsigned long irqflags;

    nbuttons = device_get_child_node_count(dev);
    if (nbuttons == 0) {
        printk(KERN_INFO "no child exist, return\n");
        return ERR_PTR(-ENODEV);
    }

    printk(KERN_INFO "child num is %d.\n", nbuttons);
    button = devm_kzalloc(dev, sizeof(struct my_gpio_keys_button) * nbuttons, GFP_KERNEL);

    /* 获取lable参数,父节点没有lable属性 */
    device_property_read_string(dev, "label", label[0]);
    printk(KERN_INFO "parent lable %s\n", label[0]);

    /* 扫描处理每个子节点 */
    device_for_each_child_node(dev, child) {
        /* 获取虚拟中断号virq */
        if (is_of_node(child)) {
            button->irq = irq_of_parse_and_map(to_of_node(child), 0);
        }

        fwnode_property_read_string(child, "label", &button->desc);
        /* 获取gpio描述符gpiod */
        button->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL,
                                child,
                                GPIOD_IN,
                                button->desc);
        if (IS_ERR(button->gpiod)) {
            printk(KERN_INFO "get gpiod failed, return.\n");
            return -ENOENT;
        }

        /* 检查虚拟中断号,可不使用 */
        if (!button->irq) {
            irq = gpiod_to_irq(button->gpiod);
            if (irq < 0) {
                error = irq;
                dev_err(dev,
                    "Unable to get irq number for GPIO %d, error %d\n",
                    button->gpio, error);
                return error;
            }
            button->irq = irq;
        }

        printk(KERN_INFO "get virq %d for key.\n", button->irq);
        isr = irq_test_irq_isr;
        irqflags = 0;
        irqflags |= IRQF_SHARED;
//devm_request_any_context_irq(class_dev, data->irq,int26_irq, IRQF_TRIGGER_FALLING, data->name, data);

        /* 设置引脚为输入模式 */
        gpiod_set_value(button->gpiod, 1);
        gpiod_direction_input(button->gpiod);

        /* 注册中断 */
        /* 最后一个参数是传给中断函数的参数 */
        error = devm_request_any_context_irq(dev, button->irq, isr, IRQF_TRIGGER_FALLING, button->desc, NULL);
        if (error < 0) {
            dev_err(dev, "Unable to claim irq %d; error %d\n", button->irq, error);
            return error;
        }
    }

    return 0;
}

static const struct of_device_id irq_test_of_match[] = {
    { .compatible = "irq-keys", },
    { },
};

MODULE_DEVICE_TABLE(of, irq_test_of_match);

static struct platform_driver irq_test_device_driver = {
    .probe      = irq_test_probe,
    .driver     = {
        .name   = "irqtest_keys",
        .of_match_table = irq_test_of_match,
    }
};

static int __init irq_test_init(void)
{
    return platform_driver_register(&irq_test_device_driver);
}

static void __exit irq_test_exit(void)
{
    devm_free_irq(dev, button->irq, NULL);
    platform_driver_unregister(&irq_test_device_driver);
}

module_init(irq_test_init);
module_exit(irq_test_exit);

MODULE_LICENSE("GPL");

原文地址:https://www.cnblogs.com/lztutumo/p/13340411.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值