platformd device、driver注册过程

news2024/10/7 20:25:06

本文以smsc911x驱动为例

platform_device注册过程

该设备被定义在dts里面了

参考文章设备树节点转换为设备节点device_node、和平台设备资源platform_device_设备树节点转换成平台设备-CSDN博客

dts里面的节点会被转换为device_node和platform_device(并不是所有节点都会被转换为platform_device)

    ethernet@3,02000000 {
			compatible = "smsc,lan9118", "smsc,lan9115";
			reg = <3 0x02000000 0x10000>;
			interrupts = <15>;
			phy-mode = "mii";
			reg-io-width = <4>;
			smsc,irq-active-high;
			smsc,irq-push-pull;
			vdd33a-supply = <&v2m_fixed_3v3>;
			vddvario-supply = <&v2m_fixed_3v3>;
		};

 内核初始化时,回去解析dts文件,然后去注册各个设备,大致的流程如下图

 (我也忘了哪里听到了这个说法,不知道对不对。platform总线,设备和驱动。设备往platform总线上注册,注册的时候回去probe,匹配驱动。同样注册驱动的时候,也会去匹配设备)

下图是之间调用platform_device_register注册设备的过程 

 在下列函数中加入了打印,看看究竟有哪些device_node

int of_platform_populate(struct device_node *root,
			const struct of_device_id *matches,
			const struct of_dev_auxdata *lookup,
			struct device *parent)
{
......................
	printk("\r\n %s,%d, device_node %s, parnet %s\n", __FUNCTION__, root->name, parent ? parent->init_name : "null");
	for_each_child_of_node(root, child) {
		printk("\r\n%s,%d child %s\n", __FUNCTION__, __LINE__, child->name);
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
		if (rc)
			break;
	}
....................
}

static int of_platform_bus_create(struct device_node *bus,
				  const struct of_device_id *matches,
				  const struct of_dev_auxdata *lookup,
				  struct device *parent, bool strict)
{
.........................
	for_each_child_of_node(bus, child) {
		printk("   create child: %s\n", child->full_name);
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
................
	}
	return rc;
}

 打印结构如下图。比较奇怪的是virtio_mmio dts里面没有,不知道是从哪里出来的

dts文件名vexpress-v2p-ca9.dts和vexpress-v2m.dtsi

最后根据dts或者是打印信息可以看到device_node的关系如下图。

 跟节点上挂了chosen...smb(simple bus),然后vexpress-v2m.dtsi里面的所有节点有全部挂到了smb下面。

这些节点是为什么会挂到smb下面呢?怀疑是smb节点包含了该头文件导致

/{

        chosen { };

..........................

        smb {
                compatible = "simple-bus";

        ..................................................

                /include/ "vexpress-v2m.dtsi"

        };

};

b. 并非所有的device_node都会转换为platform_device只有以下的device_node会转换:

  • 该节点必须含有compatible属性
  • 根节点的子节点(节点必须含有compatible属性)
  • 含有特殊compatible属性的节点的子节点(子节点必须含有compatible属性)这些特殊的compatilbe属性为: “simple-bus”,“simple-  mfd”,“isa”,"arm,amba-bus "
  • 根节点是例外的,生成platfrom_device时,即使有compatible属性也不会处理

注意:i2c, spi等总线节点会转换为platform_device,但是,spi、i2c下的子节点无论compatilbe是否为: “simple-bus”,“simple-  mfd”,“isa”,"arm,amba-bus "都应该交给对应的总线驱动程序来处理而不会被转换为platform_device

device_node并不等同于platform_device,感觉转换规则就是这个函数里面确定的

static int of_platform_bus_create(struct device_node *bus,
				  const struct of_device_id *matches,
				  const struct of_dev_auxdata *lookup,
				  struct device *parent, bool strict)
{
	const struct of_dev_auxdata *auxdata;
	struct device_node *child;
	struct platform_device *dev;
	const char *bus_id = NULL;
	void *platform_data = NULL;
	int rc = 0;

	/* Make sure it has a compatible property */
	if (strict && (!of_get_property(bus, "compatible", NULL))) {
		pr_debug("%s() - skipping %s, no compatible prop\n",
			 __func__, bus->full_name);
		return 0;
	}

	auxdata = of_dev_lookup(lookup, bus);
	if (auxdata) {
		bus_id = auxdata->name;
		platform_data = auxdata->platform_data;
	}

