荔枝派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 子系统申请使用 GPIO
    ret = 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 1

static 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_OFF
        gpio_set_value(dev->led_gpio, 1);
    if (databuf == 1 || databuf == '1') // LED_ON
        gpio_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 子系统申请使用 GPIO
    ret = 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");

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

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

相关文章

Mac测试环境搭建

1 下载pycharm 下载地址&#xff1a;PyCharm&#xff1a;JetBrains 出品的用于数据科学和 Web 开发的 Python IDE 2 安装python3.6.8 下载地址&#xff1a;Index of /ftp/python/3.6.8/ 安装后提示错误 换一种方式&#xff1a;用conda 下载地址&#xff1a;Free Download | …

实现QT中qDebug()的日志重定向

背景&#xff1a; 在项目开发过程中&#xff0c;为了方便分析和排查问题&#xff0c;我们需要将原本输出到控制台的调试信息写入日志文件&#xff0c;进行持久化存储&#xff0c;还可以实现日志分级等。 日志输出格式&#xff1a; 我们需要的格式包括以下内容&#xff1a; 1.…

鸡肋的Git

1.前言 对于大多数开发人员来说&#xff0c;我们大多数在学习或者工作过程中只关注核心部分&#xff0c;比如说学习Java&#xff0c;可能对于大多数人而言一开始都是从Java基础学起&#xff0c;然后408&#xff0c;Spring&#xff0c;中间件等&#xff0c;当你发现很多高深的技…

红队专题-开源漏扫-巡风xunfeng源码剖析与应用

开源漏扫-巡风xunfeng 介绍主体两部分:网络资产识别引擎,漏洞检测引擎。代码赏析插件编写JSON标示符Python脚本此外系统内嵌了辅助验证功能文件结构功能 模块添加IP三. 进行扫描在这里插入图片描述 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/de587a6f6f694…

html前端的几种加密/解密方式

HTML前端的加密解密方式有以下几种&#xff1a; 一、base64加密 Base64编码&#xff1a;Base64是一种将二进制数据转换为可打印字符的编码方式。在前端&#xff0c;可以使用JavaScript的btoa()函数进行Base64编码&#xff0c;使用atob()函数进行解码。 var str "hello…

二维码门楼牌管理系统在教育领域的应用及其优势

文章目录 前言一、二维码门楼牌管理系统概述二、教育领域的应用场景三、二维码门楼牌管理系统的优势四、结语 前言 随着信息技术的快速发展&#xff0c;二维码门楼牌管理系统在教育领域的应用越来越广泛。该系统不仅提高了地址信息的准确性&#xff0c;还为学校、家长和教育工…

Feign实现微服务间远程调用续;基于Redis实现消息队列用于延迟任务的处理,Redis分布式锁的实现;(黑马头条Day05)

目录 延迟任务和定时任务 使用Redis设计延迟队列原理 点评项目中选用list和zset两种数据结构进行实现 如何缓解Redis内存的压力同时保证Redis中任务能够被正确消费不丢失 系统流程设计 使用Feign实现微服务间的任务消费以及文章自动审核 系统微服务功能介绍 提交文章-&g…

C#,数值计算,解微分方程的龙格-库塔四阶方法与源代码

Carl Runge Martin Wilhelm Kutta 1 龙格-库塔四阶方法 数值分析中&#xff0c;龙格&#xff0d;库塔法&#xff08;Runge-Kutta&#xff09;是用于模拟常微分方程的解的重要的一类隐式或显式迭代法。这些技术由数学家卡尔龙格和马丁威尔海姆库塔于1900年左右发明。 对于一阶…

[Electron]中IPC进程间通信

Electron中IPC 进程间通信 (IPC) 是在 Electron 中构建功能丰富的桌面应用程序的关键部分之一。在 Electron 中&#xff0c;进程使用 ipcMain 和 ipcRenderer 模块&#xff0c;通过开发人员定义的“通道”传递消息来进行通信。 本文介绍以下几个方面&#xff1a; 1-渲染进程到…

【vue.js】文档解读【day 3】 | 列表渲染

如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 文章目录 列表渲染v-forv-for 与对象在 v-for 里使用范围值template 上的 v-forv-for与v-if通过key管理状态组件上使用v-for数组变化侦测 列表渲染 v-for 在我们想要渲染出一个数组中的元素时&#xf…

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

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

linux网络编程(概念)

概念 通信四元组 IP&#xff08;主机&#xff09; 0号地址与1号地址 端口&#xff08;进程&#xff09; 四元组组成 各种体系结构 网络的封包和解包 ip地址向物理&#xff08;mac&#xff09;地址转换 mac转换ip-------->RARP协议 TCP协议 UDP协议 socket函数接口

瑞_23种设计模式_模板方法模式

文章目录 1 模板方法模式&#xff08;Template Pattern&#xff09; ★ 钩子函数1.1 介绍1.2 概述1.3 模板方法模式的结构1.4 模板方法模式的优缺点1.5 模板方法模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK源码解析&#xff08;InputStre…

java项目线上捉BUG经验记录

一 线上问题 昨晚上突然接到公司紧急来电电桩设备大面积离线&#xff0c;意味着某市的车无法充电……”&#xff0c;细想这个平台很久都没有更新&#xff0c;最近出现问题是刚好在一个月前也是出现这种情况&#xff0c;再上一次就是几年前更新的。平台平时是稳定&#xff0c;开…

php使用ElasticSearch

ElasticSearch简介 Elasticsearch 是一个分布式的、开源的搜索分析引擎&#xff0c;支持各种数据类型&#xff0c;包括文本、数字、地理、结构化、非结构化。 Lucene与ElasticSearch Apache Lucene是一款高性能的、可扩展的信息检索&#xff08;IR&#xff09;工具库&#xf…

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

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

StarRocks实战——欢聚集团极速的数据分析能力

目录 一、大数据平台架构 二、OLAP选型及改进 三、StarRocks 经验沉淀 3.1 资源隔离&#xff0c;助力业务推广 3.1.1 面临的挑战 3.1.2 整体效果 3.2 稳定优先&#xff0c;监控先行&#xff0c;优化运维 3.3降低门槛&#xff0c;不折腾用户 3.3.1 与现有的平台做打通 …

Springboot+vue的物业管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的物业管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的物业管理系统&#xff0c;采用M&#xff08;model&#xff09;V&#xff…

C++自创题目——几点了 very hard ver.

题目难度 普及 题目描述 一个老外用一口不流利的中文问你&#xff1a;“Xian zai ji dian le?”你看了一眼表&#xff0c;知道了现在是&#xff0c;你准备用这样的形式写在纸上&#xff1a; Now is m past/to h. 如果你看不懂&#xff0c;举个例子&#xff1a; 当h10&#…

运维知识点-Apache HTTP Server

Apache 介绍 介绍 Apache是一个开源的Web服务器软件&#xff0c;全称为Apache HTTP Server&#xff0c;由Apache软件基金会开发和维护。它是目前全球使用最广泛的Web服务器软件之一&#xff0c;占全球所有网络服务器的很大比例。Apache服务器具有跨平台的特性&#xff0c;可以…