linux驱动_gpio(sysfs_gpiolib)实现

news/2024/5/2 15:28:22/文章来源:https://blog.csdn.net/gavinpeng/article/details/127004629

本文原理部分主要参考以下文章(包括图片):
https://blog.csdn.net/zhoutaopower/article/details/98082006

GPIO 的使用

linux 系统中可以采用自己编写GPIO驱动方式,通过实现GPIO的字符设备驱动程序,用户层可以通过ioctl进行控制。还有一种方式是通过linux系统通用的GPIO框架实现,目前项目采用这种方式。这种方式应用层控制GPIO的方法在<<linux驱动_leds-pgio>>这篇文章有提到过,这里把操作简单列一下:

echo 6 > /sys/class/gpio/export             # pin脚6 GPIO使能
echo 6 > /sys/class/gpio/unexport           # pin脚6 GPIO去使能
echo out > /sys/class/gpio/gpio6/direction  # 输出使能
echo in > /sys/class/gpio/gpio6/direction   # 输入使能
echo 1 > /sys/class/gpio/gpio6/value        # 输出高电平
echo 0 > /sys/class/gpio/gpio6/value        # 输出低电平
cat /sys/class/gpio/gpio6/value             # 读pin脚电平

注意:内核要支持通过sysfs方式控制GPIO(/sys/class/gpio),记得要在编译内核时(make menuconfig)勾选:Device Drivers -> GPIO Support -> /sys/class/gpio/…(sysfs interface)。

gpiolib 基础

Linux Kernel 中对 GPIO 资源进行了抽象,抽象出 Gpiolib,作为 GPIO 资源的管理核心存在,框架如下图(图片来源于网络)。Gpiolib 汇总了 GPIO 的通用操作,根据 GPIO 的特性,Gpiolib 对上(其他 Drivers)提供的一套统一通用的操作 GPIO 的软件接口,屏蔽了不同芯片的具体实现。对下,Gpiolib 提供了针对不同芯片操作的一套 framework,针对不同芯片,只需要实现 Specific Chip Driver ,然后使用 Gpiolib 提供的注册函数,将其挂接到 Gpiolib 上,这样就完成了。
gpiolib架构

gpiolib 相关数据结构

  1. struct gpio_chip (include/linux/gpio/driver.h),此结构是为了抽象 GPIO 的所有操作,同时适配不同芯片的一个 common 的结构。
  2. struct gpio_desc 结构,包含了一个 gpio_device 的结构和 flag,以及 lable 和 name,gdev 指针指向了这个 gpio_desc 所属的 gpio_device(马上描述),flag 代表了这个 GPIO 的属性状态, gpio_chip 和 gpio_desc 应该是包含关系,但是 Kernel 中并没有直接将其两个结构联系上,而是通过另外一个结构将其联系在一起,这个结构就是 gpio_device。
  3. struct gpio_device结构,gpio_device 结构包含了 gpio_chip(对接芯片的操作集),gpio_desc(一些 GPIO 的描述);这个结构贯穿了整个 Gpiolib,Linux Kernel 把多个 gpio_device 串成一个名为 gpio_devices 的链表。
struct gpio_device {int			id;struct device		dev;struct cdev		chrdev;struct device		*mockdev;struct module		*owner;struct gpio_chip	*chip;struct gpio_desc	*descs;int			base;u16			ngpio;const char		*label;void			*data;struct list_head        list;// ...
};// 链接
LIST_HEAD(gpio_devices);

Gpiolib 为其他驱动提供的 APIs

1、gpio_request:向内核申请 gpio, 要使用 GPIO 首先应该向内核进行申请,返回 0,代表申请成功,可以进行后续操作
2、gpio_free: 对应 gpio_request,是使用完gpio以后把gpio释放掉
3、gpio_direction_input :设置 GPIO 为输入
4、gpio_direction_output:设置 GPIO 为输出
5、gpio_get_value :读取 GPIO 的值
6、gpio_set_value:设置 GPIO 口的值

