荔枝派zero驱动开发06:GPIO操作(platform框架)

参考:
正点原子Linux第五十四章 platform设备驱动实验
一张图掌握 Linux platform 平台设备驱动框架

上一篇:荔枝派zero驱动开发05:GPIO操作(使用GPIO子系统)
下一篇:更新中…

概述

platform是一种分层思想,所谓的 platform 驱动并不是独立于字符设备驱动、块设备驱动和网络设备驱动之外的其他种类的驱动;platform 只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。

  • 初学对具体设计的意图无需完全掌握,大致掌握运行流,在内核驱动中可以找到和读懂相关代码即可,用户编写驱动可以参考模板

  • 参考下图,一图流,完全弄懂这张图就能完全掌握platform框架的思想 😃

在这里插入图片描述
图源:一张图掌握 Linux platform 平台设备驱动框架!

设备树修改

设备树直接使用上一章即可,无需修改

简要分析

在上一章源码基础上修改,添加platform相关的数据结构和匹配表,实现led_probe(即原驱动初始化函数),实现led_remove(原退出函数),并将驱动初始化改为platform_driver_register和platform_driver_unregister

//platform相关
// 匹配列表
static const struct of_device_id led_of_match[] = {{ .compatible = "user,led" },{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, led_of_match);/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver		= {.name	= "platform-led",			// 驱动名字,将在/sys/bus/platform/drivers/下生成 .of_match_table	= led_of_match,     // 设备树匹配表},.probe		= led_probe,.remove		= led_remove,
};static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}

如上,设备树与led_of_match的属性值匹配后,即执行led_probe函数,同理:卸载驱动时会执行led_remove函数,卸载操作无需改动

函数原型:int (*probe)(struct platform_device *);

led_probe函数参考实现:

