【Linux GPIO子系统】
·
gpio子系统
gpio接口
获取gpio
int of_get_named_gpio(struct device_node *np, const char *propname, int index) 获取gpio引脚编号
int gpio_request(unsigned gpio, const char *label) 申请单个 GPIO 引脚资源
int gpio_request_array(const struct gpio *array, size_t num) 批量申请多个 GPIO 引脚
设置方向:
int gpio_direction_input(unsigned gpio) 输入模式
int gpio_direction_output(unsigned gpio, int value) 输出模式
读写值
void gpio_set_value(unsigned int gpio, int value) 设置输出模式GPIO 的电平值
int gpio_get_value(unsigned int gpio) 读取 GPIO 的电平值
释放gpio
void gpio_free(unsigned gpio) 释放单个已申请的 GPIO 资源
void gpio_free_array(const struct gpio *array, size_t num) 批量释放多个已申请的 GPIO 资源
gpiod接口
| 接口 | 适用场景 | 核心特点 |
|---|---|---|
gpiod_get |
获取单个 GPIO(设备树单属性) | 基础款,需手动释放 |
gpiod_get_index |
获取 GPIO 数组中的单个 GPIO | 按索引选择,适配多 GPIO 属性 |
gpiod_get_array |
批量获取多个 GPIO | 返回数组,一次性操作多个 GPIO |
devm_gpiod_get |
单个 GPIO + 自动释放 | 无需手动 put,推荐优先使用 |
devm_gpiod_get_index |
GPIO 数组单个 GPIO + 自动释放 | 自动释放 + 索引选择 |
devm_gpiod_get_array |
批量 GPIO + 自动释放 | 批量获取 + 自动释放,最高效 |


