gpio-export配置默认gpio

gpio-export配置默认gpio

用于设置gpio的默认状态和导出用户空间借口。只需要在设备树中配置节点即可。

参考链接:

https://linux-arm-kernel.infradead.narkive.com/QRDUydDE/patch-0-2-gpio-allow-userspace-export-from-dt#post9

https://devicetree.vger.kernel.narkive.com/hUDm3uhy/patch-gpio-add-export-with-name-from-dts

1. 驱动源码分析

导入头文件:
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/platform_device.h>

定义驱动的数据结构:

struct gpio_export_gpio{
    char *name;       // 节点名称
    struct gpio_desc *desc;    // gpio描述
};

struct gpio_export{
    struct device *dev;  // 用于绑定相关的设备
    int gpios_num;      // 记录一共需要导出的节点数量,在卸载驱动时用于取消导出
    struct gpio_export_gpio *gpios;  // 导出的每一个gpio的信息
};

定义驱动的 of_match_table :

static struct of_device_id gpio_export_ids[] = {
    { .compatible = "linux-gpio-export" },
    { /* sentinel */}
};

当找到设备树节点中的 compatible 属性为 linux-gpio-export 时,执行 probe 函数。

定义 platform_driver 的一些信息和回调:

static struct platform_driver gpio_export_driver = {
    .driver = {
        .name = "linux-gpio-export",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(gpio_export_ids),
    },
    .probe = gpio_export_probe,
    .remove = gpio_export_remove,
};

优先匹配 of_match_table 中的 compatible 属性,匹配不上时,再使用 name 进行匹配。然后注册 proberemove 回调。

实现 probe 函数:

static int gpio_export_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node; // 获取整个平台设备节点
    struct device_node *cnp; 
    int nb = 0;
    int val;

    struct gpio_export *ge;
    struct device *dev = &pdev->dev; // 获取设备
    
    ge = devm_kzalloc(dev, sizeof(struct gpio_export), GFP_KERNEL);  // 为驱动结构分配内存空间
    if (IS_ERR(ge))
        return PTR_ERR(ge);

    ge->dev = dev; // 绑定设备
    dev_set_drvdata(dev, ge);  // 赞存驱动结构到设置中

    for_each_child_of_node(np, cnp) {  // 获取总共需要导出的gpio节点数量
        ++nb;
    }

    ge->gpios_num = nb;
    nb = 0;
    ge->gpios = devm_kzalloc(dev, 
                            sizeof(struct gpio_export_gpio) * ge->gpios_num,
                            GFP_KERNEL);  // 为每一个gpio都分配存放信息结构的空间
    if (IS_ERR(ge->gpios)) 
        return PTR_ERR(ge->gpios);    
    
    for_each_child_of_node(np, cnp) { // 读取每一个节点的内部子节点,然后对每一个子节点依次进行导出操作
        const char *name = NULL;
        int gpio;
        bool dmc;

        of_property_read_string(cnp, "gpio-export,name", &name);
        if (!name) {
            name = of_node_full_name(np);
        }

        ge->gpios[nb].name = devm_kzalloc(dev, strlen(name) + 1, GFP_KERNEL);
        strncpy(ge->gpios[nb].name, name, strlen(name));
        gpio = of_get_gpio(cnp, 0);
        ge->gpios[nb].desc = gpio_to_desc(gpio);
        // 执行 gpiod_export 前,需要先进行 gpio_request 操作
        if (devm_gpio_request(&pdev->dev, gpio, ge->gpios[nb].name) {
            ++nb;
            continue;
        }
	   // 默认电平
        if (!of_property_read_u32(cnp, "gpio-export,output", &val))
            gpio_direction_output(gpio, val);
        else
            gpio_direction_input(gpio);
	   // 是否允许用户配置输入/输出方向
        dmc = of_property_read_bool(cnp, "gpio-export,direction-may-change");
        gpiod_export(ge->gpios[nb].desc, dmc);   // 导出gpio到 /sys/class/gpio/ 目录下
        gpiod_export_link(&pdev->dev, ge->gpios[nb].name, ge->gpios[nb].desc);  // 创建符号链接到 /sys/devices/gpio-export/ 下

        ++nb;
    }

    dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);

    return 0;
}

注意,这里都是使用 devm 类的借口,使用这类接口,会把分配的内存的操作和 dev 对象绑定,在设备创建失败,或者移除卸载驱动时,自动释放分配的内存。但是,devm 接口尽量只在 probe 回调中使用,不能在 open 回调中使用。使用devm 分配内存后,在确认不需要使用内存的情况下,也可以通过 devm_kfree 借口对分配的内存进行主动释放操作。

