嵌入式Linux学习: platform 设备驱动实验

在Linux中,Platform(平台)机制是一个重要的设备驱动管理框架,它主要在Linux 2.6内核及以后的版本中引入。Platform机制的主要目的是提供一种统一的方式来管理那些不直接挂靠在传统物理总线(如USB、PCI、I2C、SPI等)上的设备,如SoC(系统芯片)内部集成的外设控制器或挂接在SoC内存空间的外设。以下是对Linux中Platform机制的简要介绍:

1. Platform总线的概念

  • 虚拟总线:Platform总线是一种虚拟的、抽象出来的总线,实际中并不存在这样的物理总线。它是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的。
  • 作用:通过将那些不依赖于传统物理总线的设备映射到Platform总线上,Linux能够对这些设备进行统一的管理,从而保持设备驱动模型的完整性和一致性。

2. Platform机制的主要组成部分

  • platform_bus:这是一个全局的bus_type结构体实例,用于描述Platform总线。它包含了总线的基本信息和匹配函数等。
  • platform_device:描述Platform总线上的设备。它包含了设备的名称、ID、资源(如I/O端口、中断号等)以及设备特定的信息(如platform_data)。
  • platform_driver:描述与Platform设备相匹配的驱动程序。它包含了用于探测(probe)、移除(remove)、挂起(suspend)和恢复(resume)设备的函数指针,以及指向设备驱动名称或ID表的指针。

3. Platform机制的工作流程

  • 设备注册:首先,需要将Platform设备通过platform_device_register函数注册到系统中。在注册过程中,系统会为设备分配必要的资源,并在sysfs中创建相应的设备节点。
  • 驱动注册:接着,将Platform驱动通过platform_driver_register函数注册到系统中。在注册过程中,系统会检查是否有与当前驱动匹配的设备,如果有,则调用驱动的probe函数进行设备的初始化和配置。
  • 设备与驱动的匹配:Platform机制通过设备名称或ID表来匹配设备和驱动。如果设备名称与驱动中指定的名称相同,或者设备的ID与驱动ID表中的某个条目匹配,则认为该设备与驱动相匹配。
  • 驱动加载与设备初始化:一旦设备和驱动匹配成功,系统就会调用驱动的probe函数来加载驱动并初始化设备。在probe函数中,驱动可以获取设备资源、配置设备寄存器以及注册中断等。

4. 匹配规则

在这里插入图片描述
在这里插入图片描述
相关函数使用方法这里不再叙述,请自行查找。

在这里插入图片描述

5. Platform机制的优点

  • 统一性:Linux内核支持多种类型的硬件设备,这些设备可能通过不同的物理总线(如USB、PCI、SPI等)连接,也可能直接集成在SoC(系统芯片)内部,没有明确的物理总线接口。Platform机制提供了一种统一的方式来管理这些不同类型的设备,无论是挂靠在物理总线上的还是集成在SoC内部的,都可以通过Platform总线进行统一管理。
  • 灵活性:Platform机制允许设备和驱动分别注册,并通过匹配机制动态地建立联系。这使得驱动的开发和部署更加灵活。
  • 可移植性:Platform机制将驱动的实现和资源分离,使得驱动代码更加独立和可移植。

综上所述,Linux中的Platform机制是一种重要的设备驱动管理框架,它通过虚拟的Platform总线来统一管理那些不依赖于传统物理总线的设备,从而提高了设备驱动的统一性、灵活性和可移植性。

6. 实验代码

本次实验所用的是韦东山I.MX6U开发板,实验功能是无设备树的platform来驱动led。

