驱动GPIO-获取单个gpio描述符

出识:获取单个gpio 描述符,初步使用gpio函数

文章目录


前言

通过获取gpio ,初步认识几个函数

  • gpiod_get
  • gpiod_get_index
  • gpiod_get_optional
  • piod_get_index_optional

环境准备

参考前文 将原理图中的一个引脚复用为gpio功能 的功能提供的环境,就用自己复用的gpio 配置来测试验证。

一、结构体gpio_desc

gpio_desc 结构体在 Linux 内核中代表一个 GPIO 引脚的描述符。它取代了旧的基于整数的 GPIO 编号方式,提供了更安全、更面向对象的 GPIO 管理方式。每个 GPIO 引脚在系统中都有一个对应的 gpio_desc 实例。

结构体定义

(基于 Linux 内核 5.x 版本,位置通常在 include/linux/gpio/consumer.h 和 drivers/gpio/gpiolib.h

java 复制代码
struct gpio_desc {
    struct gpio_device    *gdev;          // 指向所属的GPIO设备
    unsigned long         flags;          // 状态标志位
    /* 关键标志位包括: */
#define FLAG_REQUESTED    0               // 引脚已被请求
#define FLAG_IS_OUT       1               // 引脚配置为输出
#define FLAG_EXPORT       2               // 引脚已导出到sysfs
#define FLAG_SYSFS        3               // 在sysfs中显示
#define FLAG_ACTIVE_LOW   6               // 低电平有效
#define FLAG_OPEN_DRAIN   7               // 开漏输出
#define FLAG_OPEN_SOURCE  8               // 开源输出
#define FLAG_USED_AS_IRQ  9               // 用作中断引脚
#define FLAG_IS_HOGGED    10              // 引脚被预分配
#define FLAG_TRANSITORY   11              // 短暂状态变化

    /* 其他可能的标志... */
    const char            *label;         // GPIO的标签/名称
    struct irq_data       *irqdata;       // 中断相关数据
    char                  *name;          // GPIO名称 
    struct list_head      node;           // 用于链表管理
};

主要成员详解

结构体变量 struct gpio_device *gdev

  • 作用:指向管理这个 GPIO 的 GPIO 设备
  • 包含信息:GPIO 控制器的寄存器基地址、IRQ、支持的 GPIO 数量等
  • 重要成员
java 复制代码
struct gpio_chip *chip;      // GPIO控制器芯片
unsigned base;               // 这个设备的GPIO编号基值
int ngpio;                   // GPIO数量

flag 标志 unsigned long flags

  • 作用:记录 GPIO 的当前状态和配置
  • 重要标志:
java 复制代码
FLAG_REQUESTED:表示 GPIO 已被 gpio_request() 或 gpiod_get() 请求

FLAG_IS_OUT:GPIO 被配置为输出模式

FLAG_ACTIVE_LOW:逻辑反转,读取/写入的值会被反转

FLAG_OPEN_DRAIN:开漏输出模式

FLAG_USED_AS_IRQ:GPIO 被用作中断源

标志 const char *label

  • 作用:标识谁在使用这个 GPIO
  • 示例:"led-red", "reset-button" 等

二、gpiod_get 、 gpiod_get_index 、 gpiod_get_optional 、 piod_get_index_optional 方法的含义、区别和联系

方法 gpiod_get

含义

这是最基础、最常用的函数。它通过指定设备指针和 GPIO 的功能标签(Consumer Label)来获取一个 GPIO 描述符。

"功能标签" 用于说明这个 GPIO 在驱动中的用途,例如 "reset"、"power-enable"、"led" 等。它在设备树中对应 gpios 属性的标签部分。

函数原型

java 复制代码
struct gpiod_desc *gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags);

参数

  • dev: 使用这个 GPIO 的设备指针。

  • con_id: 功能标签(Consumer Label)。如果设备树中只定义了一个 GPIO,这个参数可以为 NULL。

  • flags: 指定 GPIO 的初始方向,是一个枚举值:

java 复制代码
GPIOD_ASIS: 不改变方向,保持原样(通常需要后续调用 gpiod_direction_ 函数来设置)。

