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

news2024/12/26 14:08:43

在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 3


void 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; // cdev
    struct 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_led
      if(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.o

build: kernel_modules

kernel_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.coloradmin.cn/o/1939775.html

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

相关文章

单链表的应用(3)

返回倒数第k个结点 实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 思路&#xff1a; 利用快慢指针&#xff0c;先让快指针走k步快慢指针一起往后遍历&#xff0c;直到快指针到达链表的末端此时的慢指针就是链表的倒数第k个结点 int kthToLast(…

昇思MindSpore学习总结十六 —— 基于MindSpore的GPT2文本摘要

1、mindnlp 版本要求 !pip install tokenizers0.15.0 -i https://pypi.tuna.tsinghua.edu.cn/simple # 该案例在 mindnlp 0.3.1 版本完成适配&#xff0c;如果发现案例跑不通&#xff0c;可以指定mindnlp版本&#xff0c;执行!pip install mindnlp0.3.1 !pip install mindnlp …

提供代码!直接可以运行,Chatgpt代码分享

效果演示 安装依赖库 pip install openai粘贴如下代码 # 设置 API Key import openaiopenai.api_key "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"# 设置请求参数model_engine "text-davinci-002"prompt "python的应用领域"comp…

Bonree ONE赋能汽车行业 重塑可观测性体验

随着数字化、智能化浪潮的汹涌而至&#xff0c;全球汽车产业正站在一个崭新的历史起点上。新能源汽车&#xff0c;作为这场科技革命和产业变革的领跑者&#xff0c;其数智化发展正呈现出前所未有的蓬勃态势。7月18-19日&#xff0c;第四届中国新能源汽车产业数智峰会于上海举办…

《0基础》学习Python——第二十三讲__网络爬虫/<6>爬取哔哩哔哩视频

一、在B站上爬取一段视频&#xff08;B站视频有音频和视频两个部分&#xff09; 1、获取URL 注意&#xff1a;很多平台都有反爬取的机制&#xff0c;B站也不例外 首先按下F12找到第一条复制URL 2、UA伪装&#xff0c;下列图片中&#xff08;注意代码书写格式&#xff09; 3、Co…

【2024】springboot O2O生鲜食品订购

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

sass版本更新,不推荐使用嵌套规则后的声明

目前在 Sass 中不推荐使用嵌套规则后的声明&#xff0c;在 为了通知用户即将进行的更改&#xff0c;并给他们时间进行更改 与之兼容的样式表。在未来的版本中&#xff0c;Dart Sass 将更改为 匹配纯 CSS 嵌套生成的顺序。Deprecation Warning: Sasss behavior for declarations…

视频点播项目

文章目录 视频点播技术栈与项目环境JsonCppMariaDBhttplib 工具类设计文件类Json类 数据管理模块视频信息管理&#xff08;数据表设计&#xff09;数据管理类设计 网络通信接口设计业务处理模块设计前端界面主页面播放页面 项目总结项目回顾项目结构关键技术点总结 视频点播 允…

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

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

SpringBoot源码深度解析

今天&#xff0c;聊聊SpringBoot的源码&#xff0c;本博客聊的版本为v2.0.3.RELEASE。目前SpringBoot的最新版为v3.3.2&#xff0c;可能目前有些公司使用的SpringBoot版本高于我这个版本。但是没关系&#xff0c;因为版本越新&#xff0c;新增的功能越多&#xff0c;反而对Spri…

如何将几百兆的包优化到几十兆----记一次vue项目的打包优化过程

打包优化 现象 前段时间开发的时候遇到客户反馈的一个问题 界面无法打开&#xff0c;显示白屏&#xff0c;控制台无报错 经过我们在开发环境&#xff0c;测试环境反复测试都没复现出客户的问题&#xff0c;然后我们又不停的在生产环境上找问题&#xff0c;也没复现出来 最…

一文带你读懂MLIR论文,理解MLIR设计准则.

论文MLIR: Scaling Compiler Infrastructure for Domain Specific Computation MLIR&#xff1a;针对特定领域计算扩展编译器基础设施 文章目录 论文MLIR: Scaling Compiler Infrastructure for Domain Specific Computation1. 论文下载2. TVM关于MLIR的讨论3. 论文正文0. 摘要…

5个人人都应该学会的电脑小技巧

今天分享几个电脑常用的快捷键&#xff0c;可以让你的工作事半功倍&#xff0c;建议收藏&#xff01; 不想被突然来的人看到正在浏览的网站&#xff0c;用CtrlW或者AltF4可以关闭当前页面&#xff0c;另外如果你正在看小电影也可以用这个办法关闭。 2. 同事或大Boos走了之后想…

Anthropic的Claude安卓版能否赢得用户青睐?

Anthropic的Claude安卓版能否赢得用户青睐&#xff1f; 前言 Anthropic 就在7月18日&#xff0c;这家以"可控AI"著称的初创公司再次出手&#xff0c;推出了Claude的Android版本应用。这款APP不仅支持实时语言翻译&#xff0c;更传承了Anthropic一贯的隐私保护政策。C…

腾讯云COS托管静态网站,以及如何解决访问出现了下载网页的情况

腾讯云对象存储&#xff08;Cloud Object Storage&#xff0c;简称COS&#xff09;&#xff0c;与其他云厂商所提供的云对象存储都是面向非结构化数据&#xff0c;只是每个云厂商的叫法有别于他家&#xff0c;或许是更能彰显厂商的品牌吧&#xff01; 但不管云厂商怎么给云对象…

错误:PHP:Deprecated: Required parameter $xxx follows optional parameter $yyy

前言 略 错误 Deprecated: Required parameter $xxx follows optional parameter $yyy 解决办法 设置 error_reporting E_ALL & ~E_DEPRECATED & ~E_STRICT 参考 https://blog.csdn.net/lxw1844912514/article/details/100028023

MATLAB学习日志DAY13

13.矩阵索引&#xff08;1&#xff09; 13.1 下标 上图&#xff01; A 的行 i 和列 j 中的元素通过 A(i,j) 表示。 例如&#xff0c;A(4,2) 表示第四行和第二列中的数字。 在幻方矩阵中&#xff0c; A(4,2) 为 15。 A(1,4) A(2,4) A(3,4) A(4,4) 用来计算 A 第四列中的…

【PG】PostgreSQL高可用之repmgr事件通知

目录 描述 结合脚本 占位符 repmgr命令 生成的事件&#xff1a; repmgrd 生成的事件&#xff08;流复制模式&#xff09;&#xff1a; 描述 每次repmgr或repmgrd执行重大事件时&#xff0c;都会将该事件的记录连同时间戳、失败或成功的标识以及进一步的详细信息&#xff08…

黑马商城启动流程(微服务拆分项目)

1.虚拟机ssh&#xff08;docker中布置了 mysql nacos seata&#xff08;分布式事务&#xff09;三个容器在同一个网络hm-net中&#xff09; 2.idea&#xff08;启动所有的微服务项目 &#xff09; 3.nginx 在cmd上启动 4.navicat 连接数据库 5.登录前端页面 http://localho…

Stable Diffusion AI入门介绍

Stable Diffusion模型 在上一篇的文章中我们介绍了&#xff0c;AIGC的相关知识以及AI绘画的历史——AIGC是什么&#xff0c;与AI绘画有什么关系&#xff0c;一篇文章带你了解AI绘画的前世今生。 我们知道了Stable Diffusion是一种潜在扩散模型&#xff0c;由慕尼黑大学的Comp…