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 的相关文章

  • 当存在点和下划线时,使用 sed 搜索并替换

    我该如何更换foo with foo sed 只需运行 sed s foo foo g file php 不起作用 逃离 sed s foo foo g file php Example cat test txt foo bar sed s
  • 如何查找或计算Linux进程的页表大小和其他内核占用?

    我怎样才能知道 Linux 进程页表有多大 以及任何其他可变大小的进程统计 如果您真的对页表感兴趣 请执行以下操作 cat proc meminfo grep PageTables PageTables 24496 kB
  • 如何使用sprof?

    请举例说明 从邮件中找到here http sources redhat com ml libc alpha 2003 07 msg00029 html and here http sourceware org ml binutils 20
  • 如何使用 tmuxinator 在 tmux 中拆分水平窗格内的两个垂直窗格

    目前我的 tmuxinator yml 文件中有这个 windows editor layout main horizontal panes vim server rails s 这给了我两个窗口 一个用于编辑器 另一个用于服务器 在编辑器
  • 在单个命令中使用前缀重命名文件夹中的所有文件

    重命名带有前缀的文件夹中的所有文件 Unix 假设一个文件夹有两个文件 a txt b pdf 那么它们都应该从一个命令重命名为 Unix a txt Unix b pdf 如果您的文件名包含没有空格并且你没有任何子目录 你可以使用一个简单
  • 在键盘热插拔上加载模块

    我正在尝试学习如何为 Linux 系统编写模块和驱动程序 类似于this https unix stackexchange com questions 120839 usb kernel module does not load on de
  • 跟踪 pthread 调度

    我想做的是创建某种图表 详细说明 Linux 中 两个 线程的执行情况 我不需要查看线程的作用 只需查看它们何时被安排以及持续多长时间 基本上是一条时间线 在过去的几个小时里 我一直在互联网上搜索跟踪 pthread 调度的方法 不幸的是
  • 我在哪里可以学习如何使 C++ 程序与操作系统 (Linux) 交互

    我是一个 C 初学者 我想创建与操作系统交互的小程序 使用 Kubuntu Linux 到目前为止 我还没有找到任何教程或手册来让 C 与操作系统交互 在 PHP 中 我可以使用命令 exec 或反引号运算符来启动通常在控制台中执行的命令
  • 如何使用libaudit?

    我试图了解如何使用 libaudit 我想接收有关使用 C C 的用户操作的事件 我不明白如何设置规则 以及如何获取有关用户操作的信息 例如 我想获取用户创建目录时的信息 int audit fd audit open struct aud
  • 测试linux下磁盘空间不足

    我有一个程序 当写入某个文件的磁盘空间不足时 该程序可能会死掉 我不确定是否是这种情况 我想运行它并查看 但我的测试服务器不会很快耗尽空间 有什么办法可以嘲笑这种行为吗 看起来没有任何方法可以在 Ubuntu 中设置文件夹 文件大小限制 并
  • 使用netcat将unix套接字传输到tcp套接字

    我正在尝试使用以下命令将 unix 套接字公开为 tcp 套接字 nc lkv 44444 nc Uv var run docker sock 当我尝试访问时localhost 44444 containers json从浏览器中 它不会加
  • 原生 Linux 应用程序可像 ResHacker 一样编辑 Win32 PE

    我想运行自动修改 dll服务 用户提交特定的 dll 我在服务器上修改它 然后用户可以下载 dll的修改版本 是否有任何本机 Linux 应用程序提供常见的 Win32 PE 修改功能 例如图标 字符串 加速器 对话等 至少提供命令行或脚本
  • 退出 bash 脚本但保持进程运行

    我正在运行服务器 需要使用参数执行以下命令 这些脚本目前工作得很好 但问题是当我运行脚本时我无法返回到控制台 它在控制台中保持运行 如果我强行停止它 那么该过程也会停止 我想继续运行该进程并返回到控制台 bin sh php home st
  • grails 上的同步块在 Windows 上有效,但在 Linux 上无效

    我有一个 grails 应用程序 它依赖于服务中的同步块 当我在 Windows 上运行它时 同步按预期工作 但当我在 ams linux 上运行时 会出现 StaleObjectStateException 该问题在以下示例中重现 cla
  • 使用脚本自动输入 SSH 密码

    我需要创建一个自动向 OpenSSH 输入密码的脚本ssh client 假设我需要通过 SSH 进入myname somehost用密码a1234b 我已经尝试过 bin myssh sh ssh myname somehost a123
  • 使用命令行将 MediaWiki 维基文本格式转换为 HTML

    我倾向于编写大量文档 因此 MediaWiki 格式对我来说很容易理解 而且比编写传统 HTML 节省了我很多时间 然而 我也写了一篇博客 发现一直从键盘切换到鼠标来输入正确的 HTML 标签会增加很多时间 我希望能够使用 Mediawik
  • Linux 中的电源管理通知

    在基于 Linux 的系统中 我们可以使用哪些方法 最简单的方法 来获取电源状态更改的通知 例如 当计算机进入睡眠 休眠状态等时 我需要这个主要是为了在睡眠前保留某些状态 当然 在计算机唤醒后恢复该状态 您只需配置即可获得所有这些事件acp
  • R 未获取用户库

    我有一个带 R 3 6 0 的 Fedora 30 系统 用户库设置在Renviron就像这个 R LIBS USER R LIBS USER R x86 64 redhat linux gnu library 3 6 事实上 它出现在交互
  • 如何从 Linux 的 shell 中删除所有以 ._ 开头的文件?

    确实如标题所示 我已将许多文件从 Mac 复制到 Raspberry Pi 这导致了许多以前缀开头的多余文件 我想删除以以下开头的文件夹中的每个文件 我该怎么做 尝试类似的方法 cd path to directory rm rf 或者 如
  • 在 shell 脚本中查找和替换

    是否可以使用 shell 在文件中搜索然后替换值 当我安装服务时 我希望能够在配置文件中搜索变量 然后在该值中替换 插入我自己的设置 当然 您可以使用 sed 或 awk 来完成此操作 sed 示例 sed i s Andrew James

随机推荐

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

    目标检测 三 损失函数 开始 一 匹配策略 二 损失函数 三 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