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

Linux 驱动设备匹配过程

一、Linux 驱动-总线-设备模型

1、驱动分层        

        Linux内核需要兼容多个平台,不同平台的寄存器设计不同导致操作方法不同,故内核提出分层思想,抽象出与硬件无关的软件层作为核心层来管理下层驱动,各厂商根据自己的硬件编写驱动代码作为硬件驱动层

2、设备&总线&驱动

        Linux内核建立的 设备-总线-驱动 模型,定义如下:

1、device
include\linux\device.h
struct device {...struct bus_type	*bus;		  /* type of bus device is on */struct device_driver *driver; /* which driver has allocated this device */struct device_node	*of_node; /* associated device tree node */...
}2、driver
include\linux\device\driver.h
struct device_driver {...struct bus_type		*bus;...
}3、bus
include\linux\bus\bus.h
struct bus_type {...int (*match)(struct device *dev, struct device_driver *drv);int (*probe)(struct device *dev);...
}

        这里提到的是虚拟总线,总线能将对应的设备和驱动进行匹配,可以用下面的命令查看不同总线类型

/sys/bus # ls -l 
......
drwxr-xr-x 4 root root 0 2023-02-21 13:35 i2c
drwxr-xr-x 4 root root 0 2023-02-21 13:35 mmc
drwxr-xr-x 5 root root 0 2023-02-21 13:35 pci
drwxr-xr-x 4 root root 0 2023-02-20 07:09 platform
drwxr-xr-x 4 root root 0 2023-02-21 13:35 scsi
drwxr-xr-x 4 root root 0 2023-02-21 13:35 usb
......
总线类型描述
I2C总线挂在i2c总线(硬件)下的从设备,比如加密芯片、rtc芯片、触摸屏芯片等等都需要驱动,自然也要按照分离思想来设计。内核中的i2c 总线就是用来帮助i2c从设备的设备信息和驱动互相匹配的
Platform总线

像i2c、spi这样硬件有实体总线的,从设备驱动可以用总线来管理。那么没有总线的硬件外设怎么办?比如gpio、uart、i2c控制器、spi 控制器…等等,这些通通用 platform 总线来管理

二、驱动匹配设备过程简述

        在写驱动时会用到一些注册函数比如:platform_driver_register,spi_register_driver、i2c_add_driver,接下来分析内核驱动和设备匹配的流程,原理就是在注册到总线的时候,去获取对方的链表并根据规则检测,匹配后调用probe(),也就是驱动的入口函数

        以Platform Driver举例,整个匹配过程如下

2.1 整体调用逻辑

module_platform_driver|-- module_driver|-- __platform_driver_register|-- driver_register|-- bus_add_driver|-- driver_attach|-- bus_for_each_dev|-- __driver_attach|-- driver_match_device|-- platform_match|-- of_driver_match_device|-- of_match_device|-- __of_match_node|-- driver_probe_device|-- really_probe|-- call_driver_probe|-- platform_probe|-- drv->probe()

2.2 module_platform_driver

        封装了一层,展开后实际上就是module_init和module_exit

/* module_platform_driver() - Helper macro for drivers that don't do* anything special in module init/exit.  This eliminates a lot of* boilerplate.  Each module may only use this macro once, and* calling it replaces module_init() and module_exit()*/
#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)

        例如对于MTK某平台UFS驱动,传入__platform_driver 参数为

static struct platform_driver ufs_mtk_pltform = {.probe      = ufs_mtk_probe,.remove     = ufs_mtk_remove,.shutdown   = ufshcd_pltfrm_shutdown,.driver = {.name   = "ufshcd-mtk",.pm     = &ufs_mtk_pm_ops,.of_match_table = ufs_mtk_of_match,},
};

2.3 module_driver