Gpiolib 对接芯片底层

底层需要对接那些通用的操作,也就是 gpio_chip ,所以,对接底层的部分,主要关心的是这个结构体,并且对这个结构体进行赋值的过程。在底层对接到 Gpiolib 的时候,主要是对 gpio_chip 进行实现,然后调用 gpiochip_add 的接口,向 Gpiolib 注册你的 GPIO 。实现的过程,主要是根据芯片手册,实现对应的 GPIO 的操作,也就是说,把寄存器操作编程成为函数,对接到这个 gpio_chip 结构体上。

SSD202D gpio驱动实现

本项目GPIO驱动的入口是mdrv_gpio_io.c文件,初始化时向平台注册了一个驱动,这个文件的简略代码如下:

// 跟当前平台相关的头文件,其它通用的头文件被省略了
#include "mdrv_gpio_io.h"
#include "mhal_gpio.h"
#include "mdrv_gpio.h"
#include "ms_platform.h"
#include "gpio.h"// 其中一个接口,后面其它接口的代码大同小异,被省略了
int camdriver_gpio_request(struct gpio_chip *chip, unsigned offset)
{// 会调用 MDrv_GPIO_Pad_Set -> MHal_GPIO_Pad_SetMDrv_GPIO_Pad_Set(offset); return 0;
}static void camdriver_gpio_free(struct gpio_chip *chip, unsigned offset)
{
// ...
}void camdriver_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{if(value==0)MDrv_GPIO_Pull_Low(offset);elseMDrv_GPIO_Pull_High(offset);
}int camdriver_gpio_get(struct gpio_chip *chip, unsigned offset)
{
// ...
}int camdriver_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
// ...
}int camdriver_gpio_direction_output(struct gpio_chip *chip, unsigned offset,int value)
{
// ...
}int camdriver_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
// ...
}// 全局gpio_chip 对象,把操作硬件的实现挂接上
static struct gpio_chip camdriver_gpio_chip = {.label          = "gpio",.request        = camdriver_gpio_request,.free           = camdriver_gpio_free,.direction_input    = camdriver_gpio_direction_input,.get            = camdriver_gpio_get,.direction_output   = camdriver_gpio_direction_output,.set            = camdriver_gpio_set,.to_irq         = camdriver_gpio_to_irq,.base           = 0,
};static const struct of_device_id camdriver_gpio_of_match[] = {{ .compatible = "sstar,gpio" },{ },
};// 设备探测函数
static int camdriver_gpio_probe(struct platform_device *pdev)
{const struct of_device_id *match;int ret;dev = &pdev->dev;// 驱动与设备匹配名字match = of_match_device(camdriver_gpio_of_match, &pdev->dev);if (!match) {printk("Err:[gpio] No dev found\n");return -ENODEV;}// gpio 个数,/kernel/drivers/sstar/include/infinity2m/gpio.h 文件定义camdriver_gpio_chip.ngpio = GPIO_NR; camdriver_gpio_chip.of_node = pdev->dev.of_node;ret = gpiochip_add(&camdriver_gpio_chip); // 注册到gpiolibif (ret < 0) {printk("[gpio] add err\n");return ret;}MDrv_GPIO_Init(); // 初始化硬件return 0;
}static struct platform_driver camdriver_gpio_driver = {.driver     = {.name   = "gpio",.owner  = THIS_MODULE,.of_match_table = camdriver_gpio_of_match,},.probe      = camdriver_gpio_probe,
};static int __init camdriver_gpio_init(void)
{// 注册平台驱动return platform_driver_register(&camdriver_gpio_driver);
}
postcore_initcall(camdriver_gpio_init); // 模块初始化