GPIOD_IN: 初始化为输入。

GPIOD_OUT_LOW: 初始化为输出,并设置输出低电平。

GPIOD_OUT_HIGH: 初始化为输出,并设置输出高电平。

示例

假设设备树中有:

java 复制代码
my_device {
    compatible = "my-gpio-device";
    enable-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
    reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
};

驱动代码中:

java 复制代码
struct gpiod_desc *enable_gpio, *reset_gpio;

enable_gpio = gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
if (IS_ERR(enable_gpio)) {
    return PTR_ERR(enable_gpio); // 必须处理错误
}

reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset_gpio)) {
    gpiod_put(enable_gpio); // 释放之前申请的GPIO
    return PTR_ERR(reset_gpio);
}

特别注意:第二个参数 label ,其实是定义 gpio 的前缀。

方法 gpiod_get_index

含义

当某个功能标签(con_id)对应一组(多个)GPIO 时,使用这个函数来获取其中的某一个。它通过索引(index)来区分同组中的不同 GPIO。

函数原型

java 复制代码
struct gpiod_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags);

参数

  • index: 要获取的 GPIO 在该组中的索引(从 0 开始)。

示例

假设设备树中定义了一组 LED:

java 复制代码
my_device {
    compatible = "my-led-device";
    led-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>, // Index 0
                 <&gpio1 11 GPIO_ACTIVE_HIGH>, // Index 1
                 <&gpio1 12 GPIO_ACTIVE_HIGH>; // Index 2
};

驱动代码中获取第二个 LED(索引 1):

java 复制代码
struct gpiod_desc *led_gpio;

led_gpio = gpiod_get_index(dev, "led", 1, GPIOD_OUT_LOW);
if (IS_ERR(led_gpio)) {
    return PTR_ERR(led_gpio);
}

方法 gpiod_get_optional 与 gpiod_get_index_optional

含义

这两个函数是上面两个函数的"可选"版本。它们的核心区别在于:如果请求的 GPIO 不存在,它们不会返回错误,而是返回 NULL。

函数原型

java 复制代码
struct gpiod_desc *gpiod_get_optional(struct device *dev, const char *con_id, enum gpiod_flags flags);
struct gpiod_desc *gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags);

示例

java 复制代码
struct gpiod_desc *status_led_gpio;

// 这个状态灯是可选的,没有也没关系
status_led_gpio = gpiod_get_optional(dev, "status-led", GPIOD_OUT_LOW);
if (IS_ERR(status_led_gpio)) {
    // 处理真正的错误(非不存在)
    return PTR_ERR(status_led_gpio);
}

if (status_led_gpio) {
    // 如果 GPIO 存在,就使用它
    gpiod_set_value(status_led_gpio, 1);
} else {
    // GPIO 不存在,驱动可以正常继续,只是没有这个功能
    dev_info(dev, "No status LED available\n");
}

区别与联系总结

特性 gpiod_get gpiod_get_index gpiod_get_optional gpiod_get_index_optional
核心用途 获取单个/默认GPIO 获取同标签组中的特定GPIO 可选地获取单个/默认GPIO 可选地获取同标签组中的特定GPIO
索引支持 不支持 支持 (index参数) 不支持 支持 (index参数)
GPIO不存在时的行为 返回错误指针 (如 -ENOENT) 返回错误指针 (如 -ENOENT) 返回 NULL 返回 NULL
错误处理 必须用 IS_ERR() 检查 必须用 IS_ERR() 检查 检查 IS_ERR() 和 == NULL 检查 IS_ERR() 和 == NULL
适用场景 系统中必须存在的GPIO 系统中必须存在的GPIO组 可选、可缺失的GPIO 可选、可缺失的GPIO组
联系 是基础函数 是gpiod_get的带索引版本 是gpiod_get的可选版本 是gpiod_get_index的可选版本

函数家族关系

可以这样理解它们的联系:

基础对:

gpiod_get(dev, con_id, flags) 等价于 gpiod_get_index(dev, con_id, 0, flags)

可选对:

gpiod_get_optional(dev, con_id, flags) 等价于 gpiod_get_index_optional(dev, con_id, 0, flags)

索引扩展:

gpiod_get_index 扩展了 gpiod_get,使其能处理GPIO组。

gpiod_get_index_optional 扩展了 gpiod_get_optional,使其能处理GPIO组。

可选性扩展:

gpiod_get_optional 是 gpiod_get 的"宽松"版本。

gpiod_get_index_optional 是 gpiod_get_index 的"宽松"版本。

三、配合设备树验证 获取gpio 函数

程序源码

java 复制代码
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>

struct gpio_desc *mygpio1;  // GPIO 描述符指针
struct gpio_desc *mygpio2;  // GPIO 描述符指针
int num;  // GPIO 编号

//平台设备初始化函数
// 平台设备初始化函数
static int my_platform_probe(struct platform_device *dev)
{
    printk("This is my_platform_probe\n");

    // 获取可选的GPIO描述符
    mygpio1 = gpiod_get_optional(&dev->dev, "my", 0);
    if (mygpio1 == NULL) {
        printk("gpiod_get_optional error\n");
        return -1;
    }
    num = desc_to_gpio(mygpio1);
    printk("num is %d\n", num);

    // 释放GPIO描述符
    gpiod_put(mygpio1);

    // 获取指定索引的GPIO描述符
    mygpio2 = gpiod_get_index(&dev->dev, "my", 0, 0);
    if (IS_ERR(mygpio2)) {
        printk("gpiod_get_index error\n");
        return -2;
    }
    num = desc_to_gpio(mygpio2);
    printk("num is %d\n", num);

    return 0;
}

// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "my_platform_remove: Removing platform device\n");

    // 清理设备特定的操作
    // ...

    return 0;
}


const struct of_device_id of_match_table_id[]  = {
	{.compatible="mygpio"},
};

// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
    .probe = my_platform_probe,
    .remove = my_platform_remove,
    .driver = {
        .name = "my_platform_device",
        .owner = THIS_MODULE,
		.of_match_table =  of_match_table_id,
    },
};

// 模块初始化函数
static int __init my_platform_driver_init(void)
{
    int ret;

    // 注册平台驱动
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        printk(KERN_ERR "Failed to register platform driver\n");
        return ret;
    }

    printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");

    return 0;
}

// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
    // 注销平台驱动
	gpiod_put(mygpio2);
    platform_driver_unregister(&my_platform_driver);

    printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}

module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangchen");

编译-加载驱动-验证结果

得到的pin脚标号是32 ,我们测试的是引脚是GPIO1_A0,计算不就是:1*32+(A-1)*8+0=32

部分知识点和注意实现

  • 这里的驱动测试程序其实就是之前验证平台驱动的部分代码,函数.of_match_table = of_match_table_id, 配置的 compatible="mygpio" 不就是 上一篇将原理图中的一个引脚复用为gpio功能 中的添加节点的属性compatible 配置嘛。
java 复制代码
      my_gpio:gpio1_a0{
      compatible="mygpio"
      my-gpios= <&gpio1  RK_PA0  GPIO_ACTIVE_HIGH>;
	  pinctrl-0 = <&my_gpio_ctrl>;
	  };
  • 在方法 my_platform_probe 回调中,去验证 gpiod_get_optionalgpiod_get_index 方法,
    注意点 my_platform_probe(struct platform_device 中的参数是 平台设备platform_device,但是gpiod_get_optionalgpiod_get_index 方法中的设备是 device。 这个参数不正好是 platform_device的成员变量struct device dev; 嘛。
  • 释放描述结构体指针:gpiod_put ,释放 结构体描述指针
  • 注意点,带optional 的获取gpio描述符的,判断是否获取成功可以通过 是否是NULL 来判断,不包含optional 的方法,通过 IS_ERR 函数方法来判断是否获取成功。

总结

  • 这里在前面复用gpio引脚,配置环境的基础上,学习了解4个获取 gpio_desc 结构体的方法知识点。
  • 通过实验 , 结合设备数,再一次了解设备数配置和gpio获取的联系。
  • 本人简单的知识,但是还是务必掌握。