/*** module_driver() - Helper macro for drivers that don't do anything* special in module init/exit. This eliminates a lot of boilerplate.* Each module may only use this macro once, and calling it replaces* module_init() and module_exit().** @__driver: driver name* @__register: register function for this driver type* @__unregister: unregister function for this driver type* @...: Additional arguments to be passed to __register and __unregister.** Use this macro to construct bus specific macros for registering* drivers, and do not use it on its own.*/
#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.4 __platform_driver_register

        注意此处的__register是传进来的__platform_driver_register

/*** __platform_driver_register - register a driver for platform-level devices* @drv: platform driver structure* @owner: owning module/driver*/
int __platform_driver_register(struct platform_driver *drv,struct module *owner)
{drv->driver.owner = owner;drv->driver.bus = &platform_bus_type;return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);

        对bus参数进行赋值 

struct bus_type platform_bus_type = {.name		= "platform",.dev_groups	= platform_dev_groups,.match	= platform_match,.uevent	= platform_uevent,.probe	= platform_probe,.remove	= platform_remove,.shutdown	= platform_shutdown,.dma_configure= platform_dma_configure,.dma_cleanup= platform_dma_cleanup,.pm	= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

2.5 driver_register

/*** driver_register - register driver with bus* @drv: driver to register** We pass off most of the work to the bus_add_driver() call,* since most of the things we have to do deal with the bus* structures.*/
int driver_register(struct device_driver *drv)
{......other = driver_find(drv->name, drv->bus);if (other) {pr_err("Error: Driver '%s' is already registered, ""aborting...\n", drv->name);return -EBUSY;}ret = bus_add_driver(drv);......
}
EXPORT_SYMBOL_GPL(driver_register);

2.6 bus_add_driver

        drv->bus->p->drivers_autoprobe默认是1,结构体定义时就赋值了

struct subsys_private {...unsigned int drivers_autoprobe:1;    
}
/*** bus_add_driver - Add a driver to the bus.* @drv: driver.*/
int bus_add_driver(struct device_driver *drv)
{......if (drv->bus->p->drivers_autoprobe) {error = driver_attach(drv);if (error)goto out_del_list;}......
}

2.7 driver_attach

/*** driver_attach - try to bind driver to devices.* @drv: driver.** Walk the list of devices that the bus has on it and try to* match the driver with each one.  If driver_probe_device()* returns 0 and the @dev->driver is set, we've found a* compatible pair.*/
int driver_attach(struct device_driver *drv)
{return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);

2.8 bus_for_each_dev

        此函数 fn 即为  __driver_attach 函数指针,data参数 是 drv

int bus_for_each_dev(struct bus_type *bus, struct device *start,void *data, int (*fn)(struct device *, void *))
{struct klist_iter i;struct device *dev;int error = 0;if (!bus || !bus->p)return -EINVAL;klist_iter_init_node(&bus->p->klist_devices, &i,(start ? &start->p->knode_bus : NULL));while (!error && (dev = next_device(&i)))error = fn(dev, data);klist_iter_exit(&i);return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

2.9 __driver_attach 

static int __driver_attach(struct device *dev, void *data){......ret = driver_match_device(drv, dev);......ret = driver_probe_device(drv, dev);......
}
2.9.1.1 driver_match_device
static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}/* 返回 1 是可以继续往下走的 ret <= 0 不行*/

        可以看到在Register时有match回调 

struct bus_type platform_bus_type = {.......match	= platform_match,.probe	= platform_probe,......
};
2.9.1.2 platform_match
static int platform_match(struct device *dev, struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* When driver_override is set, only bind to the matching driver */if (pdev->driver_override)return !strcmp(pdev->driver_override, drv->name);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try ACPI style match */if (acpi_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);
}
2.9.1.3 of_driver_match_device
/*** of_driver_match_device - Tell if a driver's of_match_table matches a device.* @drv: the device_driver structure to test* @dev: the device structure to match against*/
static inline int of_driver_match_device(struct device *dev,const struct device_driver *drv)
{return of_match_device(drv->of_match_table, dev) != NULL;
}

