关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究

关于platform_device和platform_driver的匹配 说句老实话,我不是太喜欢现在Linux 2.6这套bus, platform, device,device driver 的模式。我觉得这种模式破坏了“Linux简单就是美”的哲学,原来那套驱动已经可以干很多事,而且也可以直接注册驱动文件等等,以前的驱动在现在的结构上也还可以使用,把它在注册到bus这棵树上又有什么用呢?虽然可以看到一点对于移植性和平台管理方面的有点,但是我认为现在这种驱动编程的风格越来越像Windows的风格,很不直观和简约。牢骚发完了,bus结构还得继续,说说platform_devic 阅读详情


        在linux中platform平台驱动又三大部分组成,第一是bus、第二是驱动、第三是设备。

      第一总线也就是platform_bus,总线也是一种特殊的device,到底层下面还是要调用device_register来注册该总线设备,然后是用来注册总线的属性结构体 bus_type(platform_bus_type),至此platform平台的总线已经准备好。具体介绍可以看我另一篇博客。

      第二是设备,platform_device,它的注册流程是:platform_device_register(struct platform_device *pdev)->device_initialize(&pdev->dev)->platform_device_add(pdev)->pdev->dev.bus = &platform_bus_type->device_add(&pdev->dev)->把设备挂在虚拟的platform bus下。

       第三是驱动,现在总线、设备都准备好了,然后等着驱动来匹配设备进行驱动,具体的驱动匹配设备流程如下:

 1、

int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;//驱动的总线类型指向platform_bus_type
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;
	if (drv->suspend)
		drv->driver.suspend = platform_drv_suspend;
	if (drv->resume)
		drv->driver.resume = platform_drv_resume;
	return driver_register(&drv->driver);
}


 2、

int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	BUG_ON(!drv->bus->p);
//检测总线的操作函数和驱动的操作函数是否同时存在,同时存在则提示使用总线提供的操作函数
	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);
	//查找这个驱动是否已经在总线上注册,并增加引用计数,若已经注册,则返回提示信息。
	other = driver_find(drv->name, drv->bus);
	if (other) {
	//如果已经被注册,则返回提示错误并且减少引用计数。
		put_driver(other);
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EEXIST;
	}
	//若还没有注册,则在总线上注册该驱动
	ret = bus_add_driver(drv);
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret)
		bus_remove_driver(drv);
	return ret;
}


3、

 

int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;
//用于增加该bus所属的顶层bus的kobject的引用计数,返回的是其所属的顶层bus的指针。
	bus = bus_get(drv->bus);
	if (!bus)
		return -EINVAL;

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL);
	//将这两个结构体连接起来
	priv->driver = drv;
	drv->p = priv;
//指向顶层的bus的p->drivers_kset
//设置私有数据的父容器,在这一步中,设置了kset为platform下的drivers_kset结构,也就是drivers呢个目录 
	priv->kobj.kset = bus->p->drivers_kset;
//初始化kobj对象,设置容器操作集并建立相应的目录,这里由于没有提供parent,所以会使用父容器中的kobj为父对象  
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;
//检测所属总线的drivers_autoprobe属性是否为真   
  //为真则进行与设备的匹配,到这里,就会与我们之前注册的test_device连接上了,
  //至于如何连接,进行了什么操作,将在别的文章中详细描述 
	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
	//挂载到所属总线驱动链表上
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	module_add_driver(drv->owner, drv);
//建立uevent属性文件
	error = driver_create_file(drv, &driver_attr_uevent);
	if (error) {
		printk(KERN_ERR "%s: uevent attr (%s) failed\n",
			__func__, drv->name);
	}
	//建立设备属性文件
	error = driver_add_attrs(bus, drv);
	if (error) {
		/* How the hell do we get out of this pickle? Give up */
		printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
			__func__, drv->name);
	}
	error = add_bind_files(drv);
	if (error) {
		/* Ditto */
		printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
			__func__, drv->name);
	}

	kobject_uevent(&priv->kobj, KOBJ_ADD);
	return 0;
