Linux系统驱动(十六)platform驱动

news2024/9/22 15:36:21

文章目录

  • 一、简介
    • (一)device、bus、driver模型
    • (二)platform驱动原理
  • 二、platform总线驱动的API
    • (一)设备信息端
    • (二)设备驱动端
    • (三)驱动中获取设备信息函数
    • (四)idtable
  • 三、platform的设备树匹配方式
    • (一)填充匹配项
    • (二)platform设备树匹配实例
    • (三)使用示例

一、简介

(一)device、bus、driver模型

在Linux内核中所有总线驱动都遵从设备驱动的模型,总线驱动的模型如下图:
在这里插入图片描述

内核在设计这些总线驱动模型的时候将一个驱动分为了三个部分device、bus、driver。
device是用来描述硬件设备的,bus是总线用来链接device和driver,driver是用来描述驱动的对象。
在内核中所有的device放在内核的klist_devices的链表中管理,而内核中所有的driver放在klist_drivers中管理。内核中的device和driver通过bus完成关联。
当device和driver匹配成功之后执行驱动的probe函数,在probe函数中就可以完成操作硬件了。当卸载任何一方驱动的时候都会执行驱动中的remove函数。

(二)platform驱动原理

platform总线驱动遵从devices、bus、driver模型。platform总线驱动的思想就是要将设备信息和设备驱动进行分离。
platform是Linux内核抽象出来的软件代码,并没有真实的总线协议与之对应

platform_device和platform_driver通过总线匹配成功之后会执行驱动中probe函数,在probe函数中驱动就能够拿到设备信息。

二、platform总线驱动的API

(一)设备信息端

1.分配并初始化对象
    struct platform_device {
        const char *name; //用于匹配的名字
        int  id;        //总线号,PLATFORM_DEVID_AUTO
        struct device dev; //父类
        u32		num_resources;  //设备信息的个数
        struct resource *resource; //设备信息结构体指针
    };
 --------------------------------
    struct device {
        void (*release)(struct device *dev); //释放资源的函数
    };
 ---------------------------------
 //设备信息结构体
    struct resource {
        resource_size_t start; //资源的起始值
        //0x50006000           0x12345678           73
        resource_size_t end;   //资源的结束值
        //0x50006000+3         0x12345678+49        73
        unsigned long flags;   //资源类型
  //IORESOURCE_IO       IORESOURCE_MEM      IORESOURCE_IRQ
    };
3.注册
    int platform_device_register(struct platform_device *pdev)
4.注销
    void platform_device_unregister(struct platform_device *pdev)

(二)设备驱动端

1.分配并初始化对象
struct platform_driver {
    int (*probe)(struct platform_device *);
    //匹配成功执行的函数
    int (*remove)(struct platform_device *);
    //分离的时候执行的函数
    struct device_driver driver;
    //父类
    const struct platform_device_id *id_table;
    //2.idtable匹配方式
};
struct device_driver {
	const char  *name; //1.按照名字匹配
	const struct of_device_id *of_match_table; //3.设备树匹配
};
    
2.注册
  #define platform_driver_register(drv) \
 __platform_driver_register(drv, THIS_MODULE)
 
3.注销
 void platform_driver_unregister(struct platform_driver *);

4.一键注册注销的宏
#define module_platform_driver(pdrv) \
 module_driver(pdrv, platform_driver_register, \
   platform_driver_unregister)

#define module_driver(__driver, __register, __unregister, ...) \
    static int __init pdrv_init(void) 
    { 
        return platform_driver_register(&(pdrv)); 
    } 
    module_init(pdrv_init); 
    static void __exit pdrv_exit(void) 
    { 
        platform_driver_unregister(&(pdrv)); 
    } 
    module_exit(pdrv_exit);
  • 注:在注册过程去完成匹配,但是哪一方没有匹配上都不会报错,而是作为一个节点放在列表,等待对方匹配
  • 设备驱动端可以直接调用宏函数,完成入口和出口的指定,无需再单独写
  • 无论卸载pdev还是pdrv,remove函数都会执行

(三)驱动中获取设备信息函数


(四)idtable

