驱动分离思想:
在传统的字符设备驱动思想中一个驱动程序对应一个硬件资源,在驱动入口函数中对资源进行配置,在file_operation中对各个硬件资源进行操作。这种思想使得内核中驱动代码变得庞大,为了节约内核空间驱动分离思想被提出,同时提高程序可移植性。
总线设备驱动模型包含的就是总线、设备、驱动三个部分。驱动分离是将驱动和设备代码进行分离。将模块拆分为device和driver。
在Linux内核中总线由bus_type结构体表示,在platform.c中有bus_type型platform_bus_type结构体。总线管理两个链表分别为platform_device和platform_driver链表用于管理设备和驱动。
struct bus_type:

struct bus_type platform_bus_type

platform_driver结构体以及结构体内重要元素
其中struct platform_ driver结构体如下:

在struct platform_driver中结构体*id_table表示该驱动能支持的设备,一个platform_driver可能支持一个或多个设备。

还有struct device_driver结构体,该结构体内通常将设备名等相关自由度较高的信息放置在该结构体中,该结构体如下:
platform_device结构体
struct platform_device结构体如下:

总线驱动模型原理详解
总线会管理两个链表,一个是驱动链表platform_driver,一个是设备链表platfoem_devie。
假设当前注册一个设备进入到设备链表中,注册完成之后就会与驱动链表中的驱动进行比较。根据platform_match函数源码

其中platform_device_id和函数platform_match_id如下,通过遍历比较驱动中的id_table中的各个结构体name和设备中的name看是否匹配


通常为了实现一个驱动和多个设备匹配,比较时采用platform_device中的override或者platform_driver中的id_tatble来实现:
platform_device中定义了driver_override字符串,比较优先级为driver_override > name。
1.首先是根据platform_device中driver_override这个字符串所表示的设备名与platform_driver中的device_driver中的设备名进行比较。
2.如果在platform_device中没有定义driver_override,则直接将platform_device中的name与platform_driver中的device_driver中的设备 名进行比较。
platform_device中未定义了driver_override字符串
1.platform_device中的name首先与platform_driver中结构体platform_device_id *id-table中的能支持的设备进行比较。
2.如果id_table为空或者各相与platform_device中的name不匹配则会与platform_driver中的device_driver driver中的name进行比较。

匹配成功就会调用驱动结构体platform_driver中的probe函数

可以看到probe函数的参数为设备结构体platform_device,在设备结构体中通常包含所有的资源的详细信息,因此总线设备驱动的一些对于硬件寄存器的相关操作通常在驱动结构体的probe函数指针所指函数中进行,该函数调用后通常会分配设置注册fileoperation结构体,并根据platform_device确定硬件相关寄存器使用ioreamp来映射寄存器等。反之,如果先对驱动进行注册,当驱动被注册进总线中的驱动链表中之后,就会对设备链表中的设备进行比较,后面的操作同上。
这样一来就实现了驱动和设备分离。将传统驱动框架中的file_operation中对于硬件寄存器的一些操作通过驱动结构体中probe函数指针指向的函数中进行probe函数将与之相匹配的设备结点作为参数。设备结构体中通常包含一些硬件资源相关的信息。
需要了解的是,当注册一个platform_drv之后就会与设备链表上的所有设备进行一一比较,但是当注册platform_driver时,通过__device_attach_driver去匹配驱动时,匹配成功之后就立即调用probe函数。

platform_device.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>static struct resource resources[] = {{.start = (3<<8)|(1),     //引脚信息GPIO3  pin1.flags = IORESOURCE_IRQ,},
};static void led_dev_release(struct device *dev)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
}static struct platform_device led_dev = {.name = "led",.num_resources = ARRAY_SIZE(resources),.resource = resources,.dev = {.release = led_dev_release,},
};static int __init led_dev_init(void)
{int err;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);err = platform_device_register(&led_dev);         //注册设备放入链表中return err;
}static void __exit led_dev_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);platform_device_unregister(&led_dev);
}module_init(led_dev_init);
module_exit(led_dev_exit);MODULE_LICENSE("GPL");

pllatform_driver.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>#include <linux/platform_device.h>#define LED_MAX_CNT 10struct led_desc {int pin;       //引脚int minor;     //次设备号
};static int major = 0;
static struct class *led_class;static int g_ledcnt = 0;
static struct led_desc leds_desc[LED_MAX_CNT];    //放置设备资源的结构体数组static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;char status;struct inode *inode = file_inode(file);int minor = iminor(inode);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_from_user(&status, buf, 1);//对寄存器进行设置,控制硬件,相关的硬件信息置于全局变量中,可以通过全局变量进行获取printk("set led pin 0x%x as %d\n", leds_desc[minor].pin, status);return 1;
}static int led_drv_open (struct inode *node, struct file *file)
{int minor = iminor(node);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);//初始化硬件寄存器(方向寄存器,置位寄存器,复位寄存器)printk("init led pin 0x%x as output\n", leds_desc[minor].pin);return 0;
}static struct file_operations led_drv = {.owner    = THIS_MODULE,.open    = led_drv_open,.write   = led_drv_write,
};//platform_driver 中的probe函数
static int led_probe(struct platform_device *pdev)
{   int minor;int i = 0;struct resource *res;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);     //匹配成功之后利用定义的全局变量获得平台设备资源信息,以便于系统调用时使用相关的硬件寄存器引脚信息。if (!res)return -EINVAL;//记录引脚minor = g_ledcnt;leds_desc[minor].pin = res->start;    //将参数中的platform_pdev得到的硬件资源赋值到设备数组中// 创建设备节点 device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor); platform_set_drvdata(pdev, &leds_desc[minor]);g_ledcnt++;return 0;
}static int led_remove(struct platform_device *pdev)
{struct led_desc *led = platform_get_drvdata(pdev);device_destroy(led_class, MKDEV(major, led->minor));return 0;
}static struct platform_driver led_driver = {.probe      = led_probe,.remove     = led_remove,.driver     = {.name   = "led",},
};static int __init led_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "100ask_led", &led_drv);       //注册驱动,也可以在probe函数中进行注册led_class = class_create(THIS_MODULE, "100ask_led_class");    //device_create在probe函数中,卸载设备放在platform_driver中的remove中err = PTR_ERR(led_class);if (IS_ERR(led_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "led");return -1;}err = platform_driver_register(&led_driver); return err;
}static void __exit led_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);platform_driver_unregister(&led_driver); class_destroy(led_class);unregister_chrdev(major, "100ask_led");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