out_unregister:
	kfree(drv->p);
	drv->p = NULL;
	kobject_put(&priv->kobj);
out_put_bus:
	bus_put(bus);
	return error;
}


 4、驱动的匹配关键是上面函数中的

	if (drv->bus->p->drivers_autoprobe) {//drivers_autoprobe在初始化的时候定义为1,系统则会调用下面的driver_attach函数进行驱动与设备的匹配
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}



 

int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
 //bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{/*

struct klist_iter {
	struct klist		*i_klist;
	struct klist_node	*i_cur;
};

*/
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus)
		return -EINVAL;
//如果第三个参数不为空就增加引用计数
	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));//一直是NULL
//经过上面的宏之后,i实际上变成了i->i_klist=&bus->p->klist_devices,bus->p->klist_devices指向的是挂接在它上面的所有的设备的指针			     
	while ((dev = next_device(&i)) && !error)
//fu函数传入的是device的指针和device_driver的指针,
		error = fn(dev, data);
//如果klist_iter_init_node第三个参数不为空则减少引用计数
	klist_iter_exit(&i);
	return error;
}


 //寻找到下一个设备的节点

static struct device *next_device(struct klist_iter *i)
{
	struct klist_node *n = klist_next(i);
	struct device *dev = NULL;
	struct device_private *dev_prv;

	if (n) {
		dev_prv = to_device_private_bus(n);
		dev = dev_prv->device;
	}
	return dev;
}


 函数error = fn(dev, data)的原型如下:

传进来的参数第一个参数为不断遍历到的设备节点的指针,第二个参数为固定的一个驱动所对应的struct device_driver *drv指针,这样就实现驱动和设备的匹配

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */
//当设备和驱动的名字不匹配的时候返回的是0,然后就会调用下面的return 0;
	if (!driver_match_device(drv, dev))
		return 0;

	if (dev->parent)	/* Needed for USB */
		down(&dev->parent->sem);
	down(&dev->sem);
	if (!dev->driver)
		driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数
	up(&dev->sem);
	if (dev->parent)
		up(&dev->parent->sem);

	return 0;
}


在上面有两个比较关键的函数driver_match_device(dre,dev),函数原型如下:

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;//无论设备与驱动是否匹配成功都会返回1
}

当驱动的指针不为空的时候,这个drv->bus所指向的的这个驱动所属的的总线的bus_type中的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);

	/* match against the id table first */
	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);
}


最后调用驱动的probe函数进行设备的探测

driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;
//再次检查设备有没有在总线上注册,当发现还没有注册的时候,返回一个错误
	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	ret = really_probe(dev, drv);

	return ret;
}


 

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;

	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));
