当前位置: 首页 > news >正文

Linux总线设备驱动模型深度理解

文章目录

    • 1)linux内核驱动的演变
    • 2)总线设备驱动模型
      • 1、总线概念
        • 1)物理上的总线概念
        • 2)内核驱动中的总线的概念
        • 3)TV设备/sys/bus/下的总线列表
        • 4)bus数据结构
      • 2、device数据数据结构
      • 3、device_driver数据结构
      • 4、深化device和device_driver的理解
    • 3)class子系统
    • 4)各个环节的整合:一个pci设备的创建过程
    • 5)platform虚拟设备驱动模型
      • 1、基本数据结构
      • 2、内核实例
      • 3、platform bus的注册
      • 4、platform_driver_register
      • 5、platform_device_register

1)linux内核驱动的演变

在这里插入图片描述
1、理清驱动的演变和分类十分重要,否则很容易就迷失在内核世界里,并且不同阶段的命名也很令人混淆;
2、接下来看总线设备驱动模型和各类子系统的实现细节
1)子系统以platform设备驱动为例子,platform可以理解为通用简单的总线设备驱动的实现之一,其它子系统各有各“特性”,但本质思想是一样的;

2)总线设备驱动模型

1、何为模型?一种软件抽象,软件框架

2、总线设备驱动 的含义?顾名思义此软件框架 由三部分组成:总线、设备、驱动,下面来一一讲解

3、总线的设计目的:
1)旨在规范各类设备的接口(树状、拓扑结构),方便系统统一管理日益剧增的设备,在Linux统一的设备模型机制支持下,bus可以有效地组织同类型的设备驱动;
2)挂接同类型的device/driver,并进行匹配工作;

4、设备device / 驱动driver又是什么?
device和driver是应用了软件分层思想,目的是为了复用driver(居多)和device,看图,以平台设备驱动为例
在这里插入图片描述
可以看出T950/T962/T972平台可以复用一套platform_driver驱动代码

5、与sysfs的关系?从设备模型开始,均需要依赖sysfs的实现,即linux 2.6后内核驱动均需使用sysfs在/sys下创建目录文件

1、总线概念

1)物理上的总线概念

物理上的总线一般分为两种:

1、处理器与各个控制器之间的连线
在这里插入图片描述

2、控制器和外设之间的物理通信通道总线,比如SPI总线
在这里插入图片描述

2)内核驱动中的总线的概念

物理上的总线是一堆电路线路连线(比如I2C总线、SPI总线、USB总线等等),软件上用来描述控制器与外设的连接关系,内核使用Bus结构体来描述这种连接关系,本质上总线Bus只是一个数据结构,它的名字用对应通信接口命名罢了,而一些驱动程序没有实际对应的通信接口(比如GPIO),那就用platform来命名吧,我们冠名为虚拟的总线!

3)TV设备/sys/bus/下的总线列表

在这里插入图片描述