         of_match_table定义如下

static struct platform_driver ufs_mtk_pltform = {.probe      = ufs_mtk_probe,.remove     = ufs_mtk_remove,.shutdown   = ufshcd_pltfrm_shutdown,.driver = {.name   = "ufshcd-mtk",.pm     = &ufs_mtk_pm_ops,.of_match_table = ufs_mtk_of_match,},
};
static const struct of_device_id ufs_mtk_of_match[] = {{ .compatible = "mediatek,mtxxxx-ufshci" },
};
2.9.1.4 of_match_device
const struct of_device_id *of_match_device(const struct of_device_id *matches,const struct device *dev)
{if (!matches || !dev->of_node || dev->of_node_reused)return NULL;return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
2.9.1.5 of_match_node
const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node)
{match = __of_match_node(matches, node);
}
EXPORT_SYMBOL(of_match_node);
2.9.1.6 __of_match_node
static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,const struct device_node *node)
{for (; matches->name[0] ||matches->type[0] || matches->compatible[0]; matches++) { /* 每次循环,选择Vendor驱动中的match table结构体数组的下一个比较 */score = __of_device_is_compatible(node, matches->compatible,matches->type, matches->name);if (score > best_score) {best_match = matches;best_score = score;}}return best_match;
}
2.9.1.7 __of_device_is_compatible
static int __of_device_is_compatible(const struct device_node *device,const char *compat, const char *type, const char *name)
{......if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {score = INT_MAX/2 - (index << 2);break;}......
}

        cp即为从设备树节点中获取的compatible信息,示例如下

ufshci: ufshci@112b0000 {compatible = "mediatek,mtxxxx-ufshci";reg = <0 0x112b0000 0 0x2a00>;
}
2.9.2.1 driver_probe_device
static int driver_probe_device(struct device_driver *drv, struct device *dev)
{......ret = __driver_probe_device(drv, dev);......
}
2.9.2.2 __driver_probe_device

        initcall_debug是一个内核参数,可以跟踪initcall,用来定位内核初始化的问题。在cmdline中增加initcall_debug后,内核启动过程中会在调用每一个init函数前有一句打印,结束后再有一句打印并且输出了该Init函数运行的时间,通过这个信息可以用来定位启动过程中哪个init函数运行失败以及哪些init函数运行时间较长

        really_probe_debug()内部还是调用了really _probe()

static int __driver_probe_device(struct device_driver *drv, struct device *dev)
{......if (initcall_debug)ret = really_probe_debug(dev, drv);elseret = really_probe(dev, drv);......
}
2.9.2.3 really_probe
static int really_probe(struct device *dev, struct device_driver *drv)
{......ret = call_driver_probe(dev, drv);......
}
2.9.2.4 call_driver_probe
static int call_driver_probe(struct device *dev,struct device_driver *drv)
{......if (dev->bus->probe)ret = dev->bus->probe(dev);else if (drv->probe)ret = drv->probe(dev);......
}
2.9.2.5 platform_probe

        不管走没有dev->bus->probe,最终都会走到drv->probe

static int platform_probe(struct device *_dev)
{struct platform_driver *drv = to_platform_driver(_dev->driver);struct platform_device *dev = to_platform_device(_dev);......if (drv->probe) {ret = drv->probe(dev);if (ret)dev_pm_domain_detach(_dev, true);}......
}

        此时驱动匹配设备成功,会走到之前Register的probe 

static struct platform_driver ufs_mtk_pltform = {.probe      = ufs_mtk_probe,......
};

【参考博客】

[1] Linux设备驱动和设备匹配过程_linux驱动和设备匹配过程-CSDN博客

[2] platform 总线_怎么查询platform 总线-CSDN博客

[3] Linux Driver 和Device匹配过程分析(1)_linux设备驱动和设备树的match过程-CSDN博客

[4] Linux驱动(四)platform总线匹配过程_platform平台设备匹配过程-CSDN博客

相关文章:

Linux 驱动设备匹配过程