//找到了设备的驱动,并且将dev->driver指针指向自己的这个驱动
	dev->driver = drv;
	if (driver_sysfs_add(dev)) {//在sys目录下建立连接指向自己的在sys中的drivers
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}
//在bus_type  platform_bus_type中并没有设置probe函数,所以下面函数并不会被调用
	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {//上面总线没有probe函数,所以直接调用驱动当中的probe函数
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	driver_bound(dev);
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

probe_failed:
	devres_release_all(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;

	if (ret != -ENODEV && ret != -ENXIO) {
		/* driver matched but the probe failed */
		printk(KERN_WARNING
		       "%s: probe of %s failed with error %d\n",
		       drv->name, dev_name(dev), ret);
	}
	/*
	 * Ignore errors returned by ->probe so that the next driver can try
	 * its luck.
	 */
	ret = 0;
done:
	atomic_dec(&probe_count);
	wake_up(&probe_waitqueue);
	return ret;
}

        至此platform_driver的probe函数实现了调用并且匹配了platform_device
 

 

platform_device 与 platform_driver 是如何匹配的? 我们知道 platform_device 注册到内核以后,当我们每注册一个 platform_driver 时,它们就会两两确定是否能够匹配,如果能够配对成功,就会调用 platform_driver 的 probe() 函数。 platform_device 是怎么来的呢?一般有三种来源 1、从设备树的某些节点转换而来 (具体哪些节点才能够转换呢?请参考下一篇) 2、平台... 阅读详情

相关推荐

Linux驱动:probe函数何时被调用

linux中 probe函数何时调用的          所以的驱动教程上都说:只有设备和驱动的名字匹配,BUS就会调用驱动的probe函数,但是有时我们要看看probe函数里面到底做了什么,还有传递给probe函数的参数我们就不知道在哪定义(反正不是我们在驱动里定义的),如果不知道传递进的参数,去看probe函数总是感觉不求甚解...

weixin_44498318的博客 3167

platform驱动之probe函数

驱动注册的probe函数 probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工作。资源、中断调用函数以及其他相关工作。下面是probe被调用的一些程序流程。 从driver_register看起: int driver_register(struct device

gchww的专栏 2万+

RK3399平台开发系列讲解(内核入门篇)1.52、Platform 中 probe函数的调用栈分析

📢我们知道硬件刚起来的时候,bootloader 通过传参让内核选择合适的 dts,kernel 在接收到正确的 dts 时,会将各个合适的 node 进行注册(platform_device_register),并挂在 bus 上,底层驱动进行注册时(platform_driver_register),会先通过遍历 bus 上的 device(bus_for_each_dev),然后通过 dts 中的 compatible 进行匹配(platform_match中执行,dts 匹配只是其中一种匹配方式).

内核笔记 2045

linux驱动中probe函数是怎么调用的

linux驱动中probe函数是怎么调用的

qq_41483419的博客 2832

开源大模型本地部署实战:Llama、DeepSeek、Qwen选型与工程落地指南

大语言模型(LLM)本地部署已成为AI工程化核心环节,其本质不是比拼参数量或榜单分数,而是考察模型架构、推理引擎、量化支持、IDE集成与多模态扩展能力的系统性适配。Llama以纯净Transformer结构成为通用底座,依赖llama.cpp和GGUF量化实现跨平台轻量推理;DeepSeek深度优化代码与数学推理,通过原生OpenAPI、FlashAttention-2及tensor parallelism显著降低API延迟;Qwen则采用模块化设计,支持视觉编码器(Qwen-VL)、生物计算(Qwen-B

weixin_29952383的博客 396

ESP32 CAM安装配置 第四部份:通过使用ESPHome的方式ESP32 Cam加到HomeAssistant

这些包括 aec_value, aec2, ae_level, agc_gain, gain_ctrl, bpc, wpc, raw_gma, lenc, hmirror, dcw, colorbar 等。这些参数的可能值和具体效果会根据你的摄像头硬件和 ESPHome 的版本有所不同。在 ESPHome 中配置 ESP32 Cam 时,每个参数的可能值取决于具体的功能和支持范围。值的范围是 0 到 63,其中 0 是最差质量,63 是最佳质量。horizontal: 水平校正的强度,通常是一个数字值。

weixin_42895252的博客 3365

Linux内核LCD驱动分析(二)

上一节我们分析了framebuffer驱动的核心部分,下面我们就以Atmel的lcd驱动作为例子简单的分析一下驱动的实现过程,也就是注册fb_info的过程。 LCD的驱动位于内核源码的drivers/video/fbdev中, Atmel的驱动源码是atmel_lcdfb.c。驱动的入口是 module_platform_driver_probe(atmel_lcdfb_driver, at...

mr_xiaogui的博客 1975

【UE 反射】反射的原理是什么?如何使用机制?

静态类型检查确保了类型的早期一致性和高效的代码执行,但减少了灵活性。动态类型检查提供了更大的灵活性,但增加了运行时的开销和潜在的错误。反射作为一种动态特性,无论是在静态类型语言还是动态类型语言中,都提供了强大的运行时类型操作能力,但也伴随着性能和安全性方面的权衡。通过理解这些联系,开发者可以在不同的编程语言和应用场景中选择适合的类型检查方式和反射机制,以实现所需的动态行为和灵活性,同时保证性能和安全性。

hhw_hhw的博客 6033

platform驱动的probe过程

probe()函数是什么时候被调用,设备和驱动是怎么联系起来的?? 1 总线注册阶段: 内核启动初始化时,main.c文件中platform平台初始化的路径: kernel_init() -->do_basic_setup() -->driver_init() -->platform_bus_init(...

lx123010的专栏 2299

【tensorrt】——深入解析TypeError: pybind11::init()报错:CUDA版本冲突与解决方案

本文深入解析了TensorRT中常见的TypeError: pybind11::init()报错问题,重点探讨了CUDA版本冲突的根源与解决方案。通过实际案例展示了PyTorch与TensorRT的版本兼容性问题诊断方法,并提供了conda环境管理、环境变量配置等实用修复技巧,帮助开发者有效解决CUDA初始化失败问题。

ggg99的专栏 272

CANoe信号传输故障排查指南

本文提供了全面的CANoe信号传输故障排查指南,涵盖从硬件连接、通道配置到软件脚本与数据库加载的全链路诊断。针对常见的通道指示灯不亮、信号无法发送及ECU握手失败等问题,给出了基于CANoe工具和CAPL脚本的详细排查步骤与解决方案,帮助工程师系统化定位并解决实车测试中的通信故障。

ice55的博客 258

kernel study

http://blog.youkuaiyun.com/fulinus/article/details/8762014

u013704116的专栏 379

KVM — 与 QEMU 和 Libvirt 的关系

目录目录 前言 KVM QEMU KVM 与 QEMU qemu-kvm Libvirt Libvirt 在 OpenStack 中的应用前言如果是刚开始接触虚拟机技术的话, 对上述的概念肯定会有所混淆, 傻傻的分不清. 尤其在看虚拟化技术文档时导致理解能力下降, 所以在开始学习虚拟化技术之前对这些概念有一个整体的认识和清晰的理解, 就显得很有必要了.KVMKVM(Kernel-basedVirtu

烟云的计算 5737

如何理解驱动程序、设备树、platform、device、driver之间的关系

理解驱动程序、设备树、platform、device、driver之间的关系,学习利用设备树来使用或者编写驱动程序

weixin_42408707的博客 6532

【国产MCU系列】-GD32F4开发环境搭建(基于Embedded Builder)

GD32F4系列MCU采用Arm® Cortex®-M4内核,处理器主频高达240MHz,可支持算法复杂度更高的嵌入式应用,具备更快速的实时处理能力,并拥有业界领先的大容量存储优势。

视觉与物联智能 3080

【工具插件类教学】Unity翻书效果插件Book-Page Curl Pro教程

特征: • 从左到右和从右到左翻页 • 与画布缩放器 配合使用 • 可配置的自动翻转脚本 • 支持向页面 添加 UI 元素 • 支持透明页面 • 适用于“世界空间”画布

纯技术流日常开发商业开发记录总结等 3674

3.6内核对设备树的处理——platform_device跟platform_driver的匹配

platform_device跟platform_driver的匹配

一个嵌入式软件工程师的博客 1614

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Linux Driver 和Device匹配过程分析(1)

以linux-5.14.10内核为例来分析总线注册过程: 1,总线注册匹配过程 总线注册过程以rk3399的PCIe控制器注册过程为例去做分析。 1.1 struct platform_driver 代码路径:include/linux/platform_device.h 206 struct platform_driver { 207 int (*probe)(struct platform_device *); 208 int (*remove)(struct platf

天使之翼 3804

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Linux驱动中platfrom-driver/device注册及probe执行过程分析

1、platform系统 platfrom系统在linux内核中包含三个部分 ①platfrom-bus虚拟总线,是一个内核抽象出来的虚拟的总线,在linux内核中以链表的形式表示,platform-driver/device通过挂载在platform虚拟总线上,实现设备和驱动之间的匹配,在内核表现形式为一系列platform-driver/device结构体组成的链表形式; ②platform-driver模型,内核中抽象出来的driver模型,表现形式为platfrom-driver的结构体,它的

QQ135102692的博客 2485

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

上一篇: Linux内核源码阅读之打开文件篇
下一篇: Linux设备模型(上)之底层模型
ADC0809608
博客等级 码龄17年 44粉丝 10原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值