4)bus数据结构
/android/kernel/fusion/4.19/drivers/base/bus.cbus_register(struct bus_type *bus)
--kobject_set_name()
--kset_register()
--bus_create_file()
--kset_create_and_add("devices")
--kset_create_and_add("drivers")
--add_probe_files(bus)什么时候需要新增总线?很少!总线一般都定义好/android/kernel/fusion/4.19/include/linux/device.h
struct bus_type {const char *name;const char *dev_name;struct device *dev_root;const struct attribute_group **bus_groups;const struct attribute_group **dev_groups;const struct attribute_group **drv_groups;int (*match)(struct device *dev, struct device_driver *drv);int (*uevent)(struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev);const struct dev_pm_ops *pm;const struct iommu_ops *iommu_ops;
}1、
match 当一个总线上的新设备或新驱动程序被添加时,就是调用match遍历总线上所有设备和驱动,匹配成功返回NULL
2、device iterator.
int bus_for_each_dev(){klist_iter_init_node(); //链表操作
}
3、driver iterator
int bus_for_each_drv(){klist_iter_init_node(); //链表操作
}创建一个bus
1、属性
struct bus_attribute {struct attribute	attr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
2、创建文件
bus_create_file()
bus_remove_file()

2、device数据数据结构

对应一个实际的设备
/android/kernel/fusion/4.19/include/linux/device.h
struct device { struct device *parent;struct device_private *p;struct kobject kobj;const char *init_name;const struct device_type *type;struct bus_type *bus;struct device_driver *driver;struct device_node	*of_node; //设备树节点void	(*release)(struct device *dev);
}1、
struct device_attribute {struct attribute	attr;ssize_t (*show)(struct device *dev, struct device_attribute *attr,char *buf);ssize_t (*store)(struct device *dev, struct device_attribute *attr,const char *buf, size_t count);
};
2、
device_create_file3、
int device_register(struct device *dev)
--device_initialize(dev) //初始化各个成员
--device_add(dev)
----dev_set_name()
----kobject_add()
----device_create_file()
----device_add_class_symlinks()
----device_add_attrs()
----bus_add_device()
----dpm_sysfs_add()
----device_pm_add()device结构(包含了设备模型核心)的嵌入,一般使用中不会单独使用,嵌入到高层之中
比如:usb_device、pci_device、platform_driver

3、device_driver数据结构

对应一个实际的驱动
/android/kernel/fusion/4.19/include/linux/device.h
struct device_driver {const char		*name;struct bus_type		*bus;struct module		*owner;const char		*mod_name;enum probe_type probe_type;const struct of_device_id	*of_match_table;const struct acpi_device_id	*acpi_match_table;int (*probe) (struct device *dev);const struct attribute_group **groups;const struct dev_pm_ops *pm;struct driver_private *p;
}1、
struct driver_attribute {struct attribute attr;ssize_t (*show)(struct device_driver *driver, char *buf);ssize_t (*store)(struct device_driver *driver, const char *buf,size_t count);
};2、创建
driver_create_file()3、注册
int driver_register(struct device_driver *drv)
--bus_add_driver()
--driver_add_groups()
--kobject_uevent()device_driver与device结构一样,一般使用中不会单独使用,嵌入到高层之中
比如platform_driver

4、深化device和device_driver的理解

1)理解device和device_driver
device: 描述硬件规格-相当于struct
device_driver: 软件接口-相当于函数接口

2)有driver就一定要有device吗?
从bus、device、device_driver的模型来看,device、device_driverr必须同时存在且匹配才能初始化;

3)为什么现代Linux驱动中只见device_driver,device在哪里?
1、现代系统基本都使用设备树,device由硬件厂商或系统集成方通过设备树描述提供,系统启动时自动将设备树转化为device_node,最终生成device并注册到对应总线,我们无需理会,只需配置设备树!
2、开发中如果没有设备树支持,这时才需要手动注册!

3)class子系统

1、class子系统的定义:类是一个设备的高层视图,它抽象出了底层的实现细节,几乎所有的类都实现在/sys/class下,历史原因/sys/block例外,多数情况下,类子系统是向用户空间导出信息的好方法 -> 抽象,使其更加方便易用struct class {const char		*name;struct module		*owner;const struct attribute_group	**class_groups;const struct attribute_group	**dev_groups;struct kobject			*dev_kobj;int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
}2、
struct class_attribute {struct attribute attr;ssize_t (*show)(struct class *class, struct class_attribute *attr,char *buf);ssize_t (*store)(struct class *class, struct class_attribute *attr,const char *buf, size_t count);
};3、
class_create_file()
class_remove_file()4、
class_register(class)
--__class_register()
----kobject_set_name()
----kset_register()
----class_add_groups()5、创建class类
class_create(THIS_MODULE, DEV_NAME);

4)各个环节的整合:一个pci设备的创建过程

在这里插入图片描述
图片来自linux设备驱动程序(第三版),内容可能过老,注意分辨

/android/kernel/fusion/4.19/kernel/umh.c
umh : user mode helper
启动用户层程序
int call_usermodehelper(const char *path, char **argv, char **envp, int wait)
{struct subprocess_info *info;gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;info = call_usermodehelper_setup(path, argv, envp, gfp_mask,NULL, NULL, NULL);if (info == NULL)return -ENOMEM;return call_usermodehelper_exec(info, wait);
}

5)platform虚拟设备驱动模型