一、Linux 驱动-总线-设备模型 1、驱动分层 Linux内核需要兼容多个平台&#xff0c;不同平台的寄存器设计不同导致操作方法不同&#xff0c;故内核提出分层思想&#xff0c;抽象出与硬件无关的软件层作为核心层来管理下层驱动&#xff0c;各厂商根据自己的硬件编写驱动…...

游戏子弹类python设计与实现详解

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、子弹类设计思路 1. 属性定义 2. 方法设计 三、子弹类实现详解 1. 定义子弹…...

Python基础学习笔记(六)——列表

目录 一、一维列表的介绍和创建二、序列的基本操作1. 索引的查询与返回2. 切片3. 序列加 三、元素的增删改1. 添加元素2. 删除元素3. 更改元素 四、排序五、列表生成式 一、一维列表的介绍和创建 列表&#xff08;list&#xff09;&#xff0c;也称数组&#xff0c;是一种有序、…...

帝国CMS跳过选择会员类型直接注册方法

国CMS因允许多用户组注册&#xff0c;所以在注册页面会有一个选择注册用户组的界面&#xff0c;即使网站只用了一个用户组也会出现。 如果想去掉这个页面&#xff0c;直接进入注册页面&#xff0c;那么可按以下办法修改 打开 e/class/user.php 文件 查找&#xff1a; $chan…...

