u-boot 学习笔记:uclass 与 UCLASS_DRIVER 的理解

前言

  • u-boot 是嵌入式开发中经常使用的一种 bootloader,兼顾 boot (启动)与 loader(引导)等基础功能,应用于 ARM 等多个平台,通用性比较好,在嵌入式 Linux 开发中,用于引导启动 Linux 内核,也可以用于引导启动其他的操作系统

  • u-boot 地址 https://github.com/u-boot/u-boot.git

  • 当前代码版本:v2024.04-rc5

  • 本篇记录: UCLASS_DRIVER 宏定义的含义、uclass 的定义

uclass

  • u-boot 的设备与驱动管理模型中, 驱动使用 uclass 进行分类

  • struct uclass 的定义 include\dm\uclass.h

struct uclass {void *priv_;struct uclass_driver *uc_drv;struct list_head dev_head;struct list_head sibling_node;
};
  • 这里 uclass 有两个 链表节点: dev_head,用于链接这个类中的多个设备,可以简单理解为 如果设备驱动 属于同一个类,就链接在这里,dev_head 属于链表的【头部】header

  • 【备注】使用链表管理,是一种比较常见的通用的管理多个节点的方法

  • 这里 sibling_node 用于挂载到 类管理的链表上,之所以使用 链表,就是为了管理方便,所有的类,最终都挂载到一个全局的链表头部: gd->uclass_root

  • 在 u-boot 中,有一个结构比较复杂的 全局变量 gd。 gd 中的 uclass_root,用于挂载所有的 uclass 节点。

struct uclass_driver

  • struct uclass_driver 结构体看起来成员比较的多,其实仔细看看,就几个关键的成员
  • name 用于标识一个 uclass_driver
  • enum uclass_id id 属于 uclass 分类的 id,在 include\dm\uclass-id.h 定义,比如 UCLASS_I2C, /* I2C bus */
  • init 用于 uclass 初始化
struct uclass_driver {const char *name;enum uclass_id id;int (*post_bind)(struct udevice *dev);int (*pre_unbind)(struct udevice *dev);int (*pre_probe)(struct udevice *dev);int (*post_probe)(struct udevice *dev);int (*pre_remove)(struct udevice *dev);int (*child_post_bind)(struct udevice *dev);int (*child_pre_probe)(struct udevice *dev);int (*child_post_probe)(struct udevice *dev);int (*init)(struct uclass *class);int (*destroy)(struct uclass *class);int priv_auto;int per_device_auto;int per_device_plat_auto;int per_child_auto;int per_child_plat_auto;uint32_t flags;
};

uclass 的定义

  • 大概了解 struct uclassstruct uclass_driver 后,就需要了解 uclass 的使用方法,比如定义

  • 这里 u-boot 推荐使用 UCLASS_DRIVER 进行定义

/* Declare a new uclass_driver */
#define UCLASS_DRIVER(__name)						\ll_entry_declare(struct uclass_driver, __name, uclass_driver)
  • 比如 drivers\i2c\i2c-uclass.c 中的 i2c 的 类定义:
UCLASS_DRIVER(i2c) = {.id		= UCLASS_I2C,.name		= "i2c",.flags		= DM_UC_FLAG_SEQ_ALIAS,.post_bind	= i2c_post_bind,.pre_probe      = i2c_pre_probe,.post_probe	= i2c_post_probe,.per_device_auto	= sizeof(struct dm_i2c_bus),.per_child_plat_auto	= sizeof(struct dm_i2c_chip),.child_post_bind = i2c_child_post_bind,
};
  • 看来 UCLASS_DRIVER 这个宏有定义的功能,并且可以在定义后直接初始化,类似于全局的变量。

  • 这里展开了解一下 UCLASS_DRIVER 宏定义具体怎么工作的。

/* Declare a new uclass_driver */
#define UCLASS_DRIVER(__name)						\ll_entry_declare(struct uclass_driver, __name, uclass_driver)#define ll_entry_declare(_type, _name, _list)				\_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\__attribute__((unused))				\__section("__u_boot_list_2_"#_list"_2_"#_name)
  • 通过 对 ll_entry_declare 的展开了解,大概是定义了一个 基于 _type 类型的 变量,然后指定放在了 section 段中。

  • 比如 UCLASS_DRIVER(i2c) 展开后是

