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;
}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