static int led_probe(struct platform_device *pdev)
{int ret;const char *str;printk("led driver and device was matched!\r\n");// 获取 LED 灯的 GPIO 号// 进probe说明设备树已匹配,直接使用设备树节点即可platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (platform_led.led_gpio < 0){printk("can't get gpios");return -EINVAL;}printk("gpio num = %d\r\n", platform_led.led_gpio);// 向 gpio 子系统申请使用 GPIOret = gpio_request(platform_led.led_gpio, "green");	// 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯ret = gpio_direction_output(platform_led.led_gpio, 0);...// 注册字符设备...}

这里直接使用pdev->dev.of_node引用设备节点即可

后续注册字符设备、操作函数ops等无改动,不再赘述

测试

在这里插入图片描述

chardevApp同样使用上一章的测试APP,功能正常实现

源码

platform_led.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/printk.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>struct platform_led_dev
{dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *nd;int led_gpio;
};
struct platform_led_dev platform_led = {.major = 0,
};#define PIN_N 0 // 第0个引脚,PG0,绿色
#define DEV_NAME "platform_led"
#define LED_ON 0 // 上拉,低电平亮
#define LED_OFF 1static int led_gpio_open(struct inode *inode, struct file *file)
{file->private_data = &platform_led;return 0;
}static int led_gpio_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{return 0;
}static int led_gpio_release(struct inode *inode, struct file *file)
{return 0;
}static ssize_t led_gpio_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{int ret = 0;unsigned char databuf;struct platform_led_dev *dev = file->private_data;ret = copy_from_user(&databuf, user_buf, sizeof(databuf));if (ret < 0){pr_err("copy_from_user failed\r\n");return -EFAULT;}if (databuf == 0 || databuf == '0') // LED_OFFgpio_set_value(dev->led_gpio, 1);if (databuf == 1 || databuf == '1') // LED_ONgpio_set_value(dev->led_gpio, 0);return 1;
}static const struct file_operations platform_led_fops = {.open = led_gpio_open,.read = led_gpio_read,.release = led_gpio_release,.write = led_gpio_write,
};static int led_probe(struct platform_device *pdev)
{int ret;const char *str;printk("led driver and device was matched!\r\n");// 获取 LED 灯的 GPIO 号// 进probe说明设备树已匹配,直接使用设备树节点即可platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (platform_led.led_gpio < 0){printk("can't get gpios");return -EINVAL;}printk("gpio num = %d\r\n", platform_led.led_gpio);// 向 gpio 子系统申请使用 GPIOret = gpio_request(platform_led.led_gpio, "green");if (ret){printk(KERN_ERR "Failed to request gpio\n");return ret;}// 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯ret = gpio_direction_output(platform_led.led_gpio, 0);if (ret < 0){printk("can't set gpio!\r\n");}// 注册字符设备if (platform_led.major) // 定义了设备号,静态设备号{platform_led.devid = MKDEV(platform_led.major, 0);ret = register_chrdev_region(platform_led.major, 1, DEV_NAME);if (ret < 0){pr_err("cannot register %s char driver.ret:%d\r\n", DEV_NAME, ret);goto exit;}}else // 没有定义设备号,动态申请设备号{ret = alloc_chrdev_region(&platform_led.devid, 0, 1, DEV_NAME);if (ret < 0){pr_err("cannot alloc_chrdev_region,ret:%d\r\n", ret);goto exit;}platform_led.major = MAJOR(platform_led.devid);platform_led.minor = MINOR(platform_led.devid);}printk("led major=%d,minor=%d\r\n", platform_led.major, platform_led.minor);platform_led.cdev.owner = THIS_MODULE;cdev_init(&platform_led.cdev, &platform_led_fops);ret = cdev_add(&platform_led.cdev, platform_led.devid, 1);if (ret < 0)goto del_unregister;platform_led.class = class_create(THIS_MODULE, DEV_NAME);if (IS_ERR(platform_led.class))goto del_cdev;platform_led.device = device_create(platform_led.class, NULL, platform_led.devid, NULL, DEV_NAME);if (IS_ERR(platform_led.device))goto destroy_class;return 0;// 注意  goto后的标签没有return操作,将顺序执行多个label直至return,这里反向写
destroy_class:class_destroy(platform_led.class);
del_cdev:cdev_del(&platform_led.cdev);
del_unregister:unregister_chrdev_region(platform_led.devid, 1);
exit:printk("init failed\r\n");return -EIO;
}static int led_remove(struct platform_device *pdev)
{if (platform_led.led_gpio){gpio_set_value(platform_led.led_gpio, 1);gpio_free(platform_led.led_gpio);}cdev_del(&platform_led.cdev);unregister_chrdev_region(platform_led.devid, 1);device_destroy(platform_led.class, platform_led.devid);class_destroy(platform_led.class);return 0;
}//platform相关
// 匹配列表
static const struct of_device_id led_of_match[] = {{ .compatible = "user,led" },{ /* Sentinel */ }
};MODULE_DEVICE_TABLE(of, led_of_match);/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver		= {.name	= "platform-led",			// 驱动名字,将在/sys/bus/platform/drivers/下生成 .of_match_table	= led_of_match,     // 设备树匹配表},.probe		= led_probe,.remove		= led_remove,
};static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(leddriver_init);
module_exit(leddriver_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("USER");
MODULE_INFO(intree, "Y");

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

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

相关文章

ELK介绍使用

文章目录 一、ELK介绍二、Elasticsearch1. ElasticSearch简介&#xff1a;2. Elasticsearch核心概念3. Elasticsearch安装4. Elasticsearch基本操作1. 字段类型介绍2. 索引3. 映射4. 文档 5. Elasticsearch 复杂查询 三、LogStash1. LogStash简介2. LogStash安装 四、kibana1. …

向爬虫而生---Redis 探究篇8<保障缓存和持久化数据一致性的研究与实现(中) `方案篇`>

前言: 继续上一篇向爬虫而生---Redis 探究篇8<保障缓存和持久化数据一致性的研究与实现(1)>-CSDN博客 缓存双写一致性是指在系统中同时使用缓存和持久化存储时,保证两者数据的一致性。我们将探讨四种缓存双写一致性的解决方案: 先更新持久化存储再更新缓存先更新缓存再更…

【企业发展战略】某环境管理集团公司发展战略与规划项目纪实

在集团公司高速发展、业务范围不断扩大时&#xff0c;组织往往对公司未来的发展方向感到迷茫&#xff0c;不知道如何进行更好的规划&#xff0c;找到合适的发展战略&#xff0c;为企业提供更长远的发展空间&#xff0c;带来更多是利益。面对这个问题&#xff0c;华恒智信认为企…

远程在线教育平台从涉及到落地实践

在当前数字化时代&#xff0c;远程在线教育平台正成为教育行业的重要趋势之一。随着互联网技术的不断发展&#xff0c;人们对于灵活、便捷的学习方式需求日益增加&#xff0c;远程在线教育平台为广大学生和教育机构提供了全新的学习和教学模式。然而&#xff0c;要让远程在线教…

242.有效的字母异位词

242.有效的字母异位词 力扣题目链接(opens new window) 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2: 输入: s "rat", t "car&qu…

SQLite语句

1.重写SQLiteOpenHelper // 例. public class MySQLiteOpenHelper extends SQLiteOpenHelper {public MySQLiteOpenHelper(Nullable Context context, Nullable String name, Nullable SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, ve…

【Linux】Docker安装

卸载旧版Docker 新版docker无法覆盖旧版的&#xff0c;所以需要先卸载原来的旧版本 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-selinux \docker-engine-selinux \docker-eng…

uniapp微信小程序获取当前位置

uni-app微信小程序uni.getLocation获取位置&#xff1b;authorize scope.userLocation需要在app.json中声明permission&#xff1b;小程序用户拒绝授权后重新授权-CSDN博客

Python工具小技巧

Python工具小技巧 将.py后缀文件转化为.exe后缀文件安装PyinstallerPyinstaller参数大全 将.py后缀文件转化为.exe后缀文件 目前比较常见的打包exe方法都是通过Pyinstaller来实现的&#xff0c;本文也将使用这种常规方法。 安装Pyinstaller 首先我们要先安装Pyinstaller&…

CSS中em/px/rem/vh/vw区别详解

文章目录 一、介绍二、单位pxemremvh、vw 三、总结 一、介绍 传统的项目开发中&#xff0c;我们只会用到px、%、em这几个单位&#xff0c;它可以适用于大部分的项目开发&#xff0c;且拥有比较良好的兼容性 从CSS3开始&#xff0c;浏览器对计量单位的支持又提升到了另外一个境…

linux系统简述docker

简述docker docker理念docker三要素docker平台架构docker运行的基本流程 docker理念 一次镜像&#xff0c;处处运行 基于go语言实现的项目 解决了运行环境和配置问题的软件容器&#xff0c;方便做持续集成并有助于整体发布的容器虚拟化技术 能够使硬件、操作系统和应用程序三者…

SpringBoot配置文件

在SpringBoot中默认配置文件是在resources目录下的名为application的文件&#xff0c;常用后缀为&#xff1a;.properties、.yml、.yaml 一、指定自己的配置文件 如果把所有的配置文件都放到一个application.properties文件中&#xff0c;难免有些太多&#xff0c;有的时候我们…

Rust: Channel 代码示例

在 Rust 中&#xff0c;通道&#xff08;Channel&#xff09;通常使用 std::sync::mpsc&#xff08;多生产者单消费者&#xff09;或 tokio::sync::mpsc&#xff08;在异步编程中&#xff0c;特别是使用 Tokio 运行时&#xff09;来创建。下面是一个使用 std::sync::mpsc 的简单…

【C语言】数据类型和变量

前言&#x1f49e;&#x1f49e; 啦啦啦~这里是土土数据结构学习笔记&#x1f973;&#x1f973; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1f4a5; 所属专栏&#xff1a;C语言笔记 &#x1f4a5;欢迎大家&#x1f973;&#x1f973;点赞✨收藏&#x1f49…

Day24:安全开发-PHP应用文件管理模块显示上传黑白名单类型过滤访问控制

目录 文件管理模块-上传-过滤机制 文件管理模块-显示-过滤机制 思维导图 PHP知识点 功能&#xff1a;新闻列表&#xff0c;会员中心&#xff0c;资源下载&#xff0c;留言版&#xff0c;后台模块&#xff0c;模版引用&#xff0c;框架开发等 技术&#xff1a;输入输出&#…

[java基础揉碎]方法的重写/覆盖

重写介绍 简单的说:方法覆盖(重写)就是子类有一个方法,和父类(也可能是爷爷,更上一级)的某个方法的名称、返回类型、参数一样&#xff0c;那么我们就说子类的这个方法 覆盖了父类的方法 重写重载对比

语音模块学习——LSYT201B模组(实际操作篇)

目录 一、定制词条 二、直接用串口通信 三、使用单片机通信 理论篇在这&#xff0c;依旧是深圳雷龙发展的语音模块。 http://t.csdnimg.cn/2SzJL 一、定制词条 因为我想后面加到我的毕设上加个语音模块&#xff0c;所以定制的词条都是和芯测相关的。 动作词条播报串口输…

List--splice使用技巧

splice : 拼接两个list api: void dump(list<int>& li) {for(auto & i :li)cout<<i<< " ";cout<<endl; } int main() { list<int> li1 {1,3,5};list<int> li2 {2,4,6}; }1 c.splice(pos,c2); // li的开头插入li2链表…

深入理解锁的升级与降级

深入理解锁的升级与降级 在并发编程中&#xff0c;锁是一种非常重要的同步机制&#xff0c;用于协调多个线程对共享资源的访问。为了提高性能和减少线程间的竞争&#xff0c;现代操作系统和编程语言运行时通常提供了多种类型的锁&#xff0c;并支持锁的升级和降级策略。本文将…

排序(6)——快速排序算法之挖坑版&前后指针版

目录 挖坑版 基本思路 代码实现 注意点 前后指针版 基本思路 代码实现 注意点 由于hoare版本的快速排序有很多坑和需要注意的地方&#xff0c;就会导致代码写起来不容易&#xff0c;这里我们给出两种不同的单趟排序思路&#xff1a;挖坑版&#xff06;前后指针版。 挖坑…