4. GPIO 控制接口:核心 API 详解
在 Linux 内核驱动中,GPIO(通用输入输出)的软件控制通过一组标准内核 API 完成。本章聚焦于四个最基础的接口:gpio_request、gpio_direction_output、gpio_set_value 和 gpio_free。它们是驱动与硬件交互的“握手协议”,理解其生命周期与调用顺序是编写稳定驱动的前提。
4.1 接口全景:从申请到释放的生命周期
GPIO 控制遵循“申请 → 配置 → 操作 → 释放”的严格流程。任何跳过或顺序错误都可能导致资源泄漏或硬件异常。
核心原则: 每个 GPIO 在使用前必须通过
gpio_request 获得独占所有权,使用后必须通过 gpio_free 归还。内核不提供自动垃圾回收。
4.2 接口原型与参数详解
四个接口均定义在 #include <linux/gpio.h> 中。下表列出其完整原型、参数含义及返回值。
| 函数 | 原型 | 参数说明 | 返回值 |
|---|---|---|---|
| gpio_request | int gpio_request(unsigned gpio, const char *label) |
gpio: GPIO 编号;label: 用于调试的标签字符串 |
0 成功;负值错误码(如 -EBUSY) |
| gpio_direction_output | int gpio_direction_output(unsigned gpio, int value) |
gpio: 已申请的 GPIO 编号;value: 初始输出电平(0/1) |
0 成功;负值错误码 |
| gpio_set_value | void gpio_set_value(unsigned gpio, int value) |
gpio: 已配置为输出的 GPIO;value: 0 或 1 |
无返回值 |
| gpio_free | void gpio_free(unsigned gpio) |
gpio: 要释放的 GPIO 编号 |
无返回值 |
调试提示:
gpio_request 的 label 参数会出现在 /sys/kernel/debug/gpio 中,建议使用有意义的名称(如 "my_led"),便于排查资源冲突。
4.3 典型调用序列与代码模板
以下代码展示了一个完整的 GPIO 输出控制序列,包含错误处理和资源释放。
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/delay.h>
#define LED_GPIO 17 // 假设 GPIO 17 连接 LED
static int gpio_demo_init(void)
{
int ret;
// 步骤 1: 申请 GPIO
ret = gpio_request(LED_GPIO, "demo_led");
if (ret) {
pr_err("Failed to request GPIO %d: %d\n", LED_GPIO, ret);
return ret;
}
// 步骤 2: 配置为输出,初始电平为 0(LED 灭)
ret = gpio_direction_output(LED_GPIO, 0);
if (ret) {
pr_err("Failed to set direction for GPIO %d: %d\n", LED_GPIO, ret);
gpio_free(LED_GPIO); // 失败时立即释放
return ret;
}
// 步骤 3: 操作 GPIO(闪烁一次)
gpio_set_value(LED_GPIO, 1); // LED 亮
msleep(500); // 等待 500ms
gpio_set_value(LED_GPIO, 0); // LED 灭
return 0;
}
static void gpio_demo_exit(void)
{
// 步骤 4: 释放 GPIO
gpio_set_value(LED_GPIO, 0); // 确保释放前为安全状态
gpio_free(LED_GPIO);
}
常见错误: 在
gpio_direction_output 失败后直接返回,忘记调用 gpio_free 会导致 GPIO 资源泄漏,后续驱动无法申请该引脚。
4.4 接口行为对比:原子性、上下文与功耗
不同接口在原子性、可调用上下文和功耗影响上存在差异,驱动开发者需根据场景选择。
| 接口 | 原子性 | 可调用上下文 | 功耗影响 | 典型场景 |
|---|---|---|---|---|
| gpio_request | 非原子(可能睡眠) | 进程上下文 | 无直接功耗影响 | 模块初始化、probe 函数 |
| gpio_direction_output | 非原子(可能睡眠) | 进程上下文 | 配置引脚驱动能力 | 初始化阶段 |
| gpio_set_value | 原子(快速路径) | 任意上下文(含中断) | 动态功耗(电平翻转) | 中断处理、高频 PWM |
| gpio_free | 非原子(可能睡眠) | 进程上下文 | 无直接功耗影响 | 模块卸载、错误回滚 |
4.5 错误处理与资源管理最佳实践
- 检查返回值:
gpio_request和gpio_direction_output必须检查返回值,失败时执行清理。 - 释放顺序: 先调用
gpio_set_value将引脚置于安全电平(如低电平),再调用gpio_free。 - 避免重复申请: 同一 GPIO 编号在未释放前再次
gpio_request会返回-EBUSY。 - 使用 devm_ 版本: 在平台驱动中优先使用
devm_gpio_request,设备移除时自动释放。
经验法则: 在驱动的
probe 函数中申请并配置 GPIO,在 remove 函数中释放。错误路径必须包含 goto 标签统一清理,避免重复代码。