Linux kernel devm_gpiod_get()函数详解
devm_gpiod_get()
是 Linux 内核中用于通过设备树(Device Tree)获取 GPIO 描述符(GPIO Descriptor)的设备资源管理(Managed Device Resources, Devres)函数。它是现代 Linux 驱动开发中推荐使用的 GPIO 操作接口,替代了传统的 gpio_request()
等函数,具备更安全、更简洁的资源管理能力。
核心作用
从设备树中解析指定的 GPIO 资源,生成一个 gpio_desc
结构体(GPIO 描述符),并通过 Devres 机制自动管理其生命周期(设备移除时自动释放)。
函数原型
#include <linux/gpio/consumer.h>struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags);
参数说明
**
struct device *dev
**
设备指针,表示当前要获取 GPIO 的设备(通常是驱动中probe()
函数传入的pdev
)。内核通过此设备找到设备树中对应的 GPIO 节点。**
const char *con_id
**
GPIO 的“连接标识符”,用于在设备树中匹配具体的 GPIO 属性。- 若设备树中 GPIO 属性名为
gpios
(最常见的通用情况),则con_id
通常设为NULL
(表示默认的gpios
属性)。 - 若设备树中为 GPIO 定义了别名(如
reset-gpios
、irq-gpios
),则con_id
应设为对应的名称(如"reset"
、"irq"
)。
- 若设备树中 GPIO 属性名为
**
enum gpiod_flags flags
**
GPIO 的配置标志,用于指定方向、初始状态等。常见取值:enum gpiod_flags {GPIOD_ASIS = 0,GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET,GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT |GPIOD_FLAGS_BIT_DIR_VAL,GPIOD_OUT_LOW_OPEN_DRAIN = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_OPEN_DRAIN,GPIOD_OUT_HIGH_OPEN_DRAIN = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_OPEN_DRAIN, };
GPIOD_IN
:输入模式(不指定初始值)。GPIOF_DIR_OUT
:输出模式。GPIOD_OUT_LOW
:输出模式下初始电平为低。GPIOD_OUT_HIGH
:输出模式下初始电平为高。- GPIOD_OUT_LOW_OPEN_DRAIN:开漏输出模式下初始电平为低。
- GPIOD_OUT_HIGH_OPEN_DRAIN:开漏输出模式下初始电平为高。
返回值
- 成功:返回指向
gpio_desc
结构体的指针(有效 GPIO 描述符)。 - 失败:返回
ERR_PTR(-errno)
(如-ENOENT
表示未找到 GPIO 资源,-EINVAL
表示参数错误等)。需用IS_ERR()
宏检查是否为错误指针。
工作原理
- 设备树关联:驱动通过
dev
参数关联到具体设备,内核根据设备的of_node
(设备树节点)查找con_id
对应的 GPIO 属性(如gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
)。 - 资源分配:内核为该 GPIO 分配一个
gpio_desc
描述符,并根据flags
配置其方向和初始状态。 - Devres 管理:
devm_
前缀表示该资源由 Devres 机制管理。当设备被移除(如remove()
函数调用或热拔插)时,内核会自动调用gpiod_put()
释放该 GPIO 描述符,无需手动干预。
典型使用场景
在驱动的 probe()
函数中获取 GPIO 描述符,后续通过 gpio_desc
操作 GPIO(如设置方向、读写电平、注册中断等)。例如:
// 驱动 probe 函数示例
static int my_driver_probe(struct platform_device *pdev)
{struct gpio_desc *reset_gpio;int ret;// 获取名为 "reset" 的 GPIO(设备树中对应 reset-gpios)reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_HIGH);if (IS_ERR(reset_gpio)) {dev_err(&pdev->dev, "Failed to get reset GPIO: %ld\n", PTR_ERR(reset_gpio));return PTR_ERR(reset_gpio);}// 使用 GPIO(例如:拉高电平触发复位)gpiod_set_value_cansleep(reset_gpio, 1);// ... 其他初始化逻辑 ...return 0;
}// 无需手动释放 reset_gpio!Devres 自动处理
相关函数
- **
devm_gpiod_get_optional()
**:获取可选的 GPIO(设备树中可能不存在)。若 GPIO 不存在,返回NULL
(而非错误),适用于非必需的 GPIO(如调试引脚)。 - **
devm_gpiod_put()
**:手动释放 GPIO 描述符(通常无需调用,Devres 自动释放)。 - **
gpiod_direction_input()
/gpiod_direction_output()
**:动态修改 GPIO 方向(需确保未被其他逻辑占用)。 - **
gpiod_get_value_cansleep()
/gpiod_set_value_cansleep()
**:安全读写 GPIO 电平(支持睡眠,适用于中断上下文外的场景)。
注意事项
设备树配置:必须确保设备树中为设备正确配置了 GPIO 属性(如
gpios
或自定义名称的*-gpios
),否则devm_gpiod_get()
会返回-ENOENT
错误。
示例设备树节点:my_device {compatible = "my,driver";reset-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>; // con_id="reset"irq-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; // con_id="irq" };
标志位组合:
flags
需正确组合方向和初始状态(如GPIOD_OUT_HIGH
),避免无效配置。并发访问:同一
gpio_desc
不可被多个驱动或同一驱动的不同实例重复获取,否则会导致资源冲突。错误处理:必须检查
devm_gpiod_get()
的返回值(通过IS_ERR()
),避免对空指针进行操作。
总结
devm_gpiod_get()
是现代 Linux 驱动开发中管理 GPIO 资源的标准接口,通过 Devres 机制自动管理生命周期,结合设备树实现了更清晰的硬件抽象。它替代了传统的 gpio_request()
等函数,是驱动开发中 GPIO 操作的首选方案。