platform_device_led.c文件
#include "linux/ioport.h"
#include "linux/printk.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define GPIO5_DR_BASE 0x020AC000
#define GPIO5_GDIR_BASE 0X020AC004
#define SW_MUX_GPIO5_IO03_BASE 0x02290014#define REGISTER_LEN 3void led_release(struct device *dev){printk("led_release\r\n");
}
static struct resource led_resources[] = {[0] = {.start =    GPIO5_DR_BASE,.end   =    GPIO5_DR_BASE + REGISTER_LEN -1,.flags =    IORESOURCE_MEM,},[1] = {.start =    GPIO5_GDIR_BASE,.end   =    GPIO5_GDIR_BASE + REGISTER_LEN -1,.flags =    IORESOURCE_MEM,},[2] = {.start =    SW_MUX_GPIO5_IO03_BASE,.end   =    SW_MUX_GPIO5_IO03_BASE + REGISTER_LEN -1,.flags =    IORESOURCE_MEM,},
};static struct platform_device led_device = {.name   = "imx6ull_led",.id     = -1,.dev    = {.release = led_release,},.num_resources = ARRAY_SIZE(led_resources),.resource =   led_resources,
};/*设备加载
*/
static int __init led_init(void)
{// 注册platform设备return platform_device_register(&led_device);
}/*设备卸载 
*/
static void __exit led_exit(void)
{// 卸载platform设备platform_device_unregister(&led_device);
}module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pumpk1n");
platform_driver_led.c文件
#include "asm-generic/errno-base.h"
#include "asm-generic/int-ll64.h"
#include "linux/compiler.h"
#include "linux/err.h"
#include "linux/export.h"
#include "linux/ioport.h"
#include "linux/kdev_t.h"
#include "linux/leds.h"
#include "linux/printk.h"
#include "linux/stddef.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define LEDDEV_CNT  1 // 设备号长度
#define LEDDEV_NAME "plat_led" // 设备名
#define LED_OFF     0
#define LED_ON      1   /*#define GPIO5_DR_BASE 0x020AC000
#define GPIO5_GDIR_BASE 0X020AC004
#define SW_MUX_GPIO5_IO03_BASE 0x02290014*//* 寄存器名 */
static void __iomem *GPIO5_DR;
static void __iomem *GPIO5_GDIR;
static void __iomem *SW_MUX_GPIO5_IO03;/* leddev 设备结构体 */
struct leddev_dev{dev_t devid; // 设备号struct cdev cdev; // cdevstruct class *clazz;  // 类struct device *device; // 设备int major;   //主设备号
};struct leddev_dev led_dev;void led_switch(u8 sta){u32 val;if(sta == LED_ON){val = readl(GPIO5_DR);val &= ~(1 << 3);writel(val, GPIO5_DR);}else if(sta == LED_OFF){val = readl(GPIO5_DR);val |= (1 << 3);writel(val, GPIO5_DR);}
}static int led_open(struct inode *inode, struct file *file)
{file->private_data = &led_dev;return 0;
}ssize_t led_write (struct file *file, const char __user *buf, size_t size, loff_t *offset){u_char dataBuf[1];u_char led_sta;int ret;ret  =  copy_from_user(dataBuf, buf, size);if(ret < 0){return -EFAULT;}led_sta = dataBuf[0];if(led_sta == LED_ON)led_switch(LED_ON);else if(led_sta == LED_OFF)led_switch(LED_OFF);return 0;
}static const struct file_operations led_fops = {.owner		= THIS_MODULE,.open		= led_open,.write      = led_write,
};/* platform的probe函数,当驱动与设备匹配成功后执行此函数*/
static int led_probe(struct platform_device *pdev){int i;u32 val;int resize[3];struct resource *led_source[3];printk("led driver and device has matched!\r\n");/* 1.获取资源 */for(i = 0; i < 3; i++){led_source[i] = platform_get_resource(pdev,IORESOURCE_MEM, i);if(!led_source[i]){ // 资源为空dev_err(&pdev->dev, "No MEM resource for always on\r\n");return -ENXIO;}resize[i] = resource_size(led_source[i]);}/* 寄存器地址映射 */GPIO5_DR = ioremap(led_source[0]->start, resize[0]);GPIO5_GDIR = ioremap(led_source[1]->start, resize[1]);SW_MUX_GPIO5_IO03 = ioremap(led_source[2]->start,resize[2]);val = readl(GPIO5_GDIR);val |= (1 << 3);writel(val, GPIO5_GDIR); // 输出模式/* 注册字符设备驱动 */if(led_dev.major){led_dev.devid = MKDEV(led_dev.major, 0);register_chrdev_region(led_dev.devid, LEDDEV_CNT, LEDDEV_NAME);}else{alloc_chrdev_region(&led_dev.devid, 0, LEDDEV_CNT,LEDDEV_NAME);led_dev.major = MAJOR(led_dev.devid);}/* 初始化CDEV */led_dev.cdev.owner = THIS_MODULE;cdev_init(&led_dev.cdev, &led_fops);/* 添加一个CDEV */cdev_add(&led_dev.cdev, led_dev.devid,LEDDEV_CNT);/* 创建类 */led_dev.clazz = class_create(THIS_MODULE, LEDDEV_NAME); // /dev/plat_ledif(IS_ERR(led_dev.clazz)){return PTR_ERR(led_dev.clazz);}/* 创建设备 */led_dev.device = device_create(led_dev.clazz,NULL,led_dev.devid,NULL,LEDDEV_NAME);if(IS_ERR(led_dev.device)){return PTR_ERR(led_dev.device);}return 0;
}/* 移除platform驱动执行 */
int led_remove(struct platform_device * pdevice){iounmap(GPIO5_DR);iounmap(GPIO5_GDIR);iounmap(SW_MUX_GPIO5_IO03);cdev_del(&led_dev.cdev);unregister_chrdev_region(led_dev.devid,LEDDEV_CNT);device_destroy(led_dev.clazz, led_dev.devid);class_destroy(led_dev.clazz);return 0;
}/* platform_driver结构体 */
static struct platform_driver led_driver = {.driver = {.name = "imx6ull_led",},.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("Pumpk1n");
测试文件: led_test.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************描述 : platform 驱动驱测试 APP。
***************************************************************/
#define LEDOFF 0
#define LEDON 1/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char databuf[2];
if(argc != 3){printf("Error Usage!\r\n");
return -1;
}filename = argv[1];
/* 打开 led 驱动 */fd = open(filename, O_RDWR);
if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;
}databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){
printf("LED Control Failed!\r\n");close(fd);
return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);return -1;
}
return 0;
}

