• 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 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    定义驱动的数据结构:

    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的信息
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    定义驱动的 of_match_table :

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

    当找到设备树节点中的 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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    优先匹配 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    注意,这里都是使用 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    由于使用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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

    module_platform_driver(gpio_exporter_driver);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2

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

    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>;
    		};
    	};
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    gpio-export 依赖于 OF_GPIOGPIO_SYSFS ,以及 GPIOLIB

    tristate :三态,支持 nmy

    添加Makefile

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

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

    选择编译 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 
    
    • 1
    • 2

    加载驱动后,驱动就能通过设备树,导出我们设置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
    
    • 1
    • 2

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

    /sys/class/gpio # cd gpio16/
    /sys/devices/1000000.pinctrl/gpio/gpio16 # ls
    active_low  device      power       subsystem   uevent      value
    
    • 1
    • 2
    • 3
  • 相关阅读:
    代码随想录训练营day58
    PP洗气瓶:耐用、安全、高效 —— 您的气体采样理想伙伴
    centos7安装zabbix 5.0
    史上最全MongoDB之Mongo Shell使用
    汽车电子行业入门指南「知识体系」
    Java SpringBoot 加载 yml 配置文件中字典项
    组合模式(Composite Pattern)
    黑马-Jenkins篇
    everything-everything使用技巧,过滤文件语法
    聚丙烯酸叔丁酯修饰乳清白蛋白/肌白蛋白/豆清白蛋白/蓖麻蛋白/豌豆白蛋白1b ( PA1b)纳米粒
  • 原文地址:https://blog.csdn.net/duapple/article/details/134527524