详见:https://stackoverflow.com/questions/12256986/what-is-the-difference-between-devm-kzalloc-and-kzalloc-in-linux-driver-prog

实现 remove 函数:

static int gpio_export_remove(struct platform_device *pdev)
{   
    struct device *dev = &pdev->dev;
    struct gpio_export *ge = dev_get_drvdata(dev);  // 从设备结构中获取之前暂存的驱动数据
    int i;

    for (i = 0; i < ge->gpios_num; i++) {
        sysfs_remove_link(&ge->dev->kobj, ge->gpios[i].name);
        gpiod_unexport(ge->gpios[i].desc);   // 对已经的导出的所有gpio进行取消导出的操作
    }

    return 0;
}

由于使用devm类接口,因此在 remove 回调中,不需要去进行资源释放。在这里,只需要对已经导出的gpio,进行取消导出操作即可。

注册平台设备:

将驱动注册到平台设备上。

static int __init gpio_export_init(void)
{
    return platform_driver_register(&gpio_export_driver);
}
 
static void __exit gpio_export_exit(void)
{
    platform_driver_unregister(&gpio_export_driver);
}

module_init(gpio_export_init);
module_exit(gpio_export_exit);
MODULE_LICENSE("GPL");

加载驱动时,会将驱动注册到平台设备上。下载驱动时,会取消注册。

也可以使用下面的宏完成这个操作:

module_platform_driver(gpio_exporter_driver);
MODULE_LICENSE("GPL");

至此,一个完整的驱动的代码就完成了。

2. 设备树

设备树需要添加导出节点到根节点:

/ {
	model = "Qualcomm Technologies, Inc. MDM 9607";
	compatible = "qcom,mdm9607";
	..................
	soc: soc { };

	gpio-export {
		compatible = "linux-gpio-export";
		#size-cells = <0>;

		4G_LDO_1V8 {
			gpio-export,name = "4G_LDO_1V8";
			gpio-export,output = <1>;
            // gpio-export,direction-may-change;
			gpios = <&tlmm_pinmux 16 0>;
		};
	};
};

3. 编译

需要将编写的驱动加入到内核构建的系统中,才能在编译的时候将驱动编译进内核或者编译成内核模块。

添加Kconfig

在当前文件路径下,找到Kconfig文件,打开编辑,将下面的内容添加到if GPIOLIB 宏定义之间:

config GPIO_EXPORT
	tristate "Export GPIO to userspace by DT"
	depends on OF_GPIO && GPIO_SYSFS
	help
	  Export GPIO to /sys/class/gpio/ direction. And set the 
	  default state by DT.

gpio-export 依赖于 OF_GPIOGPIO_SYSFS ,以及 GPIOLIB

tristate :三态,支持 nmy

添加Makefile

在当前文件路径下,找到Makefile文件,打开编辑,将下面的内容添加到文件末尾:

obj-$(CONFIG_GPIO_EXPORT)   += gpio-export.o

选择编译 gpio-export 驱动

Kconfig 和 Makefile 已经对 gpio-export 驱动进行支持了,最后还需要配置编译 gpio-export 驱动,驱动才能真正被编译到内核或者编译成内核模块。

找到kernel构建使用的默认 defconfig 配置文件,将 CONFIG_GPIO_EXPORT=m 添加到文件中。

我这里是 arch/arm/configs/mdm9607_defconfig ,因此可以将其添加到这里面。添加后需要clean,重新进行编译。为了避免clean重编,再将其添加到 .config 中,我这里的 .config 文件在 build/.config 中,也将 CONFIG_GPIO_EXPORT=m 添加到文件末尾,直接make即可编译 gpio-export 驱动。

4. 加载驱动

modprobe gpio-export
lsmod 

加载驱动后,驱动就能通过设备树,导出我们设置GPIO了:

[   26.689436] of_get_named_gpiod_flags: parsed 'gpios' property of node '/gpio-export/4G_LDO_1V8[0]' - status (0)
[   26.689646] linux-gpio-export gpio-export: 1 gpio(s) exported

然后会在 /sys/class/gpio/ 下生成节点。

/sys/class/gpio # cd gpio16/
/sys/devices/1000000.pinctrl/gpio/gpio16 # ls
active_low  device      power       subsystem   uevent      value
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值