一图掌握Linux platform平台设备驱动框架:

https://blog.csdn.net/qq_16504163/article/details/118562670

1、概述:

1)现在大多数驱动子系统,都基于基本的bus、device、device_driver结构,进一步封装的结果;

2)什么时候使用platform虚拟总线?与真实存在的 USB总线/I2C总线/SPI总线差异?

相当于MISC类,比如有些外设不依附物理总线,挂接在soc内存空间的外设,就可以使用platform设备驱动

2、platform平台设备驱动 与 其它子系统(I2C总线/SPI总线)的关联?

属于同一层级,并立关系,当然也可以嵌套使用,现在驱动也是如此操作,多个子系统完成一个驱动

1、基本数据结构

1、resource
/android/kernel/fusion/4.19/include/linux/ioport.h
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;unsigned long desc;struct resource *parent, *sibling, *child;
}2、platform_device
/android/kernel/fusion/4.19/include/linux/platform_device.h
struct platform_device {const char  *name;         //设备名称int     id;bool        id_auto;struct device   dev;       // 真正的设备,嵌入在platform_device中u32     num_resources;     // 设备资源数量struct resource *resource; //
};3、platform_driver
struct platform_driver {int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);struct device_driver driver;const struct platform_device_id *id_table;bool prevent_deferred_probe;
};4、platform_bus_type
extern struct bus_type platform_bus_type;5、有用的宏
内核封装一些宏,方便定义module的入口函数和出口函数
#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

2、内核实例

/android/kernel/fusion/4.19/drivers/mfd/sm501.c
/android/kernel/fusion/4.19/drivers/gpio/gpio-omap.c1、内核driver的例子一般都是综合的,糅杂多种子系统!
2、在linux代码中,如何找到platform_device对应的代码或设备树? - 根据name来找!/* use platform_driver for this. */
static struct platform_driver omap_mpuio_driver = {.driver		= {.name	= "mpuio",.pm	= &omap_mpuio_dev_pm_ops,},
};static struct platform_device omap_mpuio_device = {.name		= "mpuio",.id		= -1,.dev = {.driver = &omap_mpuio_driver.driver,}/* could list the /proc/iomem resources */
};static inline void omap_mpuio_init(struct gpio_bank *bank)
{platform_set_drvdata(&omap_mpuio_device, bank);if (platform_driver_register(&omap_mpuio_driver) == 0)(void) platform_device_register(&omap_mpuio_device);
}为什么不见platform bus?系统初始化时就准备好了!详见下文

3、platform bus的注册

/android/kernel/fusion/4.19/drivers/base/platform.c
platform_bus_init()在哪里被调用?系统启动时调用创建Platform总线,后面等设备/驱动加进来即可
/android/kernel/fusion/4.19/init/main.c
start_kernel()
--rest_init()
----kernel_thread(kernel_init, NULL, CLONE_FS);
------kernel_init_freeable()
-------do_basic_setup()
--------/android/kernel/fusion/4.19/drivers/base/init.c
--------driver_init()
----------platform_bus_init()
------------bus_register(&platform_bus_type)
--------------kset_create_and_add("devices", NULL, &priv->subsys.kobj);
--------------kset_create_and_add("drivers", NULL, &priv->subsys.kobj);

创建的文件如下
在这里插入图片描述

4、platform_driver_register

