gpio-export配置默认gpio

gpio-export配置默认gpio

用于设置gpio的默认状态和导出用户空间借口。只需要在设备树中配置节点即可。

参考链接:

https://linux-arm-kernel.infradead.narkive.com/QRDUydDE/patch-0-2-gpio-allow-userspace-export-from-dt#post9

https://devicetree.vger.kernel.narkive.com/hUDm3uhy/patch-gpio-add-export-with-name-from-dts

1. 驱动源码分析

导入头文件:
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/platform_device.h>

定义驱动的数据结构:

struct gpio_export_gpio{char *name;       // 节点名称struct gpio_desc *desc;    // gpio描述
};struct gpio_export{struct device *dev;  // 用于绑定相关的设备int gpios_num;      // 记录一共需要导出的节点数量,在卸载驱动时用于取消导出struct gpio_export_gpio *gpios;  // 导出的每一个gpio的信息
};

定义驱动的 of_match_table :

static struct of_device_id gpio_export_ids[] = {{ .compatible = "linux-gpio-export" },{ /* sentinel */}
};

当找到设备树节点中的 compatible 属性为 linux-gpio-export 时,执行 probe 函数。

定义 platform_driver 的一些信息和回调:

static struct platform_driver gpio_export_driver = {.driver = {.name = "linux-gpio-export",.owner = THIS_MODULE,.of_match_table = of_match_ptr(gpio_export_ids),},.probe = gpio_export_probe,.remove = gpio_export_remove,
};

优先匹配 of_match_table 中的 compatible 属性,匹配不上时,再使用 name 进行匹配。然后注册 proberemove 回调。

实现 probe 函数:

static int gpio_export_probe(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node; // 获取整个平台设备节点struct device_node *cnp; int nb = 0;int val;struct gpio_export *ge;struct device *dev = &pdev->dev; // 获取设备ge = devm_kzalloc(dev, sizeof(struct gpio_export), GFP_KERNEL);  // 为驱动结构分配内存空间if (IS_ERR(ge))return PTR_ERR(ge);ge->dev = dev; // 绑定设备dev_set_drvdata(dev, ge);  // 赞存驱动结构到设置中for_each_child_of_node(np, cnp) {  // 获取总共需要导出的gpio节点数量++nb;}ge->gpios_num = nb;nb = 0;ge->gpios = devm_kzalloc(dev, sizeof(struct gpio_export_gpio) * ge->gpios_num,GFP_KERNEL);  // 为每一个gpio都分配存放信息结构的空间if (IS_ERR(ge->gpios)) return PTR_ERR(ge->gpios);    for_each_child_of_node(np, cnp) { // 读取每一个节点的内部子节点,然后对每一个子节点依次进行导出操作const char *name = NULL;int gpio;bool dmc;of_property_read_string(cnp, "gpio-export,name", &name);if (!name) {name = of_node_full_name(np);}ge->gpios[nb].name = devm_kzalloc(dev, strlen(name) + 1, GFP_KERNEL);strncpy(ge->gpios[nb].name, name, strlen(name));gpio = of_get_gpio(cnp, 0);ge->gpios[nb].desc = gpio_to_desc(gpio);// 执行 gpiod_export 前,需要先进行 gpio_request 操作if (devm_gpio_request(&pdev->dev, gpio, ge->gpios[nb].name) {++nb;continue;}// 默认电平if (!of_property_read_u32(cnp, "gpio-export,output", &val))gpio_direction_output(gpio, val);elsegpio_direction_input(gpio);// 是否允许用户配置输入/输出方向dmc = of_property_read_bool(cnp, "gpio-export,direction-may-change");gpiod_export(ge->gpios[nb].desc, dmc);   // 导出gpio到 /sys/class/gpio/ 目录下gpiod_export_link(&pdev->dev, ge->gpios[nb].name, ge->gpios[nb].desc);  // 创建符号链接到 /sys/devices/gpio-export/ 下++nb;}dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);return 0;
}