【python】python tkinter 计算器GUI版本(模仿windows计算器 源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…...

黑马es数据同步mq解决方案

方式一:同步调用 优点:实现简单&#xff0c;粗暴 缺点:业务耦合度高 方式二:异步通知 优点:低耦含&#xff0c;实现难度一般 缺点:依赖mq的可靠性 方式三:监听binlog 优点:完全解除服务间耦合 缺点:开启binlog增加数据库负担、实现复杂度高 利用MQ实现mysql与elastics…...

通过LLM多轮对话生成单元测试用例

通过LLM多轮对话生成单元测试用例 代码 在采用 随机生成pytorch算子测试序列且保证算子参数合法 这种方法之前,曾通过本文的方法生成算子组合测试用例。目前所测LLM生成的代码均会出现BUG,且多次交互后仍不能解决.也许随着LLM的更新,这个问题会得到解决.记录备用。 代码 impo…...

[Redis]String类型

基本命令 set命令 将 string 类型的 value 设置到 key 中。如果 key 之前存在&#xff0c;则覆盖&#xff0c;无论原来的数据类型是什么。之前关于此 key 的 TTL 也全部失效。 set key value [expiration EX seconds|PX milliseconds] [NX|XX] 选项[EX|PX] EX seconds⸺使用…...

Ai速递5.29

全球AI新闻速递 1.摩尔线程与无问芯穹合作&#xff0c;实现国产 GPU 端到端 AI 大模型实训。 2.宝马工厂&#xff1a;机器狗上岗&#xff0c;可“嗅探”故障隐患。 3.ChatGPT&#xff1a;macOS 开始公测。 4.Stability AI&#xff1a;推出Stable Assistant&#xff0c;可用S…...

Android9.0 MTK平台如何增加一个系统应用

在安卓定制化开发过程中&#xff0c;难免遇到要把自己的app预置到系统中&#xff0c;作为系统应用使用&#xff0c;其实方法有很多&#xff0c;过程很简单&#xff0c;今天分享一下我是怎么做的&#xff0c;共总分两步&#xff1a; 第一步&#xff1a;要找到当前系统应用apk存…...

LabVIEW中实现Trio控制器的以太网通讯

在LabVIEW中实现与Trio控制器的以太网通讯&#xff0c;可以通过使用TCP/IP协议来完成。这种方法包括配置Trio控制器的网络设置、使用LabVIEW中的TCP/IP函数库进行数据传输和接收&#xff0c;以及处理通讯中的错误和数据解析。本文将详细说明实现步骤&#xff0c;包括配置、编程…...

C/C++运行时库与 UCRT 通用运行时库:全面总结与问题实例剖析

推荐一个AI网站&#xff0c;免费使用豆包AI模型&#xff0c;快去白嫖&#x1f449;海鲸AI 1. 概述 在开发C/C应用程序时&#xff0c;运行时库&#xff08;Runtime Library&#xff09;是不可或缺的一部分。它们提供了一系列函数和功能&#xff0c;使得开发者能够更方便地进行编…...

【Python001】python批量下载、插入与读取Oracle中图片数据(已更新)

1.熟悉、梳理、总结数据分析实战中的python、oracle研发知识体系 2.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 文章目录 1.背景说明2.环境搭建2.1 参考链接2.2 `oracle`查询测试代码3.数据请求与插入3.1 `Oracle`建表语句3.2 `Python`代码实现3.3 效果示例4.问题链…...

流形学习(Manifold Learning)

基本概念 Manifold Learning&#xff08;流形学习&#xff09;是一种机器学习和数据分析的方法&#xff0c;它专注于从高维数据中发现低维的非线性结构。流形学习的基本假设是&#xff0c;尽管数据可能在高维空间中呈现&#xff0c;但它们实际上分布在一个低维的流形上。这个流…...

区块链技术和应用

文章目录 前言 一、区块链是什么&#xff1f; 二、区块链核心数据结构 2.1 交易 2.2 区块 三、交易 3.1 交易的生命周期 3.2 节点类型 3.3 分布式系统 3.4 节点数据库 3.5 智能合约 3.6 多个记账节点-去中心化 3.7 双花问题 3.8 共识算法 3.8.1 POW工作量证明 总结 前言 学习长…...

Docker拉取镜像报错:x509: certificate has expired or is not yet v..

太久没有使用docker进行镜像拉取&#xff0c;今天使用docker-compose拉取mongo发现报错&#xff08;如下图&#xff09;&#xff1a; 报错信息翻译&#xff1a;证书已过期或尚未有效。 解决办法&#xff1a; 1.一般都是证书问题或者系统时间问题导致&#xff0c;可以先执行 da…...

猫狗分类识别模型建立②模型建立

一、导入依赖库 pip install opencv-python pip install numpy pip install tensorflow pip install keras 二、模型建立 pip install opencv-python pip install numpy pip install tensorflow pip install kerasimport os import xml.etree.ElementTree as ETimpor…...

React Native 之 ToastAndroid(提示语)(二十一)

ToastAndroid 是 React Native 提供的一个特定于 Android 平台的 API&#xff0c;用于显示简单的消息提示&#xff08;Toast&#xff09;。 两个方法&#xff1a; 1. ToastAndroid.show(message, duration, gravity) message: 要显示的文本消息。duration: Toast 的持续时间&…...

合约之间调用-如何实现函数静态调用?

合约之间的函数调用 EOA&#xff0c;external owned account&#xff0c;外部账号&#xff0c;例如metamask调用最终总是由EOA发起的合约之间的调用使得一次完整的调用成为一个调用链条 合约间调用过程 调用者须持有被调用合约的地址得到被调用合约的信息将地址重载为被调用合…...

【5.基础知识和程序编译及调试】

一、GCC概述&#xff1a;是GUN推出的多平台编译器&#xff0c;可将C/C源程序编译成可执行文件。编译流程分为以下四个步骤&#xff1a; 1、预处理 2、编译 3、汇编 4、链接 注&#xff1a;编译器根据程序的扩展名来分辨编写源程序所用的语言。根据不同的后缀名对他们进行相…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

Python爬虫实战:研究Restkit库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...

简单介绍C++中 string与wstring

在C中&#xff0c;string和wstring是两种用于处理不同字符编码的字符串类型&#xff0c;分别基于char和wchar_t字符类型。以下是它们的详细说明和对比&#xff1a; 1. 基础定义 string 类型&#xff1a;std::string 字符类型&#xff1a;char&#xff08;通常为8位&#xff09…...