platform驱动模型

2023-09-20 21:43:34

一、总线驱动模型

1.概念

        linux中将一个挂载在总线上的驱动的驱动模型分为三部分: devicedriverbus

        device是用来保存设备信息的对象,存放在内核中一个klist_device链表中进行管理。

        driver当前设备的驱动信息对象,存放在内核中一个klist_driver链表中进行管理。

        bus是当前设备挂载的总线的总线驱动

2.流程

        bus负责完成device和driver到的匹配,这一步通过总线驱动中的match函数来实现。当device和driver匹配成功后执行driver端的probe函数,在probe函数中完成驱动的注册、设备节点的创建、以及后续的硬件控制工作。

二、platform驱动模型

1.引入的原因

        为了让没有挂载在总线上的设备也能够按照总线驱动模型进行驱动的编写

2.platform总线驱动模型的部分

        设备端、驱动端、总线端

3.过程

        总线负责完成驱动和设备信息的匹配,当匹配成功之后会执行驱动端probe函数。在probe函数中实现驱动的注册、设备节点的创建以及后续的硬件控制工作

4.设备端API

1)分配设备信息对象并且初始化

        

2)注册设备信息对象

        int platform_device_register(struct platform_device *pdev)

3)注销设备对象

        void platform_device_unregister(struct platform_device *pdev)

5.驱动端API

三、名字表

1.概述

        为了能够让驱动更加适配,我们可以在驱动端构建一个名字表。只要设备的名字和名字表中的任何一个名字一样,都可以执行驱动端probe函数

2.名字表的构建

struct platform_device_id idtable[]= {

        {"aaaaa",0},

        {"bbbbb",1},

        {"ccccc",2},

         {"ddddd",3},

         { },                   //防止数组越界

}

四、设备树匹配

1.概述

        内核3.10版本以后要求将所有的设备信息都保存在设备树中,所有以后驱动端获取设备信息都在设备树中获取,所以需要使用驱动端的设备树匹配方式

2.设备树匹配匹配项的类型

        

3.准备

1)添加用于设备树匹配的设备树节点

2)构建用于设备树匹配的表

        struct of_device_id odtable[] = {

                { .compatible = "hqyj,myplatform", },

                { /* end node */ },       //防止数组越界

                };

五、任务

pdrv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include<linux/slab.h>
#include<linux/wait.h>
 
//定义一个等待队列头
wait_queue_head_t wq_head;
unsigned int condition=0;
unsigned int major;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
struct resource *res;
unsigned int irqno;
struct gpio_desc *gpiono;
unsigned int number = 0;
 
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
 
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    //判断IO方式
    if(file->f_flags&O_NONBLOCK)//非阻塞
    {}
    else//阻塞
    {
        wait_event_interruptible(wq_head,condition); //先检查condition再将进程休眠 
    }
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy_to_ user err\n");
        return -EIO;
    }
    condition=0;//下一次硬件数据没有就绪
 
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
 
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
 
struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};
 
// 定义中断处理函数
irqreturn_t key_handler(int irq, void *dev)
{
    if (number == 0)
    {
        number++;
        kbuf[0] = '1';
        // 灯亮
        gpiod_set_value(gpiono,1);
    }
    else if (number == 1)
    {
        number = 0;
        kbuf[0] = '0';
        // 灯灭
        gpiod_set_value(gpiono,0);
    }
    
    condition=1;//表示硬件数据就绪
    wake_up_interruptible(&wq_head);
 
    return IRQ_HANDLED;
}
 
// 封装probe函数
int pdri_probe(struct platform_device *pdev)
{
     //初始化等待队列
    init_waitqueue_head(&wq_head);
 
    // 1字符设备驱动注册
    major = register_chrdev(0, "myled", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);
 
    // 2向上提交目录
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
 
    // 3向上提交设备节点信息
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled0");
    if (IS_ERR(dev))
    {
        printk("向上提交设备节点信息失败\n");
        return -PTR_ERR(dev);
    }
 
    printk("向上提交设备节点信息成功\n");
 
    // 基于设备数节点信息获取gpio_desc对象指针
    gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led3-gpio", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono))
    {
        printk("解析GPIO管脚信息失败\n");
        return -ENXIO;
    }

    printk("解析GPIO管脚信息成功\n");
 
    // 获取中断类型的资源,中断号
    irqno = platform_get_irq(pdev, 0);
    if (irqno < 0)
    {
        printk("获取中断类型资源失败\n");
        return -ENXIO;
    }
    printk("key1_irq资源:%d\n", irqno);
 
    // 注册按键1中断
    int ret = request_irq(irqno, key_handler, IRQF_TRIGGER_FALLING, "key1_int", NULL);
    if (ret < 0)
    {
        printk("注册按键1中断失败\n");
        return ret;
    }
 
    printk("注册按键1中断成功\n");
 
    printk("%s-%s-%d\n", __FILE__, __func__, __LINE__);
 
    return 0;
}
// 封装remove函数
int pdri_remove(struct platform_device *pdev)
{
    // 注销中断
    free_irq(irqno, NULL);
 
    // 1销毁设备节点信息
    device_destroy(cls, MKDEV(major, 0));
 
    // 2销毁目录信息
    class_destroy(cls);
 
    // 3字符设备驱动注销
    unregister_chrdev(major, "myled");
 
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 构建设备树匹配表
struct of_device_id oftable[] = {
    {.compatible = "hqyj,myplatform"},
    {}, // 防止数组越界
};
 
// 定义驱动信息对象并初始化
struct platform_driver pdri = {
    .probe = pdri_probe,
    .remove = pdri_remove,
    .driver = {
        .name = "ccc",
        .of_match_table = oftable,
    },
};
 
module_platform_driver(pdri); // 一键注册宏
MODULE_LICENSE("GPL");
test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc,char const *argv[])
{
    char buf[128]={0};
    int fd = open("/dev/myled0",O_RDWR);
 
    if(fd < 0)
    {
        printf("设备文件打开失败\n");
        exit(-1);
    }
    while(1)
    {
        //读取number的值
        read(fd,buf,sizeof(buf));
        printf("number = %s\n",buf);
    }
 
    close(fd);
    
    return 0;  
}
测试现象:

更多推荐

Rust : 与C多种交互尝试

rust调用C端的库函数,有很多方法,场景也有所不同。包括windows还是linux,内置库还是自定义库,还是三方库等等。一、rust调用其内置的C库这个很简单,直接把extern"C"引入即可:比如,在rust端main.rs中:usestd::os::raw::c_int;//f32usestd::os::raw

什么是函数式编程(functional programming)?在JavaScript中如何实现函数式编程的概念?

聚沙成塔·每天进步一点点⭐专栏简介⭐函数式编程(FunctionalProgramming)⭐纯函数(PureFunctions)⭐不可变性(Immutability)⭐高阶函数(Higher-OrderFunctions)⭐函数组合(FunctionComposition)⭐声明式编程(DeclarativeProg

第15篇ESP32 idf框架 wifi联网_WiFi AP模式_手机连接到esp32开发板

第1篇:Arduino与ESP32开发板的安装方法第2篇:ESP32helloword第一个程序示范点亮板载LED第3篇:vscode搭建esp32arduino开发环境第4篇:vscode+platformio搭建esp32arduino开发环境​​​​​​第5篇:doit_esp32_devkit_v1使用pmw呼

AI AIgents时代-(三.)AutoGPT和AgentGPT

前两篇讲解了Agent的原理和组件,这节我将给大家介绍两个agent项目,给出它们的工作原理和区别,并教大家亲手尝试使用Agents🎉🟢AutoGPT🤖️我们的老朋友,之前文章也专门写过。AutoGPT是一个由ToranRichards创建的流行开源项目,github有接近15万星🌟。它利用GPT4作为大脑,结

运用贪心算法实现卡牌游戏-2023年全国青少年信息素养大赛Python复赛真题精选

[导读]:超平老师计划推出《全国青少年信息素养大赛Python编程真题解析》50讲,这是超平老师解读Python编程挑战赛真题系列的第18讲。全国青少年信息素养大赛(原全国青少年电子信息智能创新大赛)是“世界机器人大会青少年机器人设计与信息素养大赛”赛事之一,由中国电子学会主办,包含很多赛项,大赛自2013年举办,已连

WebGIS开发教程:切片地图服务和动态地图服务的区别以及加载方式

arcgis发布切片地图服务arcgis发布动态地图服务切⽚地图服务和动态地图服务是两种常⻅的Web地图服务类型,它们有以下⼏点区别:1.数据格式:切⽚地图服务是⼀种基于瓦⽚(Tile)数据的地图服务,地图数据被预先切成⼩块并存储在服务器上,⽤户在使⽤时只需加载需要的瓦⽚数据即可;⽽动态地图服务则是⼀种基于⽮量(Vec

面试(架构,网络)

java八股treemap和linkdedhashmap区别,实现原理https://blog.csdn.net/shidebin/article/details/126814905架构https://www.cnblogs.com/crazymakercircle/p/17197091.htmlhttps://www

免费和开源的机器翻译软件LibreTranslate

什么是LibreTranslate?LibreTranslate免费开源机器翻译API,完全自托管。与其他API不同,它不依赖于Google或Azure等专有提供商来执行翻译。它的翻译引擎由开源ArgosTranslate库提供支持。这个软件在2022年3月的时候折腾过,但当时容器会停止,从下面的文章看,应该可能是内存

常用与业务密切相关的prompt

可以在Bard、Bing、Claude2、ChatGPT和Llama2上使用定义您的业务目的和愿景。提示:“我正在[插入行业]创业。我的重点是定义与我的受众产生共鸣的明确目标和愿景。你能指导我制定有意义的愿景声明吗?”研究和分析您的目标市场。提示:“我想了解[插入行业]的目标市场。对他们的需求和行为的详细洞察将塑造我的

《确保安全:PostgreSQL安全配置与最佳实践》

🌷🍁博主猫头虎(🐅🐾)带您GotoNewWorld✨🍁🐅🐾猫头虎建议程序员必备技术栈一览表📖:🛠️全栈技术FullStack:📚MERN/MEAN/MEVNStack|🌐Jamstack|🌍GraphQL|🔁RESTfulAPI|⚡WebSockets|🔄CI/CD|🌐Git&Versio

Linux进程【1】进程概念(超详解哦)

进程概念引言(操作系统如何管理)基本概念描述与组织进程查看进程进程pid与ppidgetpid与getppid总结引言(操作系统如何管理)在冯诺依曼体系结构中,计算机由输入设备、输出设备、运算器、控制器和存储器组成。我们使用计算机的时候,实际就是数据在这些硬件中传递的过程。硬件的行为由驱动控制,驱动又由更上层的操作系统

热文推荐