struct uclass_driver _u_boot_list_2_uclass_driver_2_i2c __aligned(4) __attribute__((unused)) __section("__u_boot_list_2_""uclass_driver""_2_""i2c")
  • 类型 struct uclass_driver,类似于【静态】定义一个 struct uclass_driver 类型的全局变量,放在了
    __u_boot_list_2_uclass_driver_2_i2c 段中

  • 指定段有个好处就是可以通过段名,对段中的定义的数据进行遍历操作,类似于结构体数组。指定 section 的作用是为了 集中管理,否则普通的全局变量,无法集中操作,指定 section,编译工具链可以把这些有规律命名的全局变量集中(排序)存放。

  • C 语言中,支持定义初始化,因此定义后,就紧接着可以初始化,这样就不需要单独的初始化函数了,这里的初始化,只是数据结构的成员数据填充。

  • 看来这个 ll_entry_declare 不仅可以用于定义 struct uclass_driver,还可以用于定义其他的存放到 section 中的全局变量,为的就是方便集中遍历管理,比如一起初始化、遍历查询,这种全局变量,一般没有【删除】的操作

uclass 的操作

  • UCLASS_DRIVER 定义后,使用 uclass_get 可以获取或者添加一个新的 class 到 uclass 的全局链表头:(((gd_t *)gd)->uclass_root)
uclass_get-> uclass_find-> uclass_add  (如果没有 find 到)

uclass_find

  • 这个函数,其实就是遍历 全局 gd->uclass_root 链表,确认是否存在指定 类 id 的 uclass,这里使用 list_for_each_entry,跟 Linux 中的list 管理接口基本一致,链表的遍历。
struct uclass *uclass_find(enum uclass_id key)
{struct uclass *uc;if (!gd->dm_root)return NULL;/** TODO(sjg@chromium.org): Optimise this, perhaps moving the found* node to the start of the list, or creating a linear array mapping* id to node.*/list_for_each_entry(uc, gd->uclass_root, sibling_node) {if (uc->uc_drv->id == key)return uc;}return NULL;
}

uclass_add

  • 这里简单的理解就是 : 添加到 gd->uclass_root