gpio接口代码
led.h
#ifndef LED_HHHHH
#define LED_HHHHH
#define LED_TYPE 'H'
#define LED1_SET _IOW(LED_TYPE, 0, int)
#define LED2_SET _IOW(LED_TYPE, 1, int)
#define LED3_SET _IOW(LED_TYPE, 2, int)
#define LED_ON 0x1
#define LED_OFF 0x0
#endif
gpio_leds.c
#include <linux/kernel.h> // 内核核心头文件,提供printk、内核基础类型
#include <linux/module.h> // 模块管理头文件,提供module_platform_driver等宏
#include <linux/slab.h> // 内核内存分配头文件,提供kzalloc/kfree
#include <linux/gpio.h> // 传统GPIO操作头文件,提供gpio_request/gpio_set_value
#include <linux/platform_device.h> // 平台设备驱动核心头文件
#include <linux/uaccess.h> // 内核<->用户空间数据交互,提供copy_from_user
#include <linux/cdev.h> // 字符设备头文件,提供cdev_init/add/del等
#include <linux/of_gpio.h> // 设备树GPIO操作头文件,提供of_get_named_gpio
#include "led.h" // 自定义LED控制头文件(包含IOCTL命令等)
MODULE_LICENSE("GPL"); // 声明模块许可证为GPL,避免内核报警
#define DEVICE_NAME "leds_ctl" // 设备节点名称(/dev/leds_ctl)
#define DRIVER_NAME "leds_ctl" // 平台驱动名称
// 驱动私有数据结构体:整合LED驱动所需的所有资源
struct led_driver_t {
struct cdev cdev_leds; // 字符设备核心结构体
struct class *class_leds; // 设备类(用于创建/dev节点)
struct device_node *np; // 设备树节点指针(用于解析LED GPIO属性)
struct device *dev_leds; // 设备实例(对应/dev下的设备文件)
uint32_t led_gpio[10]; // 存储LED对应的GPIO编号(最多支持10个LED)
int led_num; // LED数量(从设备树读取)
dev_t devnum; // 字符设备号(主+次设备号)
};
// 字符设备open函数:用户态调用open("/dev/leds_ctl")时触发
int farsight_led_open(struct inode *inode, struct file *file)
{
// container_of:从inode中的cdev成员反向获取整个led_driver_t结构体
struct led_driver_t *leds = container_of(inode->i_cdev, struct led_driver_t, cdev_leds);
// 将驱动私有数据绑定到file结构体,供后续ioctl/close函数使用
file->private_data = leds;
return 0;
}
// 字符设备close函数:用户态调用close(fd)时触发
int farsight_led_close(struct inode *inode, struct file *file)
{
return 0; // 无资源需要释放,直接返回成功
}
// 字符设备IOCTL函数:处理用户态的LED控制指令
// cmd:控制命令(如LED1_SET/LED2_SET);arg:用户态传入的控制参数(亮/灭状态)
static long farsight_led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int state = 0; // 存储用户态传入的LED状态(0=灭,1=亮)
// 从file私有数据中获取驱动私有结构体
struct led_driver_t *leds = (struct led_driver_t *)file->private_data;
/* 获取用户传递过来的参数存储在state变量中 */
// copy_from_user:将用户态数据拷贝到内核态,失败返回非0
if(copy_from_user(&state, (void *)arg, sizeof(int)))
return -EFAULT; // 返回内存访问错误
/* 判断命令执行对应操作 */
switch(cmd) {
case LED1_SET: // 控制LED1
gpio_set_value(leds->led_gpio[0],state); // 设置LED1对应GPIO电平
break;
case LED2_SET: // 控制LED2
gpio_set_value(leds->led_gpio[1],state); // 设置LED2对应GPIO电平
break;
case LED3_SET: // 控制LED3
gpio_set_value(leds->led_gpio[2],state); // 设置LED3对应GPIO电平
break;
default: // 未知命令
return -EINVAL; // 返回无效参数错误
}
return 0; // 操作成功
}
// 字符设备操作集:绑定open/close/ioctl等函数
static struct file_operations farsight_led_ops = {
.owner = THIS_MODULE, // 声明所属模块,防止卸载时出错
.open = farsight_led_open, // 打开设备函数
.release = farsight_led_close, // 关闭设备函数(release对应close)
.unlocked_ioctl = farsight_led_ioctl, // IOCTL处理函数(无大内核锁,现代驱动标准)
};
// 平台驱动probe函数:设备树匹配成功/平台设备注册时触发(驱动初始化入口)
int farsight_led_probe(struct platform_device *pdev)
{
int i,ret;
// 申请驱动私有数据内存(静态变量避免栈溢出)
static struct led_driver_t *leds;
// kzalloc:分配内存并初始化为0(GFP_KERNEL:内核态通用分配标志)
leds = kzalloc(sizeof(struct led_driver_t),GFP_KERNEL);
if(leds == NULL) // 内存分配失败
{
ret = -ENOMEM; // 错误码:内存不足
printk("failed : no memory\n");
goto err_kzalloc; // 跳转到错误处理
}
// 获取设备树节点信息:从平台设备中提取对应的设备树节点
leds->np = pdev->dev.of_node;
// 从设备树读取LED数量(匹配设备树中"led_num"属性)
ret = of_property_read_u32(leds->np,"led_num",&leds->led_num);
if(ret < 0) // 读取失败
{
printk("failed : cannot get led number\n");
goto err_of_property_read_u32; // 跳转到错误处理
}
// 从设备树获取LED1对应的GPIO编号(匹配"led1-gpio"属性)
leds->led_gpio[0] = of_get_named_gpio(leds->np, "led1-gpio",0);
if(leds->led_gpio[0] < 0) // GPIO编号无效
{
printk("error get named gpio: %d\n", leds->led_gpio[0]);
goto err_of_get_named_gpio;
}
// 从设备树获取LED2对应的GPIO编号(匹配"led2-gpio"属性)
leds->led_gpio[1] = of_get_named_gpio(leds->np, "led2-gpio",0);
if(leds->led_gpio[1] < 0)
{
printk("error get named gpio: %d\n", leds->led_gpio[1]);
goto err_of_get_named_gpio;
}
// 从设备树获取LED3对应的GPIO编号(匹配"led3-gpio"属性)
leds->led_gpio[2] = of_get_named_gpio(leds->np, "led3-gpio",0);
if(leds->led_gpio[2] < 0)
{
printk("error get named gpio: %d\n", leds->led_gpio[2]);
goto err_of_get_named_gpio;
}
// 遍历所有LED,初始化GPIO
for(i=0; i<leds->led_num;i++)
{
// 向内核注册GPIO引脚,确保不被其他驱动抢占(第二个参数为NULL:无标签)
ret = gpio_request(leds->led_gpio[i],NULL);
if(ret){ // GPIO申请失败
printk("error requesting led gpio: %d\n",ret);
goto err_gpio_request;
}
// 设置GPIO为输出模式,初始电平为0(LED灭)
gpio_direction_output(leds->led_gpio[i],0);
}
// 初始化字符设备:绑定cdev与file_operations
cdev_init(&leds->cdev_leds, &farsight_led_ops);
// 动态分配字符设备号(主设备号由内核分配,次设备号从0开始,数量1)
ret = alloc_chrdev_region(&leds->devnum,0,1,"leds->ctl");
if(ret < 0) // 设备号分配失败
{
printk("err alloc_chrdev_region\n");
goto err_alloc_chrdev_region;
}
// 将字符设备添加到内核,完成注册
ret = cdev_add(&leds->cdev_leds, leds->devnum,1);
if(ret < 0) // cdev添加失败
{
printk("err cdev_add\n");
goto err_cdev_add;
}
// 创建设备类(在/sys/class/下创建leds_ctl目录)
leds->class_leds = class_create(THIS_MODULE,"leds_ctl");
if(IS_ERR(leds->class_leds)) // 类创建失败(返回错误指针)
{
ret = PTR_ERR(leds->class_leds); // 提取错误码
printk("err class_create\n");
goto err_class_create;
}
// 创建设备节点(在/dev/下创建leds_ctl文件)
device_create(leds->class_leds,NULL,leds->devnum,NULL,"leds_ctl");
// 保存驱动私有数据到平台设备,供remove函数获取
platform_set_drvdata(pdev,leds);
return 0; // probe成功
// 错误处理流程:反向释放已申请的资源(从后往前释放)
err_class_create:
cdev_del(&leds->cdev_leds); // 删除字符设备
err_cdev_add:
unregister_chrdev_region(leds->devnum,1); // 注销设备号
err_alloc_chrdev_region:
err_gpio_request: // GPIO申请失败时,需释放已申请的GPIO(此处未实现,可补充)
err_of_get_named_gpio: // 获取GPIO编号失败
err_of_property_read_u32: // 读取LED数量失败
kfree(leds); // 释放驱动私有数据内存
err_kzalloc: // 内存分配失败
return ret; // 返回错误码
}
// 平台驱动remove函数:驱动卸载/设备移除时触发(资源释放入口)
int farsight_led_remove(struct platform_device *pdev)
{
int i;
// 从平台设备中获取之前保存的驱动私有数据
struct led_driver_t *leds = platform_get_drvdata(pdev);
// 遍历释放所有LED对应的GPIO资源
for(i=0;i<leds->led_num;i++)
gpio_free(leds->led_gpio[i]);
// 反向释放字符设备相关资源
device_destroy(leds->class_leds,leds->devnum); // 销毁/dev下的设备节点
class_destroy(leds->class_leds); // 销毁/sys/class下的设备类
cdev_del(&leds->cdev_leds); // 删除字符设备
unregister_chrdev_region(leds->devnum,1); // 注销设备号
kfree(leds); // 释放驱动私有数据内存
return 0;
}
// 设备树匹配表:匹配设备树中compatible="farsight_gpio_led"的节点
static const struct of_device_id __maybe_unused led_of_match[] = {
{ .compatible = "farsight_gpio_led" },
{} // 空条目,结束匹配表
};
// 声明设备树匹配表,供内核自动匹配平台设备
MODULE_DEVICE_TABLE(of, led_of_match);
// 平台驱动结构体:绑定probe/remove函数和设备树匹配表
struct platform_driver farsight_led_driver = {
.probe = farsight_led_probe, // 匹配成功时的初始化函数
.remove = farsight_led_remove, // 卸载时的清理函数
.driver = {
.name = DRIVER_NAME, // 驱动名称(用于平台设备匹配)
.owner = THIS_MODULE, // 所属模块
.of_match_table = of_match_ptr(led_of_match), // 设备树匹配表
},
};
// 宏定义:自动完成平台驱动的注册和注销(替代module_init/module_exit)
module_platform_driver(farsight_led_driver);
gpiod接口代码
gpiod_leds.c
#include <linux/kernel.h> // 内核核心头文件,提供printk、内核基础类型
#include <linux/module.h> // 模块管理头文件,提供module_platform_driver等宏
#include <linux/slab.h> // 内核内存分配头文件,提供kzalloc/kfree
#include <linux/gpio.h> // 传统GPIO头文件(此处仅作为兼容,实际用gpiod接口)
#include <linux/platform_device.h> // 平台设备驱动核心头文件
#include <linux/uaccess.h> // 内核<->用户空间数据交互,提供copy_from_user
#include <linux/cdev.h> // 字符设备头文件,提供cdev_init/add/del等
#include <linux/of_gpio.h> // 设备树GPIO兼容头文件(实际用gpiod接口)
#include <linux/gpio/consumer.h> // 现代gpiod接口核心头文件(必须包含,原代码遗漏,补充注释)
#include "led.h" // 自定义LED控制头文件(包含IOCTL命令:LED1_SET等)
MODULE_LICENSE("GPL"); // 声明模块许可证为GPL,避免内核报警
#define DEVICE_NAME "leds_ctl" // 设备节点名称(/dev/leds_ctl)
#define DRIVER_NAME "leds_ctl" // 平台驱动名称
// 驱动私有数据结构体:整合LED驱动所需的所有资源(适配现代gpiod接口)
struct led_driver_t {
struct cdev cdev_leds; // 字符设备核心结构体
struct class *class_leds; // 设备类(用于创建/dev节点)
struct device_node *np; // 设备树节点指针(备用,gpiod接口已简化节点操作)
struct gpio_descs *led_gpio; // 批量GPIO描述符(替代传统GPIO编号数组)
int led_num; // LED数量(从设备树读取)
dev_t devnum; // 字符设备号(主+次设备号)
};
// 字符设备open函数:用户态调用open("/dev/leds_ctl")时触发
int farsight_led_open(struct inode *inode, struct file *file)
{
// container_of:从inode中的cdev成员反向获取整个led_driver_t结构体
struct led_driver_t *leds = container_of(inode->i_cdev, struct led_driver_t, cdev_leds);
// 将驱动私有数据绑定到file结构体,供后续ioctl/close函数使用
file->private_data = leds;
return 0;
}
// 字符设备close函数:用户态调用close(fd)时触发
int farsight_led_close(struct inode *inode, struct file *file)
{
return 0; // 无资源需要释放,直接返回成功
}
// 字符设备IOCTL函数:处理用户态的LED控制指令(适配gpiod接口)
// cmd:控制命令(LED1_SET/LED2_SET/LED3_SET);arg:用户态传入的亮灭状态(0/1)
static long farsight_led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int state = 0; // 存储用户态传入的LED状态(0=灭,1=亮)
// 从file私有数据中获取驱动私有结构体
struct led_driver_t *leds = (struct led_driver_t *)file->private_data;
/* 获取用户传递过来的参数存储在state变量中 */
// copy_from_user:将用户态数据拷贝到内核态,失败返回非0,返回-EFAULT错误
if(copy_from_user(&state, (void *)arg, sizeof(int)))
return -EFAULT;
/* 判断命令执行对应操作(使用gpiod接口设置电平) */
switch(cmd) {
case LED1_SET: // 控制第1个LED(对应gpio_descs数组第0个元素)
gpiod_set_value(leds->led_gpio->desc[0],state);
break;
case LED2_SET: // 控制第2个LED(对应gpio_descs数组第1个元素)
gpiod_set_value(leds->led_gpio->desc[1],state);
break;
case LED3_SET: // 控制第3个LED(对应gpio_descs数组第2个元素)
gpiod_set_value(leds->led_gpio->desc[2],state);
break;
default: // 未知命令,返回无效参数错误
return -EINVAL;
}
return 0; // 操作成功
}
// 字符设备操作集:绑定open/close/ioctl等函数(与传统版本一致)
static struct file_operations farsight_led_ops = {
.owner = THIS_MODULE, // 声明所属模块,防止卸载时出错
.open = farsight_led_open, // 打开设备函数
.release = farsight_led_close, // 关闭设备函数(release对应close)
.unlocked_ioctl = farsight_led_ioctl, // IOCTL处理函数(无大内核锁)
};
// 平台驱动probe函数:设备树匹配成功时触发(驱动初始化入口,核心适配gpiod接口)
int farsight_led_probe(struct platform_device *pdev)
{
int ret;
// 申请驱动私有数据内存(静态变量避免栈溢出,kzalloc初始化内存为0)
static struct led_driver_t *leds;
leds = kzalloc(sizeof(struct led_driver_t),GFP_KERNEL);
if(leds == NULL) // 内存分配失败
{
ret = -ENOMEM; // 错误码:内存不足
printk("failed : no memory\n");
goto err_kzalloc; // 跳转到错误处理
}
// 获取设备树节点信息(备用,gpiod接口已简化节点操作,可保留用于兼容)
leds->np = pdev->dev.of_node;
// 从设备树读取LED数量(匹配设备树中"led_num"属性)
ret = of_property_read_u32(leds->np,"led_num",&leds->led_num);
if(ret < 0) // 读取失败
{
printk("failed : cannot get led number\n");
goto err_of_property_read_u32;
}
// 核心:批量获取LED GPIO描述符(现代gpiod接口,替代传统gpio_request)
// devm_gpiod_get_array:自动释放版批量获取GPIO,无需手动释放
// 参数1:平台设备的dev指针;参数2:设备树属性名(匹配"leds-gpios");参数3:设为输出+初始低电平
leds->led_gpio = devm_gpiod_get_array(&pdev->dev,"leds",GPIOD_OUT_LOW);
if(IS_ERR(leds->led_gpio)) // 获取失败(返回错误指针)
{
printk("err devm_gpiod_get\n");
ret = PTR_ERR(leds->led_gpio); // 提取错误码(原代码遗漏赋值,补充注释)
goto err_devm_gpiod_get_array;
}
// 初始化字符设备:绑定cdev与file_operations(与传统版本一致)
cdev_init(&leds->cdev_leds, &farsight_led_ops);
// 动态分配字符设备号(主设备号由内核分配,次设备号从0开始,数量1)
ret = alloc_chrdev_region(&leds->devnum,0,1,"leds->ctl");
if(ret < 0)
{
printk("err alloc_chrdev_region\n");
goto err_alloc_chrdev_region;
}
// 将字符设备添加到内核,完成注册
ret = cdev_add(&leds->cdev_leds, leds->devnum,1);
if(ret < 0)
{
printk("err cdev_add\n");
goto err_cdev_add;
}
// 创建设备类(在/sys/class/下创建leds_ctl目录)
leds->class_leds = class_create(THIS_MODULE,"leds_ctl");
if(IS_ERR(leds->class_leds)) // 类创建失败(返回错误指针)
{
ret = PTR_ERR(leds->class_leds); // 提取错误码
printk("err class_create\n");
goto err_class_create;
}
// 创建设备节点(在/dev/下创建leds_ctl文件,用户态可访问)
device_create(leds->class_leds,NULL,leds->devnum,NULL,"leds_ctl");
// 保存驱动私有数据到平台设备,供remove函数获取
platform_set_drvdata(pdev,leds);
return 0; // probe成功
// 错误处理流程:反向释放已申请的资源(从后往前释放)
err_class_create:
cdev_del(&leds->cdev_leds); // 删除字符设备
err_cdev_add:
unregister_chrdev_region(leds->devnum,1); // 注销设备号
err_alloc_chrdev_region:
err_devm_gpiod_get_array: // gpiod获取失败(devm_接口无需手动释放GPIO)
err_of_property_read_u32: // 读取LED数量失败
kfree(leds); // 释放驱动私有数据内存
err_kzalloc: // 内存分配失败
return ret; // 返回错误码
}
// 平台驱动remove函数:驱动卸载/设备移除时触发(资源释放入口,gpiod自动释放)
int farsight_led_remove(struct platform_device *pdev)
{
// 从平台设备中获取之前保存的驱动私有数据
struct led_driver_t *leds = platform_get_drvdata(pdev);
// 释放字符设备相关资源(与传统版本一致)
device_destroy(leds->class_leds,leds->devnum); // 销毁/dev下的设备节点
class_destroy(leds->class_leds); // 销毁/sys/class下的设备类
cdev_del(&leds->cdev_leds); // 删除字符设备
unregister_chrdev_region(leds->devnum,1); // 注销设备号
kfree(leds); // 释放驱动私有数据内存
// 无需手动释放GPIO:devm_gpiod_get_array申请的资源由内核自动释放
return 0;
}
// 设备树匹配表:匹配设备树中compatible="farsight_gpiod_led"的节点(与传统版本区分)
static const struct of_device_id __maybe_unused led_of_match[] = {
{ .compatible = "farsight_gpiod_led" },
{} // 空条目,结束匹配表
};
// 声明设备树匹配表,供内核自动匹配平台设备
MODULE_DEVICE_TABLE(of, led_of_match);
// 平台驱动结构体:绑定probe/remove函数和设备树匹配表(与传统版本一致)
struct platform_driver farsight_led_driver = {
.probe = farsight_led_probe, // 匹配成功时的初始化函数
.remove = farsight_led_remove, // 卸载时的清理函数
.driver = {
.name = DRIVER_NAME, // 驱动名称
.owner = THIS_MODULE, // 所属模块
.of_match_table = of_match_ptr(led_of_match), // 设备树匹配表
},
};
// 宏定义:自动完成平台驱动的注册和注销(替代module_init/module_exit)
module_platform_driver(farsight_led_driver);
测试代码test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include "led.h"
int main(int argc, const char *argv[])
{
int fd, state;
fd = open("/dev/leds_ctl", O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}
while(1) {
state = LED_ON;
ioctl(fd, LED1_SET, &state);
ioctl(fd, LED2_SET, &state);
ioctl(fd, LED3_SET, &state);
sleep(1);
state = LED_OFF;
ioctl(fd, LED1_SET, &state);
ioctl(fd, LED2_SET, &state);
ioctl(fd, LED3_SET, &state);
sleep(1);
}
close(fd);
return 0;
}
更多推荐





所有评论(0)