注意,这里都是使用 devm 类的借口,使用这类接口,会把分配的内存的操作和 dev 对象绑定,在设备创建失败,或者移除卸载驱动时,自动释放分配的内存。但是,devm 接口尽量只在 probe 回调中使用,不能在 open 回调中使用。使用devm 分配内存后,在确认不需要使用内存的情况下,也可以通过 devm_kfree 借口对分配的内存进行主动释放操作。

详见:https://stackoverflow.com/questions/12256986/what-is-the-difference-between-devm-kzalloc-and-kzalloc-in-linux-driver-prog

实现 remove 函数:

static int gpio_export_remove(struct platform_device *pdev)
{   struct device *dev = &pdev->dev;struct gpio_export *ge = dev_get_drvdata(dev);  // 从设备结构中获取之前暂存的驱动数据int i;for (i = 0; i < ge->gpios_num; i++) {sysfs_remove_link(&ge->dev->kobj, ge->gpios[i].name);gpiod_unexport(ge->gpios[i].desc);   // 对已经的导出的所有gpio进行取消导出的操作}return 0;
}

由于使用devm类接口,因此在 remove 回调中,不需要去进行资源释放。在这里,只需要对已经导出的gpio,进行取消导出操作即可。

注册平台设备:

将驱动注册到平台设备上。

static int __init gpio_export_init(void)
{return platform_driver_register(&gpio_export_driver);
}static void __exit gpio_export_exit(void)
{platform_driver_unregister(&gpio_export_driver);
}module_init(gpio_export_init);
module_exit(gpio_export_exit);
MODULE_LICENSE("GPL");

加载驱动时,会将驱动注册到平台设备上。下载驱动时,会取消注册。

也可以使用下面的宏完成这个操作:

module_platform_driver(gpio_exporter_driver);
MODULE_LICENSE("GPL");

至此,一个完整的驱动的代码就完成了。

2. 设备树

设备树需要添加导出节点到根节点:

/ {model = "Qualcomm Technologies, Inc. MDM 9607";compatible = "qcom,mdm9607";..................soc: soc { };gpio-export {compatible = "linux-gpio-export";#size-cells = <0>;4G_LDO_1V8 {gpio-export,name = "4G_LDO_1V8";gpio-export,output = <1>;// gpio-export,direction-may-change;gpios = <&tlmm_pinmux 16 0>;};};
};

3. 编译

需要将编写的驱动加入到内核构建的系统中,才能在编译的时候将驱动编译进内核或者编译成内核模块。

添加Kconfig

在当前文件路径下,找到Kconfig文件,打开编辑,将下面的内容添加到if GPIOLIB 宏定义之间:

config GPIO_EXPORTtristate "Export GPIO to userspace by DT"depends on OF_GPIO && GPIO_SYSFShelpExport GPIO to /sys/class/gpio/ direction. And set the default state by DT.

gpio-export 依赖于 OF_GPIOGPIO_SYSFS ,以及 GPIOLIB

tristate :三态,支持 nmy

添加Makefile

在当前文件路径下,找到Makefile文件,打开编辑,将下面的内容添加到文件末尾:

obj-$(CONFIG_GPIO_EXPORT)   += gpio-export.o

选择编译 gpio-export 驱动

Kconfig 和 Makefile 已经对 gpio-export 驱动进行支持了,最后还需要配置编译 gpio-export 驱动,驱动才能真正被编译到内核或者编译成内核模块。

找到kernel构建使用的默认 defconfig 配置文件,将 CONFIG_GPIO_EXPORT=m 添加到文件中。

我这里是 arch/arm/configs/mdm9607_defconfig ,因此可以将其添加到这里面。添加后需要clean,重新进行编译。为了避免clean重编,再将其添加到 .config 中,我这里的 .config 文件在 build/.config 中,也将 CONFIG_GPIO_EXPORT=m 添加到文件末尾,直接make即可编译 gpio-export 驱动。

4. 加载驱动

modprobe gpio-export
lsmod 

加载驱动后,驱动就能通过设备树,导出我们设置GPIO了:

[   26.689436] of_get_named_gpiod_flags: parsed 'gpios' property of node '/gpio-export/4G_LDO_1V8[0]' - status (0)
[   26.689646] linux-gpio-export gpio-export: 1 gpio(s) exported

然后会在 /sys/class/gpio/ 下生成节点。

/sys/class/gpio # cd gpio16/
/sys/devices/1000000.pinctrl/gpio/gpio16 # ls
active_low  device      power       subsystem   uevent      value

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

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

相关文章

虚拟机配置centos7网络

一、编辑虚拟网络 二、编辑 ifcfg-ens32 配置静态ip vim /etc/sysconfig/network-scripts/ifcfg-ens32 三、网卡设置 四、重启网络 systemctl restart network

【外汇天眼】投资之道:成功背后的频繁交易陷阱

成功的投资需要超越人性的短板&#xff0c;其中之一就是频繁交易。巴菲特曾明言&#xff0c;如果商学院的毕业生在毕业后拿一张卡片&#xff0c;每买一支股票就打一个洞&#xff0c;那么这张卡片最终会被打得最少的人将成为巨富。“钱在这里从活跃的投资者流向有耐心的投资者。…

Android: ListView + ArrayAdapter 简单应用

​​容器与适配器&#xff1a;​​​​​ http://t.csdnimg.cn/ZfAJ7 activity_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"h…

linux内核管理

linux内核会占用一定的空间&#xff0c;所以可以清理一下不需要使用的内核. 参考链接 Linux 内核及其关联文件通常存储在 /boot 目录下&#xff0c;内核模块通常存储在 /lib/modules 目录中。 首先查看已安装的列表&#xff1a; dpkg --list | grep linux-image其中&#xff…

2023年中国聚氨酯树脂涂料需求量、市场规模及行业趋势分析[图]

聚氨酯是一种新兴的有机高分子材料&#xff0c;被誉为“第五大塑料”&#xff0c;因其卓越的性能而被广泛应用于国民经济众多领域。产品应用领域涉及轻工、化工、电子、纺织、医疗、建筑、建材、汽车、国防、航天、航空等。2022年中国聚氨酯产量已达1600万吨。 2012-2022年中国…

为什么几乎所有的量化交易都用Python?

因为python好用啊&#xff01;Python&#xff0c;作为一种功能强大且易于学习的编程语言&#xff0c;已经成为金融分析师的首选工具。 以下我将从3个方面给大家分析python为何成为量化交易的理想选择。 一、语言特性与金融分析的匹配性 1、简单易懂的语言 Python就像我们的日…

传统Office太占存储空间?快来试试这款LiberOffice

用过Microsoft Office的同学&#xff0c;在完成安装后会发现MS Office系列的软件所占内存还是不小的&#xff0c;尤其当你组件下载很多以后&#xff0c;存储量真不是一个小数目。 那么&#xff0c;有没有一款轻量级的办公软件呢&#xff1f;别着急&#xff0c;今天学长就带来这…

【vue2】前端如何播放rtsp 视频流,拿到rtsp视频流地址如何处理,海康视频rtsp h264 如何播放

文章目录 测试以vue2 为例新建 webrtcstreamer.js下载webrtc-streamervideo.vue页面中调用 最近在写vue2 项目其中有个需求是实时播放摄像头的视频&#xff0c;摄像头是 海康的设备&#xff0c;搞了很长时间终于监控视频出来了&#xff0c;记录一下&#xff0c;放置下次遇到。…

传统考勤太复杂怎么办?这个小技巧,我必须吹爆!

随着科技的不断进步&#xff0c;人脸识别技术在各个领域得到了广泛的应用。在企业管理和安全领域&#xff0c;三维人脸考勤系统成为了一种高效、准确的管理工具。 客户案例 银行 天津某银行是一家金融机构&#xff0c;对于安全性要求极高。传统的考勤系统无法满足他们对于员工…

08_面向对象高级_枚举

枚举 1. 认识枚举 枚举是一种特殊的类。 public class Test {public static void main(String[] args) {A objX A.X;System.out.println(objX); // X} }enum A {// 注意&#xff0c;枚举类的第一行必须罗列枚举对象的名称X,Y,Z;private String name;public String getName…

在 Linux 和 Windows 系统下查看 CUDA 和 cuDNN 版本的方法,包括使用 nvcc 命令

一直都比较头疼cuda与cudnn版本查看问题&#xff0c;两个系统不一样也不好查看&#xff0c;命令不通用 Linux 查看 CUDA 版本 方法一&#xff1a; nvcc --version或 nvcc -V如果 nvcc 没有安装&#xff0c;那么用方法二。 方法二&#xff1a; 去安装目录下查看&#xff…

ip地址跟wifi有关系吗

​你可能已经听说过IP地址和Wi-Fi这两个词&#xff0c;但你有没有想过它们之间是否有关系呢&#xff1f;在这篇文章中&#xff0c;我们将深入探讨IP地址与Wi-Fi之间的密切联系。从基本概念到应用实例&#xff0c;虎观代理小二二将为您解答这个问题。 首先&#xff0c;让我们来了…

Threejs_08 纹理颜色的调整(颜色空间的设置)

为什么写入的贴图颜色跟实际的颜色有差别呢&#xff1f; 具体为啥我也不知道&#xff0c;总之就是threejs有两个颜色空间 一个是线性的 一个是rgb那种样式的&#xff0c;但是人眼对光照强度的感知并不是线性的&#xff0c;所以threejs的默认属性&#xff0c;到人眼中&#xff…

存储区域网络(SAN)之FC-SAN和IP-SAN的比较

存储区域网络(Storage Area Network&#xff0c;SAN)用于将多个系统连接到存储设备和子系统。 早期FC-SAN&#xff1a; 采用光纤通道(Fibre Channel&#xff0c;FC)技术&#xff0c;通过光纤通道交换机连接存储阵列和服务器主机&#xff0c;建立专用于数据存储的区域网络。 传…

知识表示与知识图谱

目录 前言 一、知识与知识表示的概念 二、知识图谱 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x1f4e3;如…

NSS [鹤城杯 2021]Middle magic

NSS [鹤城杯 2021]Middle magic 源码直接给了。 粗略一看&#xff0c;一共三个关卡 先看第一关&#xff1a; if(isset($_GET[aaa]) && strlen($_GET[aaa]) < 20){$aaa preg_replace(/^(.*)level(.*)$/, ${1}<!-- filtered -->${2}, $_GET[aaa]);if(preg_m…

「Verilog学习笔记」输入序列连续的序列检测

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 timescale 1ns/1ns module sequence_detect(input clk,input rst_n,input a,output reg match);reg [7:0] a_tem ; always (posedge clk or negedge rst_n) begin if (~rs…

使用正则表达式匹配HTML标签出现了问题

今天有这样一个需求&#xff1a;需要匹配好多个HTML文件&#xff0c;从中找出所有的标题文字。 正则表达式 这本是一个简单的需求&#xff0c;只需要使用正则表达式进行匹配即可。下列是我们当时所使用的表达式&#xff1a; <[hH][1-6]>.*?<\/[hH][1-6]> 测试…

openfeign整合sentinel出现异常

版本兼容的解决办法&#xff1a;在为userClient注入feign的接口类型时&#xff0c;添加Lazy注解。 Lazy注解是Spring Framework中的一个注解&#xff0c;它通常用于标记Bean的延迟初始化。当一个Bean被标记为Lazy时&#xff0c;Spring容器在启动时不会立即初始化这个Bean&…