static int uclass_add(enum uclass_id id, struct uclass **ucp)
{struct uclass_driver *uc_drv;struct uclass *uc;int ret;*ucp = NULL;uc_drv = lists_uclass_lookup(id);  /* 查询是否已经存在? */if (!uc_drv) {debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n",id);/** Use a strange error to make this case easier to find. When* a uclass is not available it can prevent driver model from* starting up and this failure is otherwise hard to debug.*/return -EPFNOSUPPORT;}uc = calloc(1, sizeof(*uc));  /* 申请 struct uclass 内存 */if (!uc)return -ENOMEM;if (uc_drv->priv_auto) {void *ptr;ptr = calloc(1, uc_drv->priv_auto); /* 申请 private 私有内存 */if (!ptr) {ret = -ENOMEM;goto fail_mem;}uclass_set_priv(uc, ptr);}uc->uc_drv = uc_drv;INIT_LIST_HEAD(&uc->sibling_node);  /* 链表头部:必须初始化 */INIT_LIST_HEAD(&uc->dev_head); list_add(&uc->sibling_node, DM_UCLASS_ROOT_NON_CONST);  /* 添加到 `gd->uclass_root`,这里的宏定义  `#define DM_UCLASS_ROOT_NON_CONST	(((gd_t *)gd)->uclass_root)` */if (uc_drv->init) {ret = uc_drv->init(uc); /* uclass 初始化,调用 uclass 中 uclass_driver 的 init 成员 */if (ret)goto fail;}*ucp = uc;return 0;
fail:if (uc_drv->priv_auto) {free(uclass_get_priv(uc));uclass_set_priv(uc, NULL);}list_del(&uc->sibling_node);
fail_mem:free(uc);return ret;
}

知识点回顾

  • 首先 u-boot 有个全局的变量: gd,用于管理保存 u-boot 全局数据,其中包括 gd->uclass_root,这个是 uclass 的 链表头,用于挂载所有的 uclass 类结构

  • uclass 定义使用了 u-boot linker lists 定义方式,也就是 通过 section 进行集中管理,这样归类排序存放(编译工具链支持)后,可以集中的初始化、遍历与搜索。

  • linker lists 的理解其实并不难, 编译工具链支持的通过指定 section 名的方式存放定义的全局变量,并于会按照 ASCII 码进行排序,类似于定义了一个相同结构的数组,这个在编程中使用会方便很多。并且这个定义可以放在不同的代码文件中。这中指定 section 的定义方式编译链接阶段后完成了,因此代码执行阶段就可以方便的统一操作。

  • 【备注】 section 的操作前先获取 section 区域的起始(地址)。

小结

  • 本篇简单的记录 u-boot uclass 的定义,uclass 的定义,最终需要跟 udevice 进行关联,才能发挥 面向对象 类与对象的 软件抽象设计

  • u-boot 代码很优秀,u-boot 的 设备模型简单且实用,深入学习对嵌入式开发、软件编程有大的帮助。

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

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

相关文章

Form表单控件主要标签及属性。name属性,value属性,id属性详解。表单内容的传递流程,get和post数据传递样式。表单数据传递实例

form表单 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head> &…

ubuntu 安装java

在Ubuntu上安装Java通常有两种方式&#xff1a;使用包管理器安装默认仓库中的Java或者手动安装Oracle JDK。 使用APT包管理器安装&#xff1a; sudo apt update sudo apt install default-jdk 手动安装Oracle JDK&#xff1a; 首先&#xff0c;你需要从Oracle官网下载JDK的…

PromptRPA-手机上的智能代理框架

PromptRPA的设计基于一个智能代理的多代理框架&#xff0c;这些代理模拟人类的认知功能&#xff0c;专门用于解释用户意图、管理RPA生成的外部信息以及在智能手机上执行操作。传统的RPA技术能有效地自动化图形用户界面&#xff08;GUI&#xff09;上的任务&#xff0c;通过模仿…

浏览器缓存(强缓存、协商缓存)

一、浏览器缓存 这一点主要解析浏览器缓存以及缓存机制的详细过程。 与缓存相关的状态码&#xff1a; 200 ok 从浏览器下载的最新资源 200 (from memory cache) 不进行http请求&#xff0c;直接从浏览器内存中读取的资源&#xff0c;页面关闭&#xff0c;则资源释放&a…

【攻防世界】bug

垂直越权IP绕过文件上传 文件上传绕过&#xff1a; 1. mime检测 2. 大小写绕过 3. 等价替换&#xff08;php5&#xff0c;php3&#xff09; 4. 利用JavaScript执行php代码&#xff08;正常的php代码会被检测到&#xff0c;所以就用JavaScript来执行&#xff09; <script lan…

Linxu vim详解(常用命令)

目录 强烈建议全文阅读&#xff01; vim是什么&#xff1f; 命令模式 底行模式&#xff1a;shift &#xff1b; 普通用户无法sodu&#xff1f; vim配置问题&#xff1a;&#xff08;一点都不重要&#xff09; vim是什么&#xff1f; Vs 2022是一款集成开发软件 vim是一…

记录一下MySQL8版本更改密码规则

#查看当前密码策略 show variables like validate_password%;#修改密码等级为low set global validate_password.policy LOW; #注意MySQL8版本这是点&#xff0c;不是_#修改密码长度为6 set global validate_password.length 6;#查询我的数据库中user表host和user select host,…

[C++][算法基础]SPFA求负权边(Dijkstra优化)

给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c; 边权可能为负数。 请你判断图中是否存在负权回路。 输入格式 第一行包含整数 n 和 m。 接下来 m 行每行包含三个整数 x,y,z&#xff0c;表示存在一条从点 x 到点 y 的有向边&#xff0c;边长…

康耐视visionpro-CogFindCircleTool操作工具详细说明

◆CogFindCircleTool]功能说明: 通过用多个卡尺找到多个点来拟合所要找的圆 ◆CogFindCircleTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogFindCircleTool工具 ②.添加输入图像,右键“链接到”或以连线拖拽的方式选择相应输入源 ③预期的圆弧:设置预期圆弧的…

消除 BEV 空间中的跨模态冲突,实现 LiDAR 相机 3D 目标检测