1、注册平台驱动(以gpio-iop.c为例)
/android/kernel/fusion/4.19/drivers/gpio/gpio-iop.c
static struct platform_driver iop3xx_gpio_driver = {.driver = {.name = "gpio-iop",}.probe = iop3xx_gpio_probe,
};
static int __init iop3xx_gpio_init(void)
{return platform_driver_register(&iop3xx_gpio_driver);
}2、做了什么?
/android/kernel/fusion/4.19/drivers/base/platform.c
#define platform_driver_register(drv) __platform_driver_register(drv, THIS_MODULE)
__platform_register_drivers(struct platform_driver * const *drivers, ...)
--__platform_driver_register()
----driver_register(&drv->driver); //最终还是调用driver_register/android/kernel/fusion/4.19/drivers/base/driver.c
driver_register(struct device_driver *drv)
--bus_add_driver(drv)
----driver_attach(drv);
------/android/kernel/fusion/4.19/drivers/base/dd.c --- device /driver interactions
------bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) 
--------driver_match_device(drv, dev);
1)match driver 和 device
/android/kernel/fusion/4.19/drivers/base/base.h
static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
即调用这里的platform_bus_type中的match成员
struct bus_type platform_bus_type = {.name		= "platform",.dev_groups	= platform_dev_groups,.match		= platform_match,.uevent		= platform_uevent,.dma_configure	= platform_dma_configure,.pm		= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);platform_match的实现
--of_driver_match_device(dev, drv) //当platform使用设备树时,走这里
----of_match_device(drv->of_match_table, dev)
------of_match_node(drv->of_match_table, dev->of_node) //比较drv和dev成员
--------__of_match_node(matches, node)
--acpi_driver_match_device(dev, drv) //需要填充drv->acpi_match_table
--platform_match_id(pdrv->id_table, pdev) //如果填充了id_table(代表支持一个或多个设备名)则按优先级一次匹配
----while (platform_device_id->name[0]){strcmp(pdev->name, id->name); pdev->id_entry = id}
--strcmp(pdev->name, drv->name) //直接对比名称,优先级最低2)假如match成功,随即调用bus / driver的probe函数,注意platform_device没有probe函数,因为dev都是静态代码
/android/kernel/fusion/4.19/drivers/base/dd.c
--------driver_probe_device(drv,dev)
----------really_probe(dev, drv);
------------dev->bus->probe(dev);
------------drv->probe(dev);

5、platform_device_register

1、怎么定义和注册?
2、必须要有device和driver 才会触发driver的probe? 是的注意platform_device没有probe函数,因为dev都是在描述资源,构造结构体,没有实际的动作执行代码int platform_device_register(struct platform_device *pdev)
{device_initialize(&pdev->dev);arch_setup_pdev_archdata(pdev);return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);
http://www.xdnf.cn/news/1351711.html

相关文章:

  • Vue3 学习教程,从入门到精通,基于 Vue 3 + Element Plus + ECharts + JavaScript的51购商城项目(45)
  • imx6ull-驱动开发篇37——Linux MISC 驱动实验
  • 大模型四种常见安全问题与攻击案例
  • MySQL数据库管理与索引优化全攻略
  • 力扣(全排列)
  • 使用 PSRP 通过 SSH 建立 WinRM 隧道
  • Linux-常用文件IO函数
  • jQuery 知识点复习总览
  • (nice!!!)(LeetCode 面试经典 150 题) 173. 二叉搜索树迭代器 (栈)
  • 55 C++ 现代C++编程艺术4-元编程
  • 数据结构与算法-字符串、数组和广义表(String Array List)
  • 【Tech Arch】Apache Flume海量日志采集的高速公路
  • 解码LLM量化:深入剖析最常见8位与4位核心算法
  • Mac相册重复照片终结指南:技术流清理方案
  • chromadb使用hugging face模型时利用镜像网站下载注意事项
  • Node.js特训专栏-实战进阶:23. CI/CD流程搭建
  • 通过官方文档详解Ultralytics YOLO 开源工程-熟练使用 YOLO11实现分割、分类、旋转框检测和姿势估计(附测试代码)
  • 优先使用 `delete` 关键字删除函数,而不是将函数声明为 `private` 但不实现 (Effective Modern C++ 条款11)
  • 2025年Java在中国开发语言排名分析报告
  • 深度学习之PyTorch框架(安装,手写数字识别)
  • Redis 从入门到实践:Python操作指南与核心概念解析
  • Redis全面详解:从配置入门到实战应用
  • 联邦学习之----联邦批量归一化(FedBN)
  • 非线性规划学习笔记
  • 【KO】前端面试题一
  • 浮点数比较的致命陷阱与正确解法(精度问题)
  • 【Linux】深度学习Linux下的包管理器yum/apt
  • 自动化知识工作AI代理的工程与产品实现
  • 文献阅读笔记【物理信息神经网络】:Physics-informed neural networks: A deep learning framework...
  • 深入理解 Linux 系统文件 I/O:从 open 到重定向的底层逻辑》