编译 led_test.c文件为 led_test可执行文件

arm-buildroot-linux-gnueabihf-gcc led_test.c -o led_test

Makefile文件
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
CURRENT_PATH :=  `pwd`obj-m := platform_device_led.o platform_driver_led.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERN_DIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERN_DIR) M=$(CURRENT_PATH) clean
挂载网络文件系统
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88/hello_drv/platform_demo$  cp platform_driver_led.ko platform_device_led.ko led_test ~/nfs_rootfs/
[root@100ask:/mnt]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
设置交叉编译工具链

在串口连接终端里配置

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin

查看设备是否存在

[root@100ask:/mnt]# ls /dev/plat_led
/dev/plat_led
[root@100ask:/mnt]# ls /mnt/ 
drivers_projects  hello  input_demo  led_drv.ko  led_test  platform_device_led.ko  platform_driver_led.ko  platform_led.ko

在开发板上加载驱动程序

insmod /mnt/platform_driver_led.ko
insmod /mnt/platform_device_led.ko

测试

/mnt/led_test /dev/plat_led 1     ## LED点亮
/mnt/led_test /dev/plat_led 0     ## LED熄灭

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

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

相关文章

论文阅读:Speculative RAG: Enhancing Retrieval Augmented Generation through Drafting

论文地址&#xff1a;https://arxiv.org/abs/2407.08223 RAG 将 LLM 的生成能力与外部知识源相结合&#xff0c;以提供更准确和最新的响应。最近的 RAG 进展侧重于通过迭代 LLM 完善或通过 LLM 的额外指令调整获得自我批判能力来改进检索结果。在这项工作中&#xff0c;作者介…

编程中的智慧六:单例、原型、建造者

上一篇咱们结合Spring介绍了设计模式中的工厂模式相关方法&#xff0c;其实现在Java开发基本上都是基于Spring框架开发&#xff0c;所以后续我们在开发过程中基本上很少自己重写一个工厂模式&#xff0c;都是直接使用Spring来完成。今天咱们接着看剩下的创建型设计模式&#xf…

Dubbo学习笔记

Dubbo 简介 Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架&#xff0c;可以和Spring框架无缝集成。 其中文官网:https://dubbo.gitbooks.io/dubbo-user-book/content/ 特性和用法 架构 节点角色说明 节点角色…

应用层的重点协议

目录 一、DNS 二、NAT 1、技术背景 2、NAT机制IP转化过程 三、NAPT NAT技术的缺陷 四、HTTP 1、协议格式 2、HTTP请求 1&#xff09;method(方法) GET请求的特点 POST请求的特点 GET和POST的区别 2&#xff09;URL(网址) 基本格式 关于URL encode 3&#xff0…

el-table表头使用el-dropdown出现两个下拉框

问题描述&#xff1a;el-table在固定右边列时&#xff0c;表头使用el-dropdown会出现两个下拉框&#xff0c;如图所示&#xff1a; 解决方法&#xff1a; 1.只显示第一个下拉框&#xff0c;通过控制样式将其他的下拉框display:none; 2.如图所示&#xff0c;修改插槽写法&…

<数据集>学生课堂行为识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;13899张 标注数量(xml文件个数)&#xff1a;13899 标注数量(txt文件个数)&#xff1a;13899 标注类别数&#xff1a;8 标注类别名称&#xff1a;[js, tt, dk, zt, dx, zl, jz, xt] # 举手 js # 抬头听课 …

从FasterTransformer源码解读开始了解大模型(2.3)代码通读04

从FasterTransformer源码解读开始了解大模型&#xff08;2.3&#xff09;代码解读04-forward函数 写在前面的话 本篇的内容继续解读forward函数&#xff0c;从972行开始进行解读 零、embedding函数 让我们考虑一种不包含prefix_soft_prompt的情况&#xff0c;从999行的embe…

在 PostgreSQL 里如何实现数据的冷热数据分层存储的自动化策略调整?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 在 PostgreSQL 里如何实现数据的冷热数据分层存储的自动化策略调整 在 PostgreSQL 里如何实现数据的冷…

【数据分享】2013-2022年我国省市县三级的逐日SO2数据(excel\shp格式\免费获取)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000——2022年的省市县三级的逐日PM2.5数据和2013-2022年的省市县三级的逐日CO数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次我们分享的是我国2013——2022年的省…

mongodb数据导出与导入

一、先去检查mongodump mongodump --version 如果报 mongodump version: built-without-version-string 或者其他的较老的版本&#xff0c;直接去下载最新的【传送门】 【以Ubuntu18.04为例】 安装工具 假设你下载的是 .tgz 文件&#xff08;适用于 Linux 系统&#xff09;&am…

【项目】星辰博客介绍

目录 一、项目背景 二、项目功能 1. 登录功能&#xff1a; 2. 列表页面&#xff1a; 3. 详情页面&#xff1a; 4. 写博客&#xff1a; 三、技术实现 四、功能页面展示 1. 用户登录 2. 博客列表页 3. 博客编辑更新页 4.博客发表页 5. 博客详情页 五.系统亮点 1.强…

高性能图数据库Neo4j从入门到实战

图数据库Neo4j介绍 什么是图数据库&#xff08;graph database&#xff09; 随着社交、电商、金融、零售、物联网等行业的快速发展&#xff0c;现实社会织起了了一张庞大而复杂的关系网&#xff0c;传统数据库很难处理关系运算。大数据行业需要处理的数据之间的关系随数据量呈…

【数据结构】栈和队列的深度探索,从实现到应用详解

&#x1f48e;所属专栏&#xff1a;数据结构与算法学习 &#x1f48e; 欢迎大家互三&#xff1a;2的n次方_ &#x1f341;1. 栈的介绍 栈是一种后进先出的数据结构&#xff0c;栈中的元素只能从栈顶进行插入和删除操作&#xff0c;类似于叠盘子&#xff0c;最后放上去的盘子最…

广州机房搬迁网络部署方案

新机房网络部署应包括核心模块、业务模块、光传输模块、安全模块、流量采集模块、路由模块、带外管理模块等&#xff0c;每个模块都根据业务需求规划成多个POD&#xff08;Point Of Delivery&#xff0c;基本物理设计单元&#xff09;。 核心模块部署主要实现各业务模块的高速互…

HighConcurrencyCommFramework c++通讯服务器框架 :目录,修改标题,配置,日志打印

目录规划 nginx 根目录下的三个文件 makefile :编译项目的入口&#xff0c;编译项目从这里开始 config.mk&#xff1a;也是个配置脚本用来增加变动的东西&#xff0c;应付可变 common.mk&#xff1a;最核心的编译脚本&#xff0c;每个子目录都要被编译.cpp程序 配置文件 配…

postman创建mock server

B站博主的说明&#xff1a;

《python语言程序设计》第6章2题(求一个整数各个数字的和)编写一个函数

求一个整数各个数字的和编写一个函数&#xff0c;计算一个整数各个数字的和&#xff0c; def sumDigits(n):a n // 100b n % 100 // 10c n % 100 % 10print(f"{n}数&#xff0c;分成个&#xff0c;十&#xff0c;百&#xff0c;{a}{b}{c}", a b c)sumDigits(23…

算法日记day 16(二叉树的广度优先遍历|反转、对称二叉树)

一、二叉树的层序遍历 题目&#xff1a; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3]…

SQUID - 形状条件下的基于分子片段的3D分子生成等变模型 评测

SQUID 是一个形状条件下基于片段的3D分子生成模型&#xff0c;给一个3D参考分子&#xff0c;SQUID 可以根据参考分子的形状&#xff0c;基于片段库&#xff0c;生成与参考分子形状非常相似的分子。 SQUID 模型来自于 ICLR 2023 文章&#xff08;2022年10月6日提交&#xff09;&…

vue+watermark-dom实现页面水印效果

前言 页面水印大家应该都不陌生&#xff0c;它可以用于验证数字媒体的来源和完整性&#xff0c;还可以用于版权保护和信息识别&#xff0c;这些信息可以在不影响媒体质量的情况下嵌入&#xff0c;‌并在需要时进行提取。‌本文将通过 vue 结合 watermark-dom 库&#xff0c;教大…