Eliminating Cross-modal Conflicts in BEV Space for LiDAR-Camera 3D Object Detection 消除 BEV 空间中的跨模态冲突&#xff0c;实现 LiDAR 相机 3D 目标检测 摘要Introduction本文方法Single-Modal BEV Feature ExtractionSemantic-guided Flow-based AlignmentDissolved…

基于Spring Boot实现的图书个性化推荐系统

基于Spring Boot实现的图书个性化推荐系统 开发语言&#xff1a;Java语言 数据库&#xff1a;MySQL工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统实现 前台首页功能模块 学生注册 登录 图书信息 个人信息 管理员功能模块 学生管理界面图 图书分类管理界面图 图书信息管…

A17 STM32_HAL库函数 之 GPIO扩展驱动程序所有函数的介绍及使用

A1 STM32_HAL库函数 之 HAL系统驱动程序所有函数的介绍及使用 1 该驱动函数预览1.1 HAL_GPIOEx_ConfigEventout1.2 HAL_GPIOEx_EnableEventout1.3 HAL_GPIOEx_DisableEventout 该文档修改记录&#xff1a;总结 1 该驱动函数预览 序号函数名描述1HAL_GPIOEx_ConfigEventout()配…

基于R语言实现的负二项回归模型【理解与实现】-理解负二项回归模型和泊松回归模型之间的区别

前言 我们可以在R语言中使用MASS包中的glm.nb函数来拟合负二项模型&#xff0c;以及使用glm函数来拟合泊松模型。以下是一个详细的过程&#xff0c;包括模拟数据的生成、模型的拟合、结果的比较和解释。 需要的包 if (!require("MASS")) install.packages("M…

WPF中Binding的原理和应用

WPF中Binding的原理和应用 在WPF中&#xff0c;Binding机制是实现数据与界面的连接和同步的重要工具。了解Binding的原理和应用&#xff0c;对于开发人员来说是非常重要的。本文将详细介绍WPF中Binding的原理和应用&#xff0c;帮助读者更好地理解和运用这一强大的机制。 Bin…

Qt:发出一个信号,有多少相关槽函数执行?

返回连接signal的接收者的个数。 因为信号和槽都能作为信号的接收者&#xff0c;同时相同的连接能被建立很多次&#xff0c;接收者的数量和与该信号建立连接的数量相同。 当调用该函数时&#xff0c;你能使用SIGNAL()宏来传递一个特定的信号&#xff1a; if (receivers(SIGNA…

gitlab:Could not resolve host

fatal: unable to access http://xxx.git/: Could not resolve host: yyy Git-fatal: unable to access ‘https://gitlab.XX.git/‘: Could not resolve host: gitlab.XX.com.cn_drone unable to access .git/: could-CSDN博客 原因&#xff1a; 克隆的时候使用的是这里的HTT…

实现(图像、视频等)数据上云存储

实现&#xff08;图像、视频等&#xff09;数据上云存储 实现&#xff08;图像、视频等&#xff09;数据上云存储通常涉及以下几个步骤&#xff1a; 选择云存储服务商&#xff1a; 根据您的需求、预算、地域覆盖、数据安全性、服务稳定性等因素&#xff0c;选择一家合适的云存储…

QT助手翻译【QT 5.14】 -----QPushButton

目录 1 属性 2 公共职能 3 重新实现的公共功能 4 公用插槽 5 受保护的功能 6 保护方法 7 详细说明 1 属性 自动默认值&#xff1a;bool 此属性保存按钮是否为自动默认按钮 如果此属性设置为true&#xff0c;则该按钮为自动默认按钮。 在某些GUI样式中&a…

题目:有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数

题目&#xff1a;有n个整数&#xff0c;使其前面各数顺序向后移m个位置&#xff0c;最后m个数变成最前面的m个数 There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all pa…

Android之启动优化

不在 Application 初始化非必要的第三方库&#xff0c;尽量在使用的时候初始化&#xff0c;例如地图平台、播放器框架等这些可以在使用的时候再初始化。 启动优化误区&#xff1a; 启动页 Activity 的窗口背景采用透明色&#xff0c;由此来掩盖启动时卡白屏时间较长的问题&am…