	if (of_device_is_compatible(bus, "arm,primecell")) {
		/*
		 * Don't return an error here to keep compatibility with older
		 * device tree files.
		 */
		of_amba_device_create(bus, bus_id, platform_data, parent);
		return 0;
	}
    /* 怀疑上面的代码就是引用里面说的转换规则 */
	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
	if (!dev || !of_match_node(matches, bus))
		return 0;
	if (g_v2m == 1)
		printk("platform_device: %s, device node: %s\n", dev->name, bus->full_name);
	for_each_child_of_node(bus, child) {
		//if (g_v2m == 1)
			//printk("   create child: %s\n", child->full_name);
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	return rc;
}

可以看到这么多device_node最后转换为platform_device的节点只有这三个 。compatible属性也满足条件

platform_device: smb, device node: /smb
platform_device: smb:motherboard, device node: /smb/motherboard
platform_device: smb:motherboard:iofpga@7,00000000, device node: /smb/motherboard/iofpga@7,00000000

    motherboard {
        model = "V2M-P1";
        arm,hbi = <0x190>;
        arm,vexpress,site = <0>;
        compatible = "arm,vexpress,v2m-p1", "simple-bus";
        #address-cells = <2>; /* SMB chipselect number and offset */
        #size-cells = <1>;
        #interrupt-cells = <1>;
        ranges;

那其他的节点如果不转换为platform_device,那是如何与platform_driver匹配的呢?比如Ethernet节点 

更正一下上面的说法,后面经过加打印,确认Ethernet节点其实是被转换为了platform_device了的

b. 并非所有的device_node都会转换为platform_device只有以下的device_node会转换:

那这句话还是对的吗?后面研究研究 

static int of_platform_bus_create(struct device_node *bus,
				  const struct of_device_id *matches,
				  const struct of_dev_auxdata *lookup,
				  struct device *parent, bool strict)
{
........................
	if (g_v2m == 1)
		printk("%s,%d device node: %s\n", __FUNCTION__, __LINE__, bus->full_name);
........................
	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
	if (!dev || !of_match_node(matches, bus))
	{
		if (g_v2m == 1)
			printk("fail create platform_device ??, dev %p, device node: %s\n",dev, bus->full_name);
		return 0;
	}
	if (g_v2m == 1)
		printk("%s,%d platform_device: %s, device node: %s\n", __FUNCTION__, __LINE__, dev->name, bus->full_name);
	for_each_child_of_node(bus, child) {
		if (g_v2m == 1)
			printk("   create child: %s\n", child->full_name);
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	return rc;
}

打印信息如下。暂时不知道of_match_node没有满足是什么意思。但是看样子device是创建成功了的。

 platform driver注册

关于platform_driver是在驱动代码里面通过module init,在初始化阶段通过do init call调用注册函数

大致流程如下 ​​​​​​

1 设备向内核注册platform_device(platform_device_register、或者是通过dts)​​​​​​,把设备挂在虚拟的platform bus下

2 驱动注册的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev()。对每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device()->drv->bus->match()==platform_match(),有几种匹配规则,如果相符就调用platform_drv_probe()->driver->probe(),如果probe(本例中则是smsc911x_drv_probe)成功则绑定该设备到该驱动

本例是通过驱动代码里面的of_match_table进行匹配。名字要和dts里面的相同

感觉1和2没有先后顺序,看代码不论是设备注册或者是驱动注册的时候都会去调用driver probe device尝试发现对方

dts中尝试新增设备

1 dts中新增节点

 

2、重新编译dtb文件。这样加载dts的时候就会注册该设备

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-

make dtbs

3、添加驱动代码,注册platform_driver

static int my_drv_probe(struct platform_device *pdev)
{
	printk("%s,%d platform_device name: %s, device_node full name %s\n", \
		__FUNCTION__, __LINE__, pdev->name, pdev->dev.of_node->full_name);
	return 0;	
}
static int my_drv_remove(struct platform_device *pdev)
{
	printk("%s,%d platform_device name: %s, device_node full name %s\n", \
		__FUNCTION__, __LINE__, pdev->name, pdev->dev.of_node->full_name);
	return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id new_node_test[] = {
	{ .compatible = "arm,newnodetest", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, new_node_test);
#endif

#define MY_DRV_NAME		"newnodetest"
static struct platform_driver my_driver = {
	.probe = my_drv_probe,
	.remove = my_drv_remove,
	.driver = {
		.name	= MY_DRV_NAME,
		.owner	= THIS_MODULE,
		.pm	= NULL,
		.of_match_table = of_match_ptr(new_node_test),
	},
};

/* Entry point for loading the module */
static int __init my_init_module(void)
{
	dump_stack();
	SMSC_INITIALIZE();
	return platform_driver_register(&my_driver);
}

/* entry point for unloading the module */
static void __exit my_cleanup_module(void)
{
	platform_driver_unregister(&my_driver);
}

module_init(my_init_module);
module_exit(my_cleanup_module);

运行信息打印

解析device node能看到有新增的节点

通过module init注册的初始化函数,也调用了,从而注册了platform_drive,最后也走到了我们写的probe函数

dts语法规则不清楚,后面看一下dts语法 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1637602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pandas Series的运算原来这么简单

Series的运算主要包括加法、减法、乘法和除法等基本算术运算。这些运算通常是按照索引对应计算的&#xff0c;如果两个Series的索引不同&#xff0c;则结果中对应位置将填充为NaN&#xff08;空值&#xff09;。 需要注意的是&#xff0c;在进行Series运算时&#xff0c;需要确…

升级 Vite 5 出现警告 The CJS build of Vite‘s Node API is deprecated

错误描述 vue3-element-admin 项目将Vite4 升级至 Vite5 后,项目运行出现如下警告: The CJS build of Vites Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.图片 问题原因 Vite 官方弃用 C…

PID详解汇总

一、参照文章 PID的各种算法优缺点 二、位置式PID 优点:静态误差小,溢出的影响小。 缺点:计算量很大&#x

java-spring-mvc(服务端接收客户端传参)

目录 &#x1f3af; 服务端接收参数 ✨HttpServletRequest接收 ✨ 声明参数接收 ✨声明pojo类来接收 &#x1f52a;小试牛刀 &#x1f3af; 服务端接收参数 ✨HttpServletRequest接收 HttpServletRequest是Java Servlet规范中定义的一个接口&#xff0c;它提供了与HTTP请求…

手撕sql面试题:找出所有观看视频ID “1001“ 的观看时长大于他们观看视频ID “1002“ 的观看时长的用户ID

分享最近面试的sql面试题&#xff1a; 下面是表结构&#xff1a; CREATE TABLE video_records ( video_id char(4) NOT NULL COMMENT 视频id, user_id char(4) NOT NULL COMMENT 用户id, play_duration int NOT NULL COMMENT 观看时长, PRIMARY KEY (video_id,…

stm32f103c8t6学习笔记(学习B站up江科大自化协)-PWR电源控制

PWR简介 PVD可用在电池供电或安全要求比较高的设备&#xff0c;如果供电电压在逐渐下降&#xff0c;在电压过低的情况下可能会导致内外电路出现不确定的错误。为了避免不必要的错误&#xff0c;可以在电源电压过低的情况下&#xff0c;提前发出警告并关闭较为危险的设备 关闭的…

Typora+PicGo+阿里云OSS搭建个人博客图床(2024最新详细搭建教程)

创作者&#xff1a;Code_流苏(CSDN) 目录 一、什么是图床&#xff1f;二、准备工作三、配置PicGo四、配置Typora五、使用 很高兴你打开了这篇博客&#xff0c;如有疑问&#xff0c;欢迎评论。 更多好用的软件工具&#xff0c;请关注我&#xff0c;订阅专栏《实用软件与高效工具…

[Transactional Level Bypass] Bypass Validation Rule in Apex Batch Class

问题 现有一个batch job用于批量更新Lead&#xff0c;最近频繁收到apex exception email, 显示更新Lead的时候触发了validation rule&#xff0c;导致apex job运行失败。 batch class节选如下&#xff1a; public void execute(Database.BatchableContext bc, List<Lead&…

大白话理解IoC和DI

引言 Spring是Java领域最受欢迎的开发框架之一&#xff0c;其核心功能之一就是Spring容器&#xff0c;也就是IoC容器。这篇文章&#xff0c;我们就来聊聊Spring的两大核心功能&#xff0c;控制反转&#xff08;IOC&#xff09;和依赖注入&#xff08;DI&#xff09;。 文章思…

C++ 模拟实现 priority_queue(优先队列)

目录 一&#xff0c;优先队列简介 二&#xff0c;priority_queue 的内部实现原理 三&#xff0c;模拟实现 priority_queue 1&#xff0c;模板参数与数据结构 2&#xff0c;构造 3&#xff0c;辅助功能&#xff08;堆的有序化&#xff0c;建立堆&#xff09; 4&#xff0…

【数据结构】链表专题2

前言 本篇博客继续探讨有关链表的专题&#xff0c;这片博客的题&#xff0c;提前打个预防针&#xff0c;有点意思哦&#xff0c;哈哈哈&#xff0c;话不多说&#xff0c;进入正文 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;数据结构 若有问题 评论…

STM32定时器门控模式+单脉冲模式配置

1、实现功能及使用场景&#xff1a; 利用一个主定时器多个从定时器&#xff0c;主定时器控制从定时器的脉冲发送时机和发送个数。 适合用在多轴同步控制的自动化或机器人设备中&#xff0c;同时可以防止系统程序跑飞时运动轴没有停止&#xff0c;提高系统安全。 2、门控模式…

【华为】路由综合实验(基础)

【华为】路由综合实验 实验需求拓扑配置AR1AR2AR3AR4AR5PC1PC2 查看通信OSPF邻居OSPF路由表 BGPBGP邻居BGP 路由表 配置文档 实验需求 ① 自行规划IP地址 ② 在区域1里面 启用OSPF ③ 在区域1和区域2 启用BGP&#xff0c;使AR4和AR3成为eBGP&#xff0c;AR4和AR5成为iBGP对等体…

服务器数据恢复—服务器重装系统导致XFS分区丢失的数据恢复案例

服务器数据恢复环境&#xff1a; 一台服务器MD1200磁盘柜&#xff0c;通过raid卡将15块磁盘组建成一组raid5磁盘阵列。raid5阵列分配了2个lun&#xff0c;操作系统层面对lun进行分区&#xff1a;1个分区采用LVM扩容方式加入到了root_lv中&#xff0c;其余分区格式化为XFS文件系…

Java发送请求-http+https的

第一步&#xff1a;建议ssl连接对象&#xff0c;信任所有证书 第二步&#xff1a;代码同时支持httphttps 引入源码类 是一个注册器 引入这个类&#xff0c;和它的方法create 注册器&#xff0c;所以对http和https都进行注册&#xff0c;参数为id和item&#xff0c;其中http的…

在UI界面中播放视频_unity基础开发教程

在UI界面中播放视频_unity基础开发教程 前言操作步骤结语 前言 之前我写过一篇在场景中播放视频的文章&#xff0c;但是在开发中有时候也会在UI的界面中播放视频&#xff0c;这期我们做一下在UI的界面中播放视频。 操作步骤 首先在场景中创建一个Raw Image&#xff0c;UI->…

Visual 下载 NuGet包速度变慢

Visual 下载 NuGet包速度变慢 最近遇到一个问题&#xff0c;即我在使用 Visual Studio 下载 Nuget 包的时候会发现变得特别慢&#xff0c;那么该如何解决该问题呢 Visual Studio → 工具 → NuGet 包管理项 → 程序包管理设置 → 程序包源 从上面我们可以看到我使用的包源地址…

Codeforces Round 942 (Div. 2) ----- A ----- F --- 题解

前情提要&#xff1a;因为数学水平原因&#xff0c;没法给出e的证明&#xff0c;因为我也是举例归类得出的结论&#xff0c;但是按理来说应该可以利用生成数函数证明 f题也是因为数学原因加上水平有限&#xff0c;我的理解可能有偏差。 目录 A. Contest Proposal&#xff1a…

【无线通信开发应用】nRF905数据手册深度解读

希望通过两个stm32、两个nRF905无线通信模块、串口来实现两机通信。具体功能为&#xff1a; 板子A、B分别包含一个stm32单片机和一个nRF905无线模块&#xff0c;欲实现板子A、B之间的通信。 其中&#xff0c;PC端串口助手可向板子A的stm32发送字符‘A’控制板子B上的LED亮灯&am…

算法系列--多源BFS问题

&#x1f495;"对相爱的人来说&#xff0c;对方的心意&#xff0c;才是最好的房子。"&#x1f495; 作者&#xff1a;Lvzi 文章主要内容&#xff1a;算法系列–多源BFS问题 大家好,今天为大家带来的是算法系列--多源BFS问题 前言: 之前我们已经学习过单源的最短路问…