learned from :韦东山

Linux驱动——驱动分离思想和总线设备驱动模型相关推荐

  1. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  2. 驱动进化之路:总线设备驱动模型

    文章目录 1 驱动编写的3种方法 1.1 传统写法 1.2 总线设备驱动模型 1.3 设备树 2 在 Linux 中实现"分离":Bus/Dev/Drv 模型 2.1 模型 2.2 ...

  3. Linux SPI总线设备驱动模型详解

    随着技术不断进步,系统的拓扑结构越来越复杂,对热插拔.跨平台移植性的要求越来越高,早期的内核难以满足这些要求,从linux2.6内核开始,引入了总线设备驱动模型.其实在linux2.4总线的概念就已经 ...

  4. linux驱动开发篇(三)—— 总线设备驱动模型

    linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...

  5. linux一个spi总线挂多个设备,Linux SPI总线设备驱动模型详解

    随着技术不断进步,系统的拓扑结构越来越复杂,对热插拔.跨平台移植性的要求越来越高,早期的内核难以满足这些要求,从linux2.6内核开始,引入了总线设备驱动模型.其实在linux2.4总线的概念就已经 ...

  6. Linux总线设备驱动框架的理解(非常棒的文章!)

    以下内容源于微信公众号:嵌入式企鹅圈.有格式内容上的修改,如有侵权,请告知删除. Linux的设备驱动框架,即某类设备对应的驱动的框架. 这里是"Linux总线设备驱动框架",应该 ...

  7. linux用户空间flash驱动,全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动...

    原标题:全面掌握Linux驱动框架--字符设备驱动.I2C驱动.总线设备驱动.NAND FLASH驱动 字符设备驱动 哈~ 这几天都在发图,通过这种方式,我们希望能帮大家梳理学过的知识,全局的掌握Li ...

  8. 一起分析Linux系统设计思想——05字符设备驱动框架剖析(四)

    在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习.我的使命就是过滤掉大量的垃圾信息,将知识体系化,以短平快的方式直 ...

  9. 设备驱动模型:总线-设备-驱动

    1 设备驱动模型简介 参考 以下内容: Linux 笔记: https://xuesong.blog.csdn.net/article/details/109522945?spm=1001.2014. ...

最新文章

  1. 有kotlin中的Android项目编译出现 Unresolved reference: R
  2. python搭建自动化测试平台_如何用python语言搭建自动化测试环境
  3. linux加密解密基础、PKI及SSL、创建私有CA
  4. 送ta一朵独一无二的玫瑰花
  5. php 档案,PHP 档案包 (PHAR)
  6. @JsonSerialize 与 @JsonDeserialize 使用
  7. javascript高级程序设计-Array迭代及归并
  8. 域名型通配符ssl证书_西部数码使用指南:申请了主域名SSL证书,是否还需要申请www域名的...
  9. pythonturtle哪个版本好,python3.7安装turtle(单纯为了好玩)
  10. python中response对象的属性_关于python:AttributeError:’HTTPResponse’对象没有属性’split’...
  11. .NET 环境中使用RabbitMQ(转)
  12. 悲剧,当用cywin 写Linux脚本
  13. android实现应用程序仅仅有在第一次启动时显示引导界面
  14. 计算机重启打印服务关闭,电脑打印机消失print spooler服务启动后自动停止
  15. 干货!闲鱼上哪些商品抢手?Python 分析后告诉你
  16. Excel 复制粘贴筛选出来的数据行
  17. gmx editconf命令
  18. python获取网页验证码cookie_python接口自动化(十三)--cookie绕过验证码登录(详解)(转载)...
  19. windows无法格式化u盘_如何解决u盘0字节无法格式化的问题
  20. SQL积累 计算相除之比+% ,转型,拼接, 多个左联,求和,统计,截取等

热门文章

  1. 由国内到国外:软件推广成功之路 让软件价值发挥到最大
  2. 如何下载房山区卫星地图高清版大图
  3. yolov5-5的train函数简单流程
  4. php注册登录代码初学,php 用户注册实例代码,适合初学者_PHP教程
  5. 哈夫曼编码和二进制编码_案例
  6. iOS 15 安装普及率接近 90%,iOS 16 将于下周公布
  7. 18 款低代码平台!开发解放双手!
  8. css高级技巧(精灵图、字体图标、三角、vertical-align属性、溢出文字省略号等)
  9. vs2010打开vs2017的.sln文件
  10. Linux运维简历模板技术储备(by ob)