如上面代码,模块初始化时注册了一个平台驱动,名字是"gpio"。设备是在设备树里面定义的,文件在/kernel/arch/arm/boot/dts 目录下,内核编译后会在该目录下生成一个.dtb文件,查看同文件名的dts文件即可。我这里要看的文件是:
infinity2m-spinand-ssc011a-s01a-rgb565-rmii-doublenet.dts
同时它包含了几个文件,也要一起看,如下:

#include "infinity2m-doublenet.dtsi"
#include "infinity2m-ssc011a-s01a-rgb565-rmii-doublenet.dtsi"
#include "infinity2m-ssc011a-s01a-padmux-rgb565-rmii-doublenet.dtsi"

查到的gpio设备定义如下:

gpio:gpio{compatible = "sstar,gpio";#gpio-cells = <2>;gpio-controller;
};

从上面可见,基本只是定义了一个gpio设备,没有指明硬件资源,即硬件资源直接在驱动里面定义了,设备树文件里面定义gpio设备只是为了让内核启动时probe gpio驱动。那么gpio硬件的资源在哪里定义的呢?
从上面驱动代码调用的地方查找:

MDrv_GPIO_Init();int camdriver_gpio_request(struct gpio_chip *chip, unsigned offset)
{// 会调用 MDrv_GPIO_Pad_Set -> MHal_GPIO_Pad_SetMDrv_GPIO_Pad_Set(offset); return 0;
}

上面mdrv_gpio.c文件的camdriver_gpio_request函数会调用mdrv_gpio.c的MDrv_GPIO_Pad_Set函数,然后调用到mhal_gpio.c的MHal_GPIO_Pad_Set函数,然后调用到mhal_pinmux.c文件的 HalPadSetVal 函数这里面会用到padmux_tables.c 文件里面定义的硬件资源。
再看看上面代码中 camdriver_gpio_set 函数,调用链路是:
camdriver_gpio_set(mdrv_gpio_io.c) -> MDrv_GPIO_Pull_High(mdrv_gpio.c) -> MHal_GPIO_Pull_High(mhal_gpio.c) -> MHal_RIU_REG(mhal_gpio.c)
而 MHal_RIU_REG 是个宏定义

U32 gChipBaseAddr    = 0xFD203C00;
U32 gPmSleepBaseAddr = 0xFD001C00;
U32 gSarBaseAddr     = 0xFD002800;
U32 gRIUBaseAddr     = 0xFD000000;#define MHal_CHIPTOP_REG(addr)  (*(volatile U8*)(gChipBaseAddr + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)(gPmSleepBaseAddr + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)(gSarBaseAddr + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr)      (*(volatile U8*)(gRIUBaseAddr + (((addr) & ~1)<<1) + (addr & 1)))void MHal_GPIO_Pull_High(U8 u8IndexGPIO)
{MHal_RIU_REG(gpio_table[u8IndexGPIO].r_out) |= gpio_table[u8IndexGPIO].m_out;
}

上面 gpio_table 是在 gpio_table.c 文件定义,gpio_table.h 头文件代码:

#include "mhal_gpio.h"struct gpio_setting {U32 r_oen;U8  m_oen;U32 r_out;U8  m_out;U32 r_in;U8  m_in;
};extern const struct gpio_setting gpio_table[];

gpio_table.c 文件 部分代码:

#include "gpio_table.h"#define _CONCAT( a, b )     a##b
#define CONCAT( a, b )      _CONCAT( a, b )#define GPIO0_PAD        PAD_GPIO0
#define GPIO0_OEN        0x103C00, BIT5
#define GPIO0_OUT        0x103C00, BIT4
#define GPIO0_IN         0x103C00, BIT0// ...#define GPIO89_PAD        PAD_HDMITX_HPD
#define GPIO89_OEN        0x103C88, BIT5
#define GPIO89_OUT        0x103C88, BIT4
#define GPIO89_IN         0x103C88, BIT0#define GPIO90_PAD        PAD_SATA_GPIO
#define GPIO90_OEN        0x103C8A, BIT5
#define GPIO90_OUT        0x103C8A, BIT4
#define GPIO90_IN         0x103C8A, BIT0const struct gpio_setting gpio_table[] =
{
#define __GPIO__(_x_)   { CONCAT(CONCAT(GPIO, _x_), _OEN), CONCAT(CONCAT(GPIO, _x_), _OUT), CONCAT(CONCAT(GPIO, _x_), _IN) }
#define __GPIO(_x_)     __GPIO__(_x_)//
// !! WARNING !! DO NOT MODIFIY !!!!
//
// These defines order must match following
// 1. the PAD name in GPIO excel
// 2. the perl script to generate the package header file
////__GPIO(999), // 0 is not used__GPIO(0), __GPIO(1), __GPIO(2), __GPIO(3), __GPIO(4), __GPIO(5), __GPIO(6), __GPIO(7),__GPIO(8), __GPIO(9), __GPIO(10), __GPIO(11), __GPIO(12), __GPIO(13), __GPIO(14), __GPIO(15),__GPIO(16), __GPIO(17), __GPIO(18), __GPIO(19), __GPIO(20), __GPIO(21), __GPIO(22), __GPIO(23),__GPIO(24), __GPIO(25), __GPIO(26), __GPIO(27), __GPIO(28), __GPIO(29), __GPIO(30), __GPIO(31),__GPIO(32), __GPIO(33), __GPIO(34), __GPIO(35), __GPIO(36), __GPIO(37), __GPIO(38), __GPIO(39),__GPIO(40), __GPIO(41), __GPIO(42), __GPIO(43), __GPIO(44), __GPIO(45), __GPIO(46), __GPIO(47),__GPIO(48), __GPIO(49), __GPIO(50), __GPIO(51), __GPIO(52), __GPIO(53), __GPIO(54), __GPIO(55),__GPIO(56), __GPIO(57), __GPIO(58), __GPIO(59), __GPIO(60), __GPIO(61), __GPIO(62), __GPIO(63),__GPIO(64), __GPIO(65), __GPIO(66), __GPIO(67), __GPIO(68), __GPIO(69), __GPIO(70), __GPIO(71),__GPIO(72), __GPIO(73), __GPIO(74), __GPIO(75), __GPIO(76), __GPIO(77), __GPIO(78), __GPIO(79),__GPIO(80), __GPIO(81), __GPIO(82), __GPIO(83), __GPIO(84), __GPIO(85), __GPIO(86), __GPIO(87),__GPIO(88), __GPIO(89), __GPIO(90),
};

从上面代码可知,mhal_gpio.c 文件定义了gpio 基址, gpio_table.c 文件把/kernel/drivers/sstar/include/infinity2m/gpio.h 定义的所有gpio pin脚的硬件资源(偏移)定义在 gpio_table 数组,gpio 硬件实现基本清楚。下面把 gpio.h 部分代码贴上,这些pin脚的ID与硬件data sheet对应,ID号也与用户空间使用的ID号一致。

#ifndef __GPIO_H__
#define __GPIO_H__#define PAD_GPIO0                       0
#define PAD_GPIO1                       1// ...#define PAD_HSYNC_OUT                   85
#define PAD_VSYNC_OUT                   86
#define PAD_HDMITX_SCL                  87
#define PAD_HDMITX_SDA                  88
#define PAD_HDMITX_HPD                  89
#define PAD_SATA_GPIO                   90#define GPIO_NR                         91
#define PAD_UNKNOWN     0xFFFF#endif // __GPIO_H__

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_12928.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gitlab服务CI/CD配置

使用Gitlab作为工作仓库进行代码发布及版本控制&#xff0c;Gitlab内置了CI/CD的工具&#xff0c;这些工具可以用于代码提交的同时完成镜像构建、自动化测试、自动化部署等连续的工作&#xff1a; CI: Continuous Integration&#xff08;持续集成&#xff09;CD: Continuous …

C语言之模块化、预处理(条件编译)

