Linux内核v5.4与v6.1间GPIO管理API变更问题咨询
Linux Kernel 5.4到6.1的GPIO管理API核心变更及适配方案
你的问题核心是legacy GPIO子系统API在6.1内核中已被逐步弃用,且默认配置可能禁用了相关兼容支持,导致原gpio_request()、gpio_export()等函数看似“生效”但实际无作用。以下是关键变更点及适配建议:
1. Legacy GPIO API的弃用状态
- 5.4内核中,
gpio_request()、gpio_direction_output()等属于legacy GPIO API,仍被广泛支持;但在6.1内核中,这类API已被标记为deprecated(已弃用),部分CONFIG选项默认关闭了兼容支持(如CONFIG_GPIO_SYSFS,对应gpio_export()依赖的sysfs GPIO接口)。 - 若强制使用legacy API,需确保内核配置中开启
CONFIG_LEGACY_GPIO和CONFIG_GPIO_SYSFS,但官方不推荐长期依赖。
2. 核心API替换(Descriptor-based GPIO子系统)
6.1内核推荐使用基于GPIO描述符(gpiod)的新API,替代所有legacy函数:
| Legacy API函数 | 替代的gpiod函数 | 说明 |
|---|---|---|
gpio_request(gpio, label) | gpiod_get(dev, con_id, flags) | 通过设备(或NULL表示全局)、连接ID(如设备树属性)获取GPIO描述符 |
gpio_request_one(gpio, flags, label) | gpiod_get(dev, con_id, flags) | 直接在flags中指定方向/初始电平,无需额外调用方向设置函数 |
gpio_direction_output(gpio, value) | gpiod_direction_output(gpiod, value) | 设置GPIO为输出模式并指定初始电平 |
gpio_set_value(gpio, value) | gpiod_set_value(gpiod, value) | 设置输出GPIO电平 |
gpio_get_value(gpio) | gpiod_get_value(gpiod) | 获取输入GPIO电平 |
gpio_export(gpio, direction_may_change) | gpiod_export(gpiod, direction_may_change) | 导出GPIO到sysfs(不推荐,sysfs GPIO已弃用) |
gpio_request_irq(gpio, irq_flags, handler, dev_id, label) | gpiod_request_irq(gpiod, irq_flags, handler, dev_id, label) | 申请GPIO中断 |
3. 关键适配细节
(1)GPIO编号方式变化
Legacy API依赖全局GPIO编号(如BBB上的60、49等),但新的gpiod API不推荐硬编码全局编号,建议通过**设备树(Device Tree)**获取GPIO:
- 在设备树中为驱动节点添加GPIO属性,例如:
my_driver_node { compatible = "my,vendor-driver"; led-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; // BBB上的GPIO60对应gpio1组第28号 key-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; }; - 驱动中通过连接ID(
con_id)获取:struct gpio_desc *led_gpiod = gpiod_get(dev, "led", GPIOD_OUT_LOW); struct gpio_desc *key_gpiod = gpiod_get(dev, "key", GPIOD_IN); if (IS_ERR(led_gpiod) || IS_ERR(key_gpiod)) { // 错误处理,返回对应的错误码 return PTR_ERR(led_gpiod); }
(2)错误处理逻辑变化
Legacy API返回0/-errno表示成功/失败;gpiod API返回struct gpio_desc*指针,失败时返回ERR_PTR(errno),需用IS_ERR()检查:
struct gpio_desc *gpiod = gpiod_get(NULL, "gpio60", GPIOD_OUT_LOW); if (IS_ERR(gpiod)) { pr_err("Failed to get GPIO: %ld\n", PTR_ERR(gpiod)); return PTR_ERR(gpiod); }
(3)sysfs GPIO接口的弃用
gpio_export()依赖的sysfs GPIO接口在6.1内核中已被标记为弃用,官方推荐使用libgpiod用户空间库(工具如gpiodetect、gpioinfo、gpioset)替代直接操作sysfs节点。若必须保留导出功能,需确保内核开启CONFIG_GPIO_SYSFS,并使用gpiod_export()替代gpio_export()。
4. 适配示例(原代码改造)
原5.4内核代码(legacy API)
#include <linux/gpio.h> #define LED_GPIO 60 #define KEY_GPIO 49 static int __init my_driver_init(void) { int ret; // 请求LED GPIO ret = gpio_request(LED_GPIO, "my_led"); if (ret) return ret; gpio_direction_output(LED_GPIO, 0); gpio_export(LED_GPIO, false); // 请求按键GPIO ret = gpio_request(KEY_GPIO, "my_key"); if (ret) goto err_led; gpio_direction_input(KEY_GPIO); gpio_export(KEY_GPIO, false); return 0; err_led: gpio_free(LED_GPIO); return ret; }
适配6.1内核的代码(gpiod API)
#include <linux/gpio/consumer.h> static struct gpio_desc *led_gpiod; static struct gpio_desc *key_gpiod; static int __init my_driver_init(void) { // 通过全局GPIO编号获取(不推荐,建议用设备树) led_gpiod = gpiod_get(NULL, "gpio60", GPIOD_OUT_LOW); if (IS_ERR(led_gpiod)) { pr_err("Failed to get LED GPIO: %ld\n", PTR_ERR(led_gpiod)); return PTR_ERR(led_gpiod); } gpiod_export(led_gpiod, false); key_gpiod = gpiod_get(NULL, "gpio49", GPIOD_IN); if (IS_ERR(key_gpiod)) { pr_err("Failed to get KEY GPIO: %ld\n", PTR_ERR(key_gpiod)); goto err_led; } gpiod_export(key_gpiod, false); return 0; err_led: gpiod_put(led_gpiod); return PTR_ERR(key_gpiod); } static void __exit my_driver_exit(void) { gpiod_put(key_gpiod); gpiod_put(led_gpiod); }
5. 额外注意事项
- BeagleBone Black的6.1内核设备树可能调整了GPIO组的映射关系,硬编码全局编号可能导致GPIO对应错误,务必通过设备树属性获取。
- 编译驱动时,需确保使用6.1内核的头文件,避免混合不同版本的API定义。
内容的提问来源于stack exchange,提问作者Maruko
相关产品推荐
相关产品推荐