在编写驱动时,有时一个驱动可以通过兼容多个设备,在驱动中就需要填写可以匹配的所有名字,将这个名字填写到一个表里,这个表就是idtable

匹配顺序是:设备树、idtable、name,依次进行匹配

当使用idtable进行匹配时,匹配上的id会保存在struct platform_device结构体中的id_entry字段中

struct platform_device_id {
	char name[PLATFORM_NAME_SIZE];
	kernel_ulong_t driver_data;
};
  • 注:
  • idtable中必须写一个空的{},表示结束,否则内核可能会越界访问
  • pdrv驱动运行时会以name创建文件夹,即使不以name匹配,但是name必须填充,否则可能出现空指针,导致内核崩溃
    在这里插入图片描述

三、platform的设备树匹配方式

platform_device结构体中的resource是用来描述设备信息的结构体,在linux-3.10版本之后
在linux内核启动时会根据设备树节点(compatible)自动创建platform_device结构体,并完成注册

(一)填充匹配项

struct of_device_id {
 char name[32];
 char type[32];
 char compatible[128];
 const void *data;
};

struct of_device_id oftable[] = {
    {.compatible = "hqyj,plat_node",},
    {} //必须填写一个空的括号,防止内存越界
};

struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "duang", //会以name创建文件夹,如果没有填写,会出现内核空指针
                         //虽然不按照name匹配,但name不能缺省
     .of_match_table = oftable,
    }};

(二)platform设备树匹配实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
struct resource* res;
int irqno;
int pdrv_probe(struct platform_device* pdev)
{
    //pdev->dev.of_node //设备树节点
    //platform_get_resouce的参数填写为IORESOURCE_MEM只能解析
    // 设备树中reg属性
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res == NULL) {
        pr_err("platform_get_resource error");
        return -ENODATA;
    }
    // platform_get_irq只能用来解析设备树中的中断属性
    irqno = platform_get_irq(pdev, 0);
    if (irqno < 0) {
        pr_err("platform_get_irq error");
        return irqno;
    }
    printk("addr=%#x,irqno=%d\n", res->start, irqno);

    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
int pdrv_remove(struct platform_device* pdev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
struct of_device_id oftable[] = {
    {.compatible = "hqyj,plat_node",},
    {}
};
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "duang duang duang", //会以name创建文件夹,如果没有填写,会出现内核空指针
                                   //虽然不按照name匹配,但name不能缺省
        .of_match_table = oftable
    },
};

