zoukankan      html  css  js  c++  java
  • 基于设备树编写按键中断驱动程序

      Linux内核版本:4.14.2

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

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

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

        3. 重新编译烧写设备树

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

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

      在原理图中找到HOME按键对应的引脚是GPX1_1

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

      itop4412开发板中将GPIO分成多个组,GPX1_1在gpx1组中,设备树文件exynos4412-pinctrl.dtsi中用如下节点描述gpx1组:

      我们需要在设备树文件exynos4412-itop-elite.dts中,根据exynos4412-pinctrl.dtsi文件对gpx1组的描述添加HOME引脚的描述节点,如下

    3. 重新编译烧写设备树

    4. 编写驱动程序,调用设备树接口函数获取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

    测试驱动程序如下:

      1 #include <linux/module.h>
      2 #include <linux/init.h>
      3 #include <linux/fs.h>
      4 #include <linux/interrupt.h>
      5 #include <linux/irq.h>
      6 #include <linux/sched.h>
      7 #include <linux/pm.h>
      8 #include <linux/slab.h>
      9 #include <linux/sysctl.h>
     10 #include <linux/proc_fs.h>
     11 #include <linux/delay.h>
     12 #include <linux/platform_device.h>
     13 #include <linux/input.h>
     14 #include <linux/gpio_keys.h>
     15 #include <linux/workqueue.h>
     16 #include <linux/gpio.h>
     17 #include <linux/gpio/consumer.h>
     18 #include <linux/of.h>
     19 #include <linux/of_irq.h>
     20 #include <linux/spinlock.h>
     21 
     22 /* 设备树节点 */
     23 #if 0
     24 irqtest_keys {
     25     compatible = "irq-keys";
     26     key-home {
     27         label = "GPIO key-home";
     28         gpios = <&gpx1 1 GPIO_ACTIVE_LOW>;
     29               pinctrl-0 = <&my_irq_key>;
     30               pinctrl-names = "default";
     31     };
     32 };
     33 
     34 my_irq_key: my-irq-key {
     35     samsung,pins = "gpx1-1";
     36     samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
     37 //    samsung,pin-drv = <EXYNOS4_PIN_DRV_LV4>;
     38 };
     39 #endif
     40 
     41 struct my_gpio_keys_button {
     42     unsigned int code;
     43     int gpio;
     44     int active_low;
     45     const char *desc;
     46     unsigned int type;
     47     int wakeup;
     48     int debounce_interval;
     49     bool can_disable;
     50     int value;
     51     unsigned int irq;
     52     struct gpio_desc *gpiod;
     53 };
     54 
     55 static char *label[2];
     56 static struct device *dev;
     57 static struct my_gpio_keys_button *button;
     58 
     59 static irqreturn_t irq_test_irq_isr(int irq, void *dev_id)
     60 {
     61     printk(KERN_INFO "get irq --> irq_test_irq_isr.
    ");
     62 
     63     return IRQ_HANDLED;
     64 }
     65 
     66 static int irq_test_probe(struct platform_device *pdev)
     67 {
     68     /* 获取节点信息,注册中断 */
     69     dev = &pdev->dev;
     70     struct fwnode_handle *child = NULL;
     71     int nbuttons;
     72     int irq, error;
     73     irq_handler_t isr;
     74     unsigned long irqflags;
     75 
     76     nbuttons = device_get_child_node_count(dev);
     77     if (nbuttons == 0) {
     78         printk(KERN_INFO "no child exist, return
    ");
     79         return ERR_PTR(-ENODEV);
     80     }
     81 
     82     printk(KERN_INFO "child num is %d.
    ", nbuttons);
     83     button = devm_kzalloc(dev, sizeof(struct my_gpio_keys_button) * nbuttons, GFP_KERNEL);
     84 
     85     /* 获取lable参数,父节点没有lable属性 */
     86     device_property_read_string(dev, "label", label[0]);
     87     printk(KERN_INFO "parent lable %s
    ", label[0]);
     88 
     89     /* 扫描处理每个子节点 */
     90     device_for_each_child_node(dev, child) {
     91         /* 获取虚拟中断号virq */
     92         if (is_of_node(child)) {
     93             button->irq = irq_of_parse_and_map(to_of_node(child), 0);
     94         }
     95 
     96         fwnode_property_read_string(child, "label", &button->desc);
     97         /* 获取gpio描述符gpiod */
     98         button->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL,
     99                                 child,
    100                                 GPIOD_IN,
    101                                 button->desc);
    102         if (IS_ERR(button->gpiod)) {
    103             printk(KERN_INFO "get gpiod failed, return.
    ");
    104             return -ENOENT;
    105         }
    106 
    107         /* 检查虚拟中断号,可不使用 */
    108         if (!button->irq) {
    109             irq = gpiod_to_irq(button->gpiod);
    110             if (irq < 0) {
    111                 error = irq;
    112                 dev_err(dev,
    113                     "Unable to get irq number for GPIO %d, error %d
    ",
    114                     button->gpio, error);
    115                 return error;
    116             }
    117             button->irq = irq;
    118         }
    119 
    120         printk(KERN_INFO "get virq %d for key.
    ", button->irq);
    121         isr = irq_test_irq_isr;
    122         irqflags = 0;
    123         irqflags |= IRQF_SHARED;
    124 //devm_request_any_context_irq(class_dev, data->irq,int26_irq, IRQF_TRIGGER_FALLING, data->name, data);
    125 
    126         /* 设置引脚为输入模式 */
    127         gpiod_set_value(button->gpiod, 1);
    128         gpiod_direction_input(button->gpiod);
    129 
    130         /* 注册中断 */
    131         /* 最后一个参数是传给中断函数的参数 */
    132         error = devm_request_any_context_irq(dev, button->irq, isr, IRQF_TRIGGER_FALLING, button->desc, NULL);
    133         if (error < 0) {
    134             dev_err(dev, "Unable to claim irq %d; error %d
    ", button->irq, error);
    135             return error;
    136         }
    137     }
    138 
    139     return 0;
    140 }
    141 
    142 static const struct of_device_id irq_test_of_match[] = {
    143     { .compatible = "irq-keys", },
    144     { },
    145 };
    146 
    147 MODULE_DEVICE_TABLE(of, irq_test_of_match);
    148 
    149 static struct platform_driver irq_test_device_driver = {
    150     .probe      = irq_test_probe,
    151     .driver     = {
    152         .name   = "irqtest_keys",
    153         .of_match_table = irq_test_of_match,
    154     }
    155 };
    156 
    157 static int __init irq_test_init(void)
    158 {
    159     return platform_driver_register(&irq_test_device_driver);
    160 }
    161 
    162 static void __exit irq_test_exit(void)
    163 {
    164     devm_free_irq(dev, button->irq, NULL);
    165     platform_driver_unregister(&irq_test_device_driver);
    166 }
    167 
    168 module_init(irq_test_init);
    169 module_exit(irq_test_exit);
    170 
    171 MODULE_LICENSE("GPL");
  • 相关阅读:
    navicat for mysql 10.1.7注册码
    去除GHOST版系统自带的2345流氓软件
    利用EXCEL表实现网页数据采集到MYSQL数据库
    Java开发的一个简单截屏工具
    初学JSP+Servlet常见的错误
    从零单排Linux – 3 – 目录结构
    从零单排Linux – 2 – 目录权限
    从零单排Linux – 1 – 简单命令
    在虚拟机中安装Linux
    随机生成MyEclipse注册码
  • 原文地址:https://www.cnblogs.com/lztutumo/p/13340411.html
Copyright © 2011-2022 走看看