Linux的GPIO子系统解析 ( 一 ) 之 gpiolib.c

2023-11-03

Linux的GPIO子系统解析 ( 一 ) 之 gpiolib.c

绪论

Linux中有非常多的子系统实现,甚至连LED都有相应的子系统,学好内核中的各种驱动相关的子系统,可以让我们写驱动更加高效,不用重复造轮子,尽量利用内核给我们提供的机制减轻工作量同时又达到高效可用。本章不涉及具体的函数代码,只涉及具体的函数的作用,代码需读者自行去查看

关于GPIO子系统库文件的gpiolib.c解析

drivers/gpio/gpiolib.c

gpio_desc结构体

其中涉及一个重要结构体 gpio_desc
该结构体用来描述一个GPIO 我们主要关注其中包含的gpio_chip结构体

struct gpio_desc {
	struct gpio_chip	*chip;  //每个GPIO对应一个gpio_chip结构体
	unsigned long		flags;  //接下来是这个gpio_desc的一些标志描述的位定义 
/* flag symbols are bit numbers */
#define FLAG_REQUESTED	0  //标志gpio是否已被请求
#define FLAG_IS_OUT	1      //标志该gpio的默认输出状态
#define FLAG_RESERVED	2   //标志gpio是否被保留 保留的gpio不能被申请
#define FLAG_EXPORT	3	/* protected by sysfs_lock */
#define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL	5	/* 下降沿触发*/ 
#define FLAG_TRIG_RISE	6	/* 上升沿触发 */
#define FLAG_ACTIVE_LOW	7	/* sysfs value has active low */

#define ID_SHIFT	16	/* add new flags before this one */

#define GPIO_FLAGS_MASK		((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK	(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

#ifdef CONFIG_DEBUG_FS
	const char		*label;
#endif
};
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //为所有GPIO都定义一个gpio_desc数组

相应的ARCH_NR_GPIOS定义在你的arch/arm/你的架构/include/mach/gpio.h中定义
比如我用的芯片是EXYNOS4412 所以相应的目录就在 arch/arm/mach-exynos/include/mach/gpio.h
EXYNOS4412的GPIO数量定义
具体整个宏展开后就是板子上最大的GPIO数量了,这里不做展开,有兴趣可以看看该头文件的定义就知道了

gpio_chip结构体

接着我们来看gpio_desc中的一个重要结构 gpio_chip 该结构包含了对gpio的各种操作方法,比如设置gpio的输入输出设置gpio的输出值或者获取值等 是核心重点

struct gpio_chip {
	const char		*label;  //名字
	struct device		*dev;  //对应的设备结构体
	struct module		*owner; //所属模块 在驱动中经常出现的字段
	
	int			(*request)(struct gpio_chip *chip,
						unsigned offset); //申请一个gpio的函数 主要用来防止gpio冲突使用
	void			(*free)(struct gpio_chip *chip,
						unsigned offset);//用完了gpio后要调用free函数释放

	int			(*direction_input)(struct gpio_chip *chip,
						unsigned offset); //设定GPIO为输入
	int			(*get)(struct gpio_chip *chip,
						unsigned offset); //获取gpio的值
	int			(*direction_output)(struct gpio_chip *chip,
						unsigned offset, int value); //设定gpio为输出
	int			(*set_debounce)(struct gpio_chip *chip,
						unsigned offset, unsigned debounce);//设置去抖动的时间 按键可能会用到

	void			(*set)(struct gpio_chip *chip,
						unsigned offset, int value);//设置gpio的值

	int			(*to_irq)(struct gpio_chip *chip,
						unsigned offset);

	void			(*dbg_show)(struct seq_file *s,
						struct gpio_chip *chip);
	int			base;   //所管辖的起始GPIO号
	u16			ngpio;  //所管辖的GPIO数量
	const char		*const *names;
	unsigned		can_sleep:1;
	unsigned		exported:1;

#if defined(CONFIG_OF_GPIO) //这里是为了设备树中定义的GPIO而准备的
	/*
	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
	 * device tree automatically may have an OF translation
	 */
	struct device_node *of_node; //对应设备树的GPIO节点
	int of_gpio_n_cells; //gpio的个数 
	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
		        const void *gpio_spec, u32 *flags); //定义设备树的信息转换函数
#endif
};

该库中所涉及的函数作用我简单说一下:

gpio_ensure_requested函数
 int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset)
 该函数用来确保gpio被申请,如果没有申请的话调用该函数会自动去申请,
 并且会发出警告,并将GPIO描述符的label字段置为"[auto]"
gpio_to_chip函数
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
获取相应GPIO的的gpio_chip结构体
gpiochip_find_base函数
static int gpiochip_find_base(int ngpio)
动态的在GPIO的描述符数组中寻找连续的ngpio个空闲GPIO,并返回该连续GPIO的第一个序号
主要是用于给那些拥有可热插拔GPIO的设备
gpiochip_reserve函数
int __init gpiochip_reserve(int start, int ngpio)
该函数用来将指定范围的GPIO设为不可动态分配,为什么要这么做呢?是因为对应的设备还没准备好
所以此时必须把这些IO给禁止掉,如果指定范围内有任何一个GPIO已经被设置为保留,该函数失败返回-EBUSY

还有一些函数只有在开启了 CONFIG_GPIO_SYSFS才会启用
主要是为了定义了一些show,store方法方便在文件系统中查看GPIO的信息状态
这里不解释

下面的所有函数都是导出函数 可被外部模块所使用 也就是提供给外部模块的接口

导出函数

gpiochip_add函数
int gpiochip_add(struct gpio_chip *chip)
注册一个gpio_chip,注册失败返回负数,成功返回0 
该函数在引导过程中很早就被调用,以便GPIO能够自由使用
chip->dev必须在GPIO框架的arch_initcall()前注册好,否则sysfs的初始化会因为GPIO而失败
该函数实际就是根据chip所管辖的GPIO范围,将gpio描述数组中的相关gpio的chip字段设为该chip
接着将chip->ofnode设置为chip->dev->of_node 最后调用gpiochip_export将芯片信息导出到文件系统中
这一步只有有开SYSFS才会做 这里不分析 后面独立来分析
gpiochip_remove函数
int gpiochip_remove(struct gpio_chip *chip)
注销指定的gpio_chip 会同时减少对应ofnode的引用计数,实际就是把所管辖的GPIO的chip字段全部设为NULL
同时取消对SYSFS的导出
gpiochip_find函数
struct gpio_chip *gpiochip_find(void *data,
				int (*match)(struct gpio_chip *chip, void *data))
用于定位特定的gpio_chip 用户需要自己构造一个match函数 之后gpiochip_find会对每一个gpio_chip调用该函数
成功匹配后会返回该chip

gpio_request函数
int gpio_request(unsigned gpio, const char *label)
请求注册一个GPIO,并设置gpio_desc的label和FLAG_REQUESETED位,最后再调用chip->request
gpio_request_one函数
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
该函数请求一个GPIO,并对其进行默认的GPIO方向设置 
其实就是调用gpio_request再根据方向调用gpio_direction_xxxx
gpio_request_array函数
int gpio_request_array(const struct gpio *array, size_t num)
该函数为以下gpio数组循环调用gpio_request_one请求每个gpio
struct gpio {
	unsigned	gpio;
	unsigned long	flags;
	const char	*label;
};
gpio_free_array函数
void gpio_free_array(const struct gpio *array, size_t num)
为指定的gpio数组循环调用gpio_free
gpiochip_is_requested函数
const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
返回指定chip管辖的第offset个GPIO的label
gpio_direction_input / gpio_direction_output函数
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
最终通过对应的chip->direction_input/output来完成实际操作 如果对应gpio没有被request 也会调用chip->request
gpio_set_debounce函数
int gpio_set_debounce(unsigned gpio, unsigned debounce)
做一些参数的检查工作,最终调用chip->set_debounce(chip, gpio, debounce)
__gpio_get_value / __gpio_set_value 函数
int __gpio_get_value(unsigned gpio)
void __gpio_set_value(unsigned gpio, int value)
调用chip->get/set
__gpio_cansleep 函数
int __gpio_cansleep(unsigned gpio)
返回chip->can_sleep
__gpio_to_irq函数
int __gpio_to_irq(unsigned gpio)
返回gpio对应的中断irq数量
gpio_get_value_cansleep / gpio_set_value_cansleep函数
int gpio_get_value_cansleep(unsigned gpio)
void gpio_set_value_cansleep(unsigned gpio, int value)
调用后可能会发生休眠 取决于内核是否开启了CONFIG_PREEMPT_VOLUNTARY
最终会调用chip_get/set