模块化、预处理1. C 模块化1.1. 头文件 .h1.1.1. 用户头文件1.1.2. extern 关键字1.2. c文件2. 预处理指令(以 # 开头)2.1. 宏定义2.2. 条件编译2.3. #ifdef、#ifndef、#endif2.4. defined() 运算符1. C 模块化 模块化编程的核心思想: 将系统的各个功能进行封装&#xff0c;变成…

CDH6.3.2安装python3

背景&#xff1a;需要使用pyspark或者python去自动读取远程文件&#xff0c;但是CDH集群里面自带着python2.7.5&#xff0c;python3.0是以后的趋势&#xff0c;所以决定自己安装python3。以下的安装步骤是参照网上的步骤&#xff0c;实操是自己亲自操作的。 1.1 系统版本信息 …

Vim编辑器的使用

一、什么是Vim编辑器 Vim是从vi发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。 简单的来说&#xff0c;vi 是老式的字处理器,不过功能已经很齐全了,但是还是有可以进步的地方。vim则可以说是程序开发者的一项很好用的工具…

.NET MAUI学习笔记——1.初识.NET MAUI_初级篇

文章目录一、 引言二、什么是.NET MAUI&#xff1f;1. .NET MAUI为谁服务&#xff1f;2. .NET MAUI是怎样工作的&#xff1f;3. .NET MAUI提供了什么&#xff1f;3.1. 设备特性的跨平台API3.2. 单一项目3.3. 热重载三、结尾一、 引言 打算开发一款简单的Android的App&#xff…

springboot-鑫源停车场管理系统 毕业设计 -附源码 290915

springboot鑫源停车场管理系统 摘 要 21世纪时信息化的时代&#xff0c;几乎任何一个行业都离不开计算机&#xff0c;将计算机运用于停车场管理也是十分常见的。过去使用手工的管理方式对停车场进行管理&#xff0c;造成了管理繁琐、难以维护等问题&#xff0c;如今使用计算机对…

03-JVM-对象内存、执行引擎

一、创建对象的方式 new、Xxx的静态方法、XXXBuilder/XXXFactory的静态方法Class的newInstance&#xff0c;只能调用空参构造器&#xff0c;权限必须是publicConstruct的newInstance&#xff0c;可以调用空参、带参的构造器&#xff0c;权限无要求Clone()&#xff0c;不调用构…

瑞吉外卖(16) - 新增菜品功能开发

文章目录需求分析数据模型dish表dis flaow表代码开发搭建框架新增dishFlavor实体类新增Mapper接口DishFlavorMapper新增业务接口DishFlavorService新增业务层实现类DishFlavorServiceImpl新增控制层DishController梳理交互过程编辑CategoryControoler下拉框功能测试导入DTO编写…

香橙派Orange Pi i96 初次使用遇到的坑和解决方法+附加c# iot .net 代码实例

香橙派Orange Pi i96这个主板是香橙派最便宜的一样主板&#xff0c;功能也不少&#xff0c;我觉得可以满足我的需求&#xff0c;如果可以用好的话&#xff0c;可以做很多东西&#xff0c;批量买也便宜&#xff0c;做出来的产品&#xff0c;定价也不用很高&#xff0c;符合效益 …

4计算机网络与信息安全——软件设计师

一、七层模型 内容&#xff1a; 习题&#xff1a; 局域网不能通过三层通信 二、网络技术标准与协议 一般分为4/5层(没有明确说明&#xff0c;了解主要几层即可) TCP协议: DHCP协议 DNS协议 递归直接回答&#xff0c;迭代刨根到底 三、网络类型与拓扑类型 计算机网络按不同…

一起Talk Android吧(第三百八十五回:数据绑定-DataBinding总结)

文章目录数据绑定使用方法7.单向数据绑8.双向数据绑定9.绑定事件演示结果使用总结各位看官们&#xff0c;大家好&#xff0c;上一回中咱们说的是数据绑定-DataBinding的例子,这一回中咱们继承介绍该例子。闲话休提&#xff0c;言归正转&#xff0c;让我们一起Talk Android吧&am…

线程池概念2

线程池类图 ExecutorService:是一个线程池最基本的接口&#xff0c;提供了提交任务&#xff0c;关闭线程池这些基本的方法。 ScheduledExecutorService:扩展接口&#xff0c;在基础线程池的功能上又新增了任务调度的功能&#xff0c;可以用来定时执行任务。 线程池状态 线程…

Devtools Console 面板输入了 1+1 ,浏览器内部发生了什么?

背景 新来的实习生妹子问了一个问题&#xff1a;「你看 Chrome 的 Devtools 调试工具&#xff0c;代码写一半还没运行下面就会提示输出结果&#xff0c;这个咋做到的&#xff1f;」 咋做的&#xff1f;对于 Devtools 的内部执行逻辑&#xff0c;咱不了解&#xff0c;但咱也不能…

WhatsApp和WhatsApp Business之间的区别

关键词&#xff1a;WhatsApp、WhatsApp Business WhatsApp 无疑是一款超级方便、免费且毫不费力的即时通讯应用程序。这就是庞大的用户群的原因。对于企业来说&#xff0c;它也是一个超级方便、免费且不费吹灰之力的即时通讯应用程序。特别是自从 WhatsApp 推出了 WhatsApp Bus…

《MySQL DBA封神打怪之路》专栏学习大纲

《MySQL DBA封神打怪之路》专栏学习大纲 文章目录《MySQL DBA封神打怪之路》专栏学习大纲1、作者介绍2、专栏介绍3、专栏部分文章截图3.1.所有文章一栏3.2.文章内容截图4、专栏大纲学习指南4.1对数据库的初步认识4.2.四种类型的SQL语句基本使用4.3.超丰富的多表联查案例4.4.事物…

消除笔去水印怎么做?教你怎么用这些消除笔软件

在网上看到好看的照片想要用作壁纸&#xff0c;但是上面有水印看起来不好看怎么办&#xff1f;我们可以用有消除笔的软件将图片水印去除&#xff0c;那么消除笔去水印怎么做呢&#xff1f;今天的这个教程分享给你们。方法一&#xff1a;借助“Styler”进行去水印操作 这是一款拥…

Echarts绘制geo地图属性设置大全(一)

1、Echarts版本 "echarts": "^5.3.3", 2、基础地图绘制&#xff08;以中国地图为例&#xff09; 绘制地图需要有用于绘制地理坐标系的数据&#xff0c;如示例使用的china.js <template><div ref"mapBar" class"map-class"&…

面试之HashMap

1.初始大小&#xff1a;HashMap默认初始大小是16&#xff0c;这个默认值是可以设置的&#xff0c;如果事先知道大概的数据量有多大&#xff0c;可以通过修改默认初始大小&#xff0c;减少动态扩容的次数&#xff0c;这样会大大提高HashMap的性能 2.动态扩容&#xff1a;最大 装…

兼容性测试包含哪几类呢?

兼容性测试包含哪几类呢? (1)浏览器方面 关于浏览器的兼容性测试&#xff0c;主要是检查页面的交互、元素和样式展示是否正常。我们都知道&#xff0c;目前市面上主流的浏览器非常多&#xff0c;像&#xff1a;360、搜狗、火狐等等。 在进行测试的时候&#xff0c;由于兼容性问…

2022出海东南亚:越南电商市场现状及网红营销特点

近几年&#xff0c;东南亚整体发展态势非常好&#xff0c;加上国内市场饱和&#xff0c;不少国内企业在计划出海或已经出海东南亚。作为东南亚第二大电商市场&#xff0c;越南自然也成了香饽饽&#xff0c;越南蓬勃发展的数字经济和电商领域&#xff0c;让其成为了卖家挖掘新商…