4. Linux GPIO 子系统框架:gpiolib 架构、gpio_chip 结构体、gpio_desc 描述符、sysfs 与字符设备接口
在全志 T113 平台上,GPIO 驱动并非直接操作寄存器,而是通过 Linux 内核标准的 GPIO 子系统(gpiolib) 进行管理。gpiolib 为内核空间和用户空间提供了统一的 GPIO 操作抽象层,屏蔽了不同 SoC 的寄存器差异。本章将深入剖析 gpiolib 的软件架构、核心数据结构以及用户空间访问接口。
gpio_chip,将每个引脚抽象为 gpio_desc,并提供 sysfs 和字符设备两种用户空间接口。
4.1 gpiolib 软件架构总览
gpiolib 采用分层设计,从上到下依次为:用户空间接口层、内核空间 API 层、gpiolib 核心层、芯片驱动层。每一层都依赖下一层提供的服务,并通过标准的数据结构进行交互。
gpio_chip 结构体中的回调函数。上层的 API 和用户空间接口由内核 gpiolib 核心自动提供。
4.2 gpio_chip 结构体:GPIO 控制器的抽象
gpio_chip 是 gpiolib 中最核心的数据结构,用于描述一个 GPIO 控制器(例如全志 T113 的 PA、PB、PC 等端口组)。每个 GPIO 控制器对应一个 gpio_chip 实例。
| 成员名称 | 类型 | 描述 |
|---|---|---|
label |
const char * |
控制器名称,如 "PA", "PB" |
base |
int |
该控制器管理的 GPIO 起始编号(全局唯一) |
ngpio |
int |
该控制器管理的 GPIO 引脚数量 |
parent |
struct device * |
父设备指针,用于设备模型关联 |
request |
int (*)(struct gpio_chip *, unsigned offset) |
请求引脚时调用,可做电源管理或冲突检测 |
free |
void (*)(struct gpio_chip *, unsigned offset) |
释放引脚时调用 |
direction_input |
int (*)(struct gpio_chip *, unsigned offset) |
设置引脚为输入方向 |
direction_output |
int (*)(struct gpio_chip *, unsigned offset, int value) |
设置引脚为输出方向,并设置初始电平 |
get |
int (*)(struct gpio_chip *, unsigned offset) |
读取引脚当前电平值 |
set |
void (*)(struct gpio_chip *, unsigned offset, int value) |
设置引脚输出电平值 |
to_irq |
int (*)(struct gpio_chip *, unsigned offset) |
将 GPIO 引脚映射为中断号 |
gpio_chip 实例。每个实例的 base 值必须全局唯一且不重叠,通常由内核自动分配(传入 -1)。
4.3 gpio_desc 描述符:GPIO 引脚的内核抽象
在 gpiolib 内部,每个 GPIO 引脚使用 gpio_desc 结构体描述。该结构体包含了引脚的状态、标志、标签以及所属的 gpio_chip 指针。内核通过 gpio_desc 实现对 GPIO 引脚的精细化管理。
struct gpio_desc {
struct gpio_chip *chip; /* 所属的 gpio_chip */
unsigned long flags; /* 引脚状态标志 */
const char *label; /* 使用者标签 */
int irq; /* 关联的中断号 */
/* ... 其他内部字段 */
};
关键标志位(flags)包括:
- FLAG_REQUESTED — 引脚已被请求
- FLAG_IS_OUT — 引脚配置为输出方向
- FLAG_ACTIVE_LOW — 引脚逻辑电平反转
- FLAG_OPEN_DRAIN — 开漏输出
- FLAG_OPEN_SOURCE — 开源输出
- FLAG_USED_AS_IRQ — 引脚被用作中断源
/sys/kernel/debug/gpio 中可以查看所有 gpio_desc 的实时状态,包括是否被请求、方向、当前电平值等,是调试 GPIO 冲突的利器。
4.4 sysfs 接口:传统的用户空间 GPIO 访问方式
sysfs 接口通过 /sys/class/gpio 目录暴露 GPIO 操作,适合简单的控制场景。虽然在新内核中逐渐被字符设备接口取代,但在嵌入式开发中仍广泛使用。
| sysfs 路径 | 操作 | 说明 |
|---|---|---|
/sys/class/gpio/export |
写入 GPIO 编号 | 导出 GPIO 到用户空间 |
/sys/class/gpio/unexport |
写入 GPIO 编号 | 取消导出 GPIO |
/sys/class/gpio/gpioN/direction |
读/写 | 读取或设置方向(in/out/high/low) |
/sys/class/gpio/gpioN/value |
读/写 | 读取或设置电平值(0/1) |
/sys/class/gpio/gpioN/edge |
读/写 | 设置中断触发边沿(none/rising/falling/both) |
/sys/class/gpio/gpioN/active_low |
读/写 | 设置逻辑电平反转(0/1) |
使用示例(操作全志 T113 的 PA0,假设 GPIO 编号为 0):
# 导出 GPIO0
echo 0 > /sys/class/gpio/export
# 设置为输出方向,初始高电平
echo high > /sys/class/gpio/gpio0/direction
# 输出低电平
echo 0 > /sys/class/gpio/gpio0/value
# 设置为输入方向
echo in > /sys/class/gpio/gpio0/direction
# 读取电平
cat /sys/class/gpio/gpio0/value
# 设置上升沿中断
echo rising > /sys/class/gpio/gpio0/edge
# 取消导出
echo 0 > /sys/class/gpio/unexport
4.5 字符设备接口:新一代用户空间 GPIO 访问方式
从 Linux 4.8 开始,内核引入了基于字符设备的 GPIO 用户空间接口,通过 /dev/gpiochipX 设备文件提供更高效、更灵活的 GPIO 操作。该接口支持批量操作、事件监听和更精细的控制。
字符设备接口的核心数据结构:
struct gpiohandle_request {
unsigned int lineoffsets[GPIOHANDLES_MAX]; /* 引脚偏移数组 */
unsigned int flags; /* 操作标志 */
unsigned int default_values[GPIOHANDLES_MAX];/* 默认输出值 */
char consumer_label[32]; /* 使用者标签 */
unsigned int lines; /* 操作引脚数量 */
int fd; /* 返回的文件描述符 */
};
struct gpioevent_request {
unsigned int lineoffset; /* 引脚偏移 */
unsigned int handleflags; /* 句柄标志 */
unsigned int eventflags; /* 事件标志 */
char consumer_label[32]; /* 使用者标签 */
int fd; /* 返回的事件文件描述符 */
};
字符设备接口的优势:
- 低延迟: 通过 ioctl 系统调用,减少文件系统开销
- 批量操作: 一次 ioctl 可同时操作多个 GPIO 引脚
- 事件驱动: 支持 epoll/select 监听 GPIO 中断事件
- 更安全: 支持细粒度的权限控制
4.6 全志 T113 GPIO 编号映射规则
全志 T113 的 GPIO 编号由 gpiolib 自动分配,但了解其映射规则有助于调试。每个端口组对应一个 gpio_chip,其 base 值由内核动态分配。通常的映射关系如下:
| 端口组 | 引脚范围 | 典型 base 值 | 引脚数量 |
|---|---|---|---|
| PA | PA0 ~ PA21 | 0 | 22 |
| PB | PB0 ~ PB15 | 22 | 16 |
| PC | PC0 ~ PC7 | 38 | 8 |
| PD | PD0 ~ PD25 | 46 | 26 |
| PE | PE0 ~ PE15 | 72 | 16 |
| PF | PF0 ~ PF7 | 88 | 8 |
| PG | PG0 ~ PG15 | 96 | 16 |
| PH | PH0 ~ PH11 | 112 | 12 |
| PI | PI0 ~ PI15 | 124 | 16 |
echo 41 > /sys/class/gpio/export。
4.7 本章小结
本章详细介绍了 Linux GPIO 子系统的核心框架:
- gpiolib 架构 采用四层设计,将用户空间、内核 API、核心管理和芯片驱动分离。
- gpio_chip 是 GPIO 控制器的抽象,驱动开发者需要实现其回调函数来操作硬件寄存器。
- gpio_desc 是每个 GPIO 引脚的内核描述符,包含状态标志和关联信息。
- sysfs 接口 适合简单控制,但延迟较高;字符设备接口 性能更优,支持批量操作和事件监听。
- 全志 T113 的 GPIO 编号映射遵循端口组 + 偏移的规则,理解此映射对调试至关重要。