如何去使用该GPIO库?

    gpiolib库可以看出依赖于gpio_chip来提供最终的gpio操作,所以我们如果要用这个gpiolib,就要定义各chip,然后调用gpiochip_add来注册 之后就可以使用该gpiolib提供的gpio的操作函数了
    一般如果linux支持你的板子的芯片的话,会有一个你的芯片专用的.c文件 在里面就会定义一系列的gpiochip 然后会调用gpio_add去注册
    比如我的板子是exynos4412 在driver/gpio/gpio-samsung.c中就会有这些gpiochip的定义和注册。但是如果我们的芯片没有支持的话,你也可以自己定义gpiochip使得gpiolib库被支持
我们的驱动程序要使用该库 需 #include <mach/gpio.h> 使用该库操作gpio,可省去我们繁琐的io映射及io配置 假设我们要使用该库在驱动中,我们可以这么做:

1. 添加头文件 #include <mach/gpio.h>
2. 调用gpio_direction_input / gpio_direction_input
3. 使用__gpio_get_value / __gpio_set_value / gpio_get_value_cansleep / gpio_set_value_cansleep 等函数操作gpio

读者可能好奇,难道我不用ioremap去映射寄存器就可以直接操作了吗?是的,其实kernel在初始化时已经对IO寄存器有了映射,而我们的gpiolib.c正是直接使用了这些已经映射好的地址,省去了我们自己去映射的那些琐碎麻烦。
GPIO库的流程示意图如下:
在这里插入图片描述
下一章我们将来解析gpio-samsung.c中是如何定义gpiochip 以及如何完成整个注册过程的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux的GPIO子系统解析 ( 一 ) 之 gpiolib.c 的相关文章

  • 何时用引号将 shell 变量括起来?

    我应该或不应该在 shell 脚本中用引号括住变量吗 例如 下列说法正确的是 xdg open URL eq 2 or xdg open URL eq 2 如果是这样 为什么 一般规则 如果它可以为空或包含空格 或实际上任何空格 或特殊字符
  • Qt 嵌入式触摸屏 QMouseEvents 在收到 MouseButtonRelease 之前未收到

    我在带有触摸屏的小型 ARM 嵌入式 Linux 设备上使用 Qt 4 8 3 我的触摸屏配置了 tslib 并对其进行了校准 因此 etc 中有一个 pointcal 文件 我的触摸事件的位置工作得很好 但无论如何我都会在鼠标按下或鼠标释
  • PIL 的 Image.show() 带来*两个*不同的查看器

    在 python shell 中处理图像时 我使用 image show 其中 image 是 Image 的实例 很久以前什么也没发生 但在定义了一个名为 xv 的 Mirage 符号链接后 我很高兴 最近几天 show 将显示 Imag
  • 选择fasta文件中氨基酸超过300个且“C”出现至少4次的序列

    我有一个包含蛋白质序列的 fasta 文件 我想选择超过 300 个氨基酸且半胱氨酸 C 氨基酸出现超过 4 次的序列 我使用此命令来选择具有超过 300 个 aa 的序列 cat 72hDOWN fasta fasta bioawk c
  • 应用程序中两个不同版本的库

    考虑一个场景 其中有两个不同版本的共享库 考虑 A 1 so 链接到 B so A 2 so 链接到 C so 现在 B so 和 C so 都链接到 d exe 当 B so 想要调用 A 1 so 中的函数时 它最终会调用 A 2 so
  • 我想在 Red Hat Linux 服务器中执行 .ps1 powershell 脚本

    我有一个在窗口中执行的 ps1 powershell 脚本 但我的整个数据都在 Linux 服务器中 有什么可能的方法可以让我在红帽服务器中执行 powershell 脚本 powershell脚本是 Clear Host path D D
  • 如何在Linux上用C/C++编写Web服务器[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在考虑在 Linux 平台上开发一个小型 阅读 初级 Web 服务器 但我不知道从哪里开始 我希望它能够做的是 监听特定端口 接受
  • 如何将一个文本文件拆分为多个 *.txt 文件?

    我有一个文本文件file txt 12 MB 包含 something1 something2 something3 something4 有没有办法分开file txt分成 12 个 txt 文件 比方说file2 txt file3 t
  • Vagrant 遇到问题 - “404 - 未找到”

    我正在尝试使用 Vagrant 制作一个 LAMP 盒子 有人告诉我它使用起来非常简单 我对网络和虚拟机完全陌生 对 Linux Ubuntu 的经验也很少 我目前已尝试按照官方文档页面上的教程进行操作 http docs vagrantu
  • 在 scapy 中通过物理环回发送数据包

    我最近发现了 Scapy 它看起来很棒 我正在尝试查看 NIC 上物理环回模块 存根上的简单流量 但是 Scapy sniff 没有给出任何结果 我正在做的发送数据包是 payload data 10 snf sniff filter ic
  • 使用 ioctl 在 C++ 中以编程方式添加路由

    我编写了简单的 C 函数 添加了新路线 void addRoute int fd socket PF INET SOCK DGRAM IPPROTO IP struct rtentry route memset route 0 sizeof
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • bash while 循环的布尔表达式中的 -lt 意味着什么?

    我猜测它代表小于基于输出 但是我在哪里可以找到有关此语法的文档 bin bash COUNTER 0 while COUNTER lt 10 do echo The counter is COUNTER let COUNTER COUNTE
  • 如何在 Linux 和 C 中使用文件作为互斥体?

    我有不同的进程同时访问 Linux 中的命名管道 并且我想让此访问互斥 我知道可以使用放置在共享内存区域中的互斥体来实现这一点 但作为一种家庭作业 我有一些限制 于是 我想到的是对文件使用锁定原语来实现互斥 我做了一些尝试 但无法使其发挥作
  • “git add”返回“致命:外部存储库”错误

    我刚刚进入 git 的奇妙世界 我必须提交我对程序所做的一系列更改 位于名为的目录中 var www myapp 我创建了一个新目录 home mylogin gitclone 从这个目录中 我做了一个git clone针对公共回购 我能够
  • 如何并行执行4个shell脚本,我不能使用GNU并行?

    我有4个shell脚本dog sh bird sh cow sh和fox sh 每个文件使用 xargs 并行执行 4 个 wget 来派生一个单独的进程 现在我希望这些脚本本身能够并行执行 由于某些我不知道的可移植性原因 我无法使用 GN
  • Ubuntu Python shebang 线不工作

    无法让 shebang 线在 Ubuntu 中为 python 脚本工作 我每次只收到命令未找到错误 test py usr bin env python print Ran which python usr bin python 在 sh
  • 在内核代码中查找函数的最佳方法[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我开始浏览内核代码 遇到的一件事是如何跟踪函数调用 结构定义等 有没有一种好的方法可以快速跳转到函数定义并退出 我尝试过 Source N
  • ALSA:snd_pcm_writei 调用时缓冲区不足

    当运行我最近从灰烬中带回来的旧程序时 我遇到了缓冲区不足的情况 该程序将原始声音文件完全加载到内存中 2100 字节长 525 帧 并准备 ALSA 进行输出 44 1khz 2 通道 有符号 16 位 if err snd pcm set
  • 适用于 Linux 的轻量级 IDE [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi

随机推荐

  • 目标检测(三)损失函数

    目标检测 三 损失函数 开始 一 匹配策略 二 损失函数 三 Hard negative mining 总结 开始 内容参考 Datawhale Task03 化劲儿 损失函数设计 一 匹配策略 我们要想让其预测类别和目标框信息 我们先要知
  • 如何使用multipart/form-data格式上传文件

    有时 在网络编程过程中需要向服务器上传文件 Multipart form data是上传文件的一种方式 Multipart form data其实就是浏览器用表单上传文件的方式 最常见的情境是 在写邮件时 向邮件后添加附件 附件通常使用表单
  • Django安装操作教程

    一 环境准备 确保已安装好python和pycharm工具 二 django安装并配置环境变量 方法一 cmd中命令安装 pip install i https pypi douban com simple django 或 指定相应的dj
  • tr字符使用

    当我们把文件从Windows传到Linux环境时候 常常在每一行的末尾 会出现一些 M的字符 而这些字符会影响文件的正常读写和执行 要去掉这些 M 字符 有很多种办法 比如直接dox2unix 也可以使用一些命令去处理 比如 删除Windo
  • 406. Queue Reconstruction by Height

    class Solution public vector
  • c++学习笔记二十——派生类的构造函数,复制构造函数和析构函数

    在讲派生类的构造和析构函数时候我们先介绍类的兼容性 类的兼容性 类的兼容性是指在需要基类对象的任何地方都可以使用派生类来替代 通过继承 派生类得到了除了基类构造函数 复制函数中的所有成员 这样公有派生类实际具备了基类所有的功能 凡是基类所能
  • 基于 BEM 规范实现简单的全局 scss

    该文章是在学习 小满vue3 课程的随堂记录 示例均采用
  • One PUNCH Man——变量显著性检验

    文章目录 显著性检验简介 t检验 单侧检验与双侧检验 区别在于是否知道标准 确定P值和做出推断结论 T检验例子 栗子no 1 栗子No 2 F检验 判断一个变量是否显著 我们一般采用T检验和F检验的方式 显著性检验简介 假设检验也叫显著性检
  • STM32单片机颜色识别分拣系统颜色名称显示2路舵机分拣

    实践制作DIY GC0120 颜色识别分拣系统 一 功能说明 基于STM32单片机设计 颜色识别分拣系统 功能介绍 硬件组成 STM32F103C系列最小系统单片机 颜色识别模块 2路舵机 2个按键 LCD1602显示器 1 可以识别颜色
  • Python 字符串

    原始字符串 print r n t n t 续行符 name woshi abc print name name woshi abc print name woshiabc 三引号 可直接跨行书写 用于注释文档 字符串拼接 str1 str
  • lintcode 1692. 组队打怪

    你现在有n个英雄 每个英雄的战斗力为atk1 你要用这些英雄去对付n个怪物 每个怪物的战斗力为atk2 在一场战斗中 你需要安排每个英雄分别与一个怪兽战斗 如果英雄战斗力高于怪兽 那个怪兽就会被击杀 问最多能击杀几个怪兽 给定atk1 6
  • excel二进制移位运算_Excel揭秘13:在Excel中实现位运算

    我们知道 计算机使用的是二进制计数法 也就是说 在计算机中的所有信息都是使用二进制来存储和处理的 下表列出了我们熟悉的十进制数及与其相对应的二进制数 位运算规则 在位运算中 按位与 运算 AND运算 分别按位比较两个相应的数字 0或1 当且
  • centos7搭建svn服务器

    一 安装svn服务器 root svnserver yum y install subversion 查看svn 安装位置 可以用以下命令 root svnserver rpm ql subversion etc subversion et
  • Java中String类的使用

    目录 1 MS String 类中两种对象实例化的区别 1 1 直接赋值 1 2 构造方法 2 字符 字节与字符串 2 1 字符与字符串转换 2 2 字节与字符串转换 3 字符串常见操作 3 1 字符串比较 3 2 字符串查找 3 3 字符
  • HS BDC 【HDU - 3472】【混合半欧拉图构建欧拉图+最大流】

    题目链接 有N个字符 如果字符可以首尾相同字符相接组成一条链的话 那么就是说明是well done的 不然 就不是 所以考虑成一条边 我们把每个字符串考虑成有向边 又有些字符串是可以反转的 实际上可以把它当成是无向边来考虑 现在 就是要知道
  • idea 生成项目结构图

    Terminal中输入tree D mybatis plus generator demo gt tree 文件夹 PATH 列表 卷序列号为 ECE0 24D1 D idea inspectionProfiles libraries mv
  • Java如何将一个对象的所有字段值赋值给另一个对象?

    我们开发的时候可能需要进行对象值的复用 下面给大家介绍一个方法 就是使用BeanUtils public static void main String args throws Exception Student student new S
  • MySQL 经典练习 50 题(完美解答版)

    一 创建数据库和表 数据库 学生表 student 课程表 course 教师表 teacher 成绩表 score 表关系 创建数据库和表 创建数据库 drop database if exists mysql test cascade
  • 51单片机EEPROM(I²C总线通信)AT24C02数据存储

    一 存储器介绍 补充 1 易失性存储器 RAM 存储速度特别快但掉电丢失 SRAM 运行速度最快 用于电脑CPU 高速缓存 单片机中的SRAM 定义一个变量就会存在SRAM中 使用触发器做的 存储容量小 成本高 DRAM 运行速度仅次于SR
  • Linux的GPIO子系统解析 ( 一 ) 之 gpiolib.c

    文章目录 Linux的GPIO子系统解析 一 之 gpiolib c 绪论 关于GPIO子系统库文件的gpiolib c解析 drivers gpio gpiolib c gpio desc结构体 gpio chip结构体 gpio ens