module_platform_driver(pdrv);
MODULE_LICENSE("GPL
  • 注:与原有方式的差异,原有方式在没有设备树节点时会直接返回报错,无法安装驱动,但是基于platform的方式可以安装,当有驱动时可以立即执行
  • 不用自己去获取节点

(三)使用示例

功能需求
使用驱动代码实现如下要求:
a.应用程序通过阻塞的io模型来读取status变量的值
b.status是内核驱动中的一个变量,代表LED1的状态
c.status的值随着按键按下而改变(按键中断)
例如status=0 按下按键status=1 ,再次按下按键status=0
d.在按下按键的时候需要同时将led1的状态取反
e.驱动中需要编写字符设备驱动
f.驱动中需要自动创建设备节点
g.这个驱动需要的所有设备信息放在设备树的同一个节点中
h.通过platform驱动实现

需求分析

代码实现

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h> //设备树文件相关头文件
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>

#define CNAME "plat_led"
struct cdev *led_cdev;
struct class *led_class;
struct device *led_device;
int major = 0; //主设备号
int minor = 0;
dev_t led_dev_num;

int led_no;     //led的gpio号
int irqno;      //中断号

int status=0;   //LED灯状态

int kbuf[8]={0};

wait_queue_head_t wait_head; // 定义等待队列头
int condition = 0;

int led_open(struct inode *inode, struct file *file){
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t led_read(struct file *file, char __user *ubuf, size_t size, loff_t *offset){
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    if(file->f_flags & O_NONBLOCK){
        return -EINVAL;
    }
    ret = wait_event_interruptible(wait_head, condition);
        if (ret) {
            pr_err("interrupt by signal...\n");
            return ret;
    }
    if(size>sizeof(status)){
        size = status;
    }
    kbuf[0]=status;
    copy_to_user(ubuf,kbuf,sizeof(kbuf));
    // 将condition清零
    condition = 0;
    return 0;
}
int led_close(struct inode *inode, struct file *file){
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

struct file_operations ledfops={
    .open=led_open,
    .read=led_read,
    .release=led_close,
};

irqreturn_t key_irq_handler(int irqno, void *arg){
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    status=!status;
    gpio_set_value(led_no,status);
    
    condition=1;
    wake_up_interruptible(&wait_head);
    
    printk("key_irq_handler status=%d;condition=%d\n",status,condition);
    
    return IRQ_HANDLED;
}

int my_drv_probe(struct platform_device *dev){
    struct device_node *plat_node;
    int ret=0;
    /***gpio初始化***/
    //1. 获取节点
    plat_node = dev->dev.of_node;

    //2.获取gpio号
    led_no=of_get_named_gpio(plat_node,"led",0);
    if(led_no < 0){
            pr_err("of_get_named_gpio error");
            return led_no;
    }
    //3. 申请gpio
    ret=gpio_request(led_no,NULL);
    if(ret){
        pr_err("gpio_request error");
        goto err0;
    }
    //4.设置方向为输出
    ret = gpio_direction_output(led_no, 0);
    if (ret) {
        pr_err("gpio_direction_output error\n");
        goto err1;
    }
    printk("**************gpio init over\n");
    /***注册字符设备驱动***/
    //1. 分配对象
    led_cdev = cdev_alloc();
    if(NULL == led_cdev){ //成功返回结构体指针,失败返回NULL
        pr_err("cdv_err error");
        ret = -ENOMEM;
        goto err1;
    }
    //初始化对象:部分成员初始化
    cdev_init(led_cdev,&ledfops);
    //申请设备号:如果major为0,则动态申请,否则就静态指定
    if(major > 0){
        ret = register_chrdev_region(MKDEV(major,minor),1,CNAME);
        if (ret) {
            pr_err("register_chrdev_region error\n");
            goto err2;
        }
    }else if(major == 0){
        ret = alloc_chrdev_region(&led_dev_num,0,1,CNAME); 
        if (ret) {
            pr_err("alloc_chrdev_region error\n");
            goto err2;
        }
        major=MAJOR(led_dev_num);
        minor=MINOR(led_dev_num);
    }
    //注册
    ret = cdev_add(led_cdev,MKDEV(major,minor),1); 
    if (ret) {
        pr_err("cdev_add error\n");
        goto err3;
    }
    //自动创建设备节点
    led_class=class_create(THIS_MODULE,CNAME);
    if(IS_ERR(led_class)){
        pr_err("class_create error");
        ret = PTR_ERR(led_class);
        goto err4;
    }
    led_device = device_create(led_class,NULL,MKDEV(major,minor),NULL,CNAME);
     if(IS_ERR(led_device)){
        pr_err("device_create error");
        ret = PTR_ERR(led_device);
        goto err5;
    }
    printk("**************cdev init over\n");
    /***初始化中断***/
    //2.获取软中断号
    irqno=platform_get_irq(dev,0);
    if(irqno < 0){
        pr_err("platform_get_irq error");
        ret = irqno;
        goto err5;
    }
    //3.注册中断号
    ret = request_irq(irqno,key_irq_handler,IRQF_TRIGGER_FALLING,"my_IRQ_test",NULL);
    if(ret){
        pr_err("request_irq error");
        goto err5;
    }
    printk("**************irq init over\n");
    /***阻塞IO初始化***/
    //初始化等待队列头
    init_waitqueue_head(&wait_head);
    printk("**************wait_head init over\n");
    return 0;

err5:
    class_destroy(led_class);
err4:
    cdev_del(led_cdev);
err3:
    unregister_chrdev_region(MKDEV(major,minor),1);
err2:
    kfree(led_cdev);
err1:
    gpio_free(led_no);
err0:
    return ret;
}

int my_drv_remove(struct platform_device *dev){
    printk("my_drv_remove is run...\n");
    free_irq(irqno,NULL);

    device_destroy(led_class, MKDEV(major, 0));
    class_destroy(led_class);

    unregister_chrdev(major,"mypdrv");
    gpio_free(led_no);
    return 0;
}
//设备树匹配
struct of_device_id	my_of_match[]={
    {.compatible="zyx,myplatform"},
    {},
};
struct platform_driver myplatform={
    .probe=my_drv_probe,
    .remove=my_drv_remove,
    .driver={
        .name="platformName",
        .of_match_table=my_of_match,
    },
};

module_platform_driver(myplatform);
MODULE_LICENSE("GPL");

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

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

相关文章

【子豪兄】精讲CS231N斯坦福计算机视觉公开课学习笔记

wx 搜索 gzh 司南锤&#xff0c;回复 子豪兄231笔记 可获取笔记文件&#xff08;pdfdoc) 文章目录 学习链接:- 斯坦福大学公开课- 计算机视觉发展历史- 图像分类算法- 线性分类、损失函数与梯度下降- 神经网络与反向传播- 卷积神经网络- 可视化卷积神经网络- 训练神经网络 &…

关于WMS仓储管理系统的开发与应用

在当今这个全球化与数字化交织并进的时代&#xff0c;高效的仓储管理已经跃升为企业运营效能与市场竞争力提升的关键驱动力。仓库&#xff0c;这一供应链体系中的核心枢纽&#xff0c;其角色已远远超越了简单的货物存储功能&#xff0c;而是成为了推动货物流转效率与精准度的战…

软件测试之面试常见问题大全

软件测试之常见软件测试面试题 面试题解读&#xff0c;轻轻松松过面试&#xff0c;我以一个过来人的身份&#xff0c;写下这篇面试常见问题 1. 最常见的就是&#xff0c;为什么想进本公司&#xff0c;你了解本公司的业务吗&#xff1f; 再回答这个问题的时候是灵活的&#x…

DHCP学习笔记

1.DHCP快速配置 1.1配置接口IP R1: sysname R1 undo info-center enable interface Ethernet0/0/0 ip address 192.168.1.1 255.255.255.0 quit 1.2开启DHCP服务&#xff0c;接着在R1的e0/0/0配置DHCP Server功能 dhcp enable #全局下开启DHCP服务 interface Ethernet…

深度强化学习,模型改进

深度强化学习&#xff1a;DQN、PPO、DDPG、A3C、TD3、SAC、Rainbow、MADDPG、模仿学习&#xff0c;提供创新点&#xff0c;实验对比&#xff0c;代文章、润色 代码不收敛 菲涅尔模型 python深度学习算法模型定制

事务消息使用及方案选型思考

1. 事务消息概念与重要性 1.1 分布式系统中的事务问题 在分布式系统中&#xff0c;事务的一致性是一个核心问题。以电商登录送积分活动为例&#xff0c;用户登录成功后&#xff0c;系统需要执行两个关键操作&#xff1a;记录登录日志和发放积分。这两个操作需要保持一致性&am…

高性能的 C++ Web 开发框架 CPPCMS + WebSocket 模拟实现聊天与文件传输案例。

1. 项目结构 2. config.json {"service": {"api": "http","port": 8080,"ip": "0.0.0.0"},"http": {"script": "","static": "/static"} }3. CMakeLists.txt…

html+css+js网页设计 电商模版4个页面

htmlcssjs网页设计 电商模版4个页面 带js 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&…

我把AI洗脑了!看了潘展乐、全红婵的采访,我才知道:远离人情世故,就是远离内耗——早读(逆天打工人爬取热门微信文章解读)

想无 引言Python 代码第一篇 洞见看了潘展乐、全红婵的采访&#xff0c;我才知道&#xff1a;远离人情世故&#xff0c;就是远离内耗第二篇 讲个鬼故事结尾 引言 昨天晚上把干货赶出来了 也就是昆虫小达人 大家可以看看 大概率能够让你心理上克服昆虫恐惧 今天没什么重点得事…

一、软件工程概述

软件工程概述 1. 软件的概念和特点2. 软件危机的产生3. 软件工程的概念和发展过程4. 软件工程知识体系与职业道德 1. 软件的概念和特点 软件定义 软件程序数据文档。 软件生存周期 问题定义&#xff1a;要解决的问题是什么&#xff1f;可行性分析&#xff1a;对于上阶段所确定…

智慧农业新篇章:实时监测与精细化管理

智慧农业&#xff0c;作为现代农业技术革命的先锋&#xff0c;正引领着一场深刻的产业变革。它通过融合物联网、大数据、人工智能等前沿科技&#xff0c;实现了对农业生产环境的实时监测与精细化管理&#xff0c;开启了农业发展的新篇章。这一转型不仅提升了农产品的质量与产量…

云仓酒庄湖南团队启航新征程:筑基新质生产力,深耕啤酒屋市场

原标题&#xff1a;云仓酒庄湖南团队启航新征程&#xff1a;筑基新质生产力&#xff0c;深耕啤酒屋市场 在当前经济转型升级的关键期&#xff0c;新质生产力的崛起正成为推动行业发展的重要引擎。云仓酒庄湖南团队积极响应市场变革&#xff0c;率先启动基础培训项目&#xff0…

基于paddlehub 未戴口罩检测算法

一、简介 以前大夏天戴着口罩别人觉得你不正常&#xff0c;现在上街不戴口罩你不正常。 本文要讲的未戴口罩算法是基于paddlehub提供的模型&#xff0c;paddlehub是百度飞浆(PaddlePaddle)下的深度学习模型开发工具。 PaddleHub旨在为开发者提供丰富的、高质量的、直接可用的…

beautifulsoup的简单使用

文章目录 beautifulsoup一. beautifulsoup的简单使用1、安装2、如何使用3、对象的种类 二、beautifulsoup的遍历文档树2.1 子节点.contents 和 .children descendants2.2 节点内容.string.text 2.3 多个内容.strings**.stripped_strings** 2.4 父节点.parent.parents 三、beaut…

AD如何在封装制作时添加禁止铺铜区域?

在PCB封装库中&#xff0c;选择“Top Layer”层执行菜单命令“放置→多边形铺铜挖空”&#xff0c; 然后画好所需要的挖空的区域即可&#xff0c;如果是设计完PCB之后才来进行铺铜挖空的&#xff0c;可以在添加完铺铜挖空之后选中器件右击点选“Update PCB With All”&#x…

PyFluent入门之旅(10)Fluent Python Console

之前的文章中都在介绍如何在Fluent外部环境使用PyFluent&#xff0c;那么是否有可能在Fluent内部使用PyFluent呢&#xff1f; 自Ansys 2023 R1开始&#xff0c;Ansys Fluent的内置控制台支持Python命令&#xff0c;这使在Fluent内部控制台使用PyFluent成为了可能。 准备工作 …

【SpringBoot】9 定时任务(Quartz)

介绍 实现方式 java定时任务调度的实现方式&#xff1a;Timer&#xff0c;ScheduledExecutor&#xff0c;Spring Scheduler&#xff0c;JCron Tab&#xff0c;Quartz 等。 Quartz Quartz是一个由Java开发的开源项目&#xff0c;它可以与J2EE、J2SE应用程序相结合也可以单独…

直播预约|8月14日,无人系统开发阶段故障注入与安全测试详解

电机失效故障硬件在环仿真 01 培训背景 卓翼飞思实验室暑期公益培训(第六期)将于8月14日&#xff0c;19:00开启&#xff01;通过【飞思实验室】视频号线上直播&#xff0c;由中南大学计算机学院特聘副教授&#xff0c;RflySim平台总研发负责人戴训华副教授主讲。 第六期培训将…

Navicat Premium15 下载与安装(免费版)以及链接SqlServer数据库

转自:https://blog.csdn.net/m0_75188141/article/details/139842565

Hi910X 系列恒压恒流 BUCK 控制器

1. 产品介绍 Hi910X 是一系列外围电路简洁的宽输入电压降压 BUCK 恒压恒流驱动器&#xff0c;适用于 8-150V 输入电压范围的 DCDC 降压应用。Hi9100、Hi9101、Hi9102、Hi9103智芯半导体降压恒压系列 Hi910X 采用我司专利算法&#xff0c;实现高精度的降压恒压恒流。支持输出…