1.Driver Model Introduction
Why driver model? 源自Driver Model Conversion
U-Boot originally had a pretty ad-hoc device driver system. While some subsystems have their own framework (e.g. mmc, block devices, stdio), others are implemented as bare function calls to be provided by whichever driver shows up. This limits us to one driver of a particular type (e.g. you cannot have USB2 and USB3 ports at the same time as they use different drivers).
Driver model aims to provide a consistent model for drivers and the platform data / device tree information needed by drivers to instantiate devices. In particular:
Uboot引入驱动模型(driver model),这种驱动模型为驱动的定义和访问接口提供了统一的方法,提高了驱动之间的兼容性以及访问的标准型,Uboot驱动模型和linux kernel的设备驱动模型相类似,但是又有所区别。
uboot driver model设计目标包括:
1.1.The modular concept
The driver core design is done with modularity in mind. The long-term plan is to extend this modularity to allow loading not only drivers, but various other objects into U-Boot at runtime – like commands, support for other boards etc.
1.2.Driver core initialization stages
The drivers have to be initialized in two stages, since the U-Boot bootloader runs in two stages itself.
1.2.1.First stage
The first stage runs after the bootloader did very basic hardware init. This means the stack pointer was configured, caches disabled and that’s about it. The problem with this part is the memory management isn’t running at all. To make things even worse, at this point, the RAM is still likely uninitialized and therefore unavailable.
1.2.2.Second stage
At this stage, the bootloader has initialized RAM and is running from it’s final location. Dynamic memory allocations are working at this point. Most of the driver initialization is executed here.
2.Uboot DM Enable
1>.add the config
configs/xxx_defconfig:
+CONFIG_DM=y //drivers/core/Kconfig
2>.add the uclass driver config
DM和uclass是息息相关的,如果希望在某个模块引入DM,那么就需要使用相应模块的uclass driver来代替旧版的通用driver。 以serial为例:
configs/xxx_defconfig:
+CONFIG_DM_SERIAL=y
Mafile定义:
driver/serial/Makefile:
ifdef CONFIG_DM_SERIAL
obj-y += serial-uclass.o //引入DM serial core驱动
else
obj-y += serial.o // 通用的serial core驱动
endif
3>.对应设备驱动也要引入dm的功能
设备驱动主要是实现和底层交互,为uclass层提供相应的接口函数。
233 U_BOOT_DRIVER(serial_msm) = {
234 .name = "serial_msm",
235 .id = UCLASS_SERIAL,
236 .of_match = msm_serial_ids,
237 .ofdata_to_platdata = msm_serial_ofdata_to_platdata,
238 .priv_auto_alloc_size = sizeof(struct msm_serial_data),
239 .probe = msm_serial_probe,
240 .ops = &msm_serial_ops,
241 };
3.DM模型
DM模型包含:uclass,udevice,driver,udevice和driver绑定,设备层级关系。
3.1.设计对象分析
对象的设计出于设计模块化的考虑,分为静态形式和运行态形式。
3.1.1.静态表达形式
3.1.2.运行态形式
3.2.uclass、udevice
框架图如下所示:
3.2.1.uclass
uclass代表一个类,同一类设备属于同一个uclass,拥有相同的uclass ID。以RTC为例,市面上RTC芯片很多,由不同的厂家生产,其内存寄存器定义甚至访问接口都不一样,所以RTC的driver肯定是不一样的,但是从功能的角度来说,它们都是用来记录时间的,所以它们都属于rtc-class。
3.2.2.udevice
device拥有设备资源,提供三种方式进行bind:
3.2.2.1.静态编译方式(Binding with Platform Data)
platform data is the old way of doing things. It is basically a C structure which is passed to drivers to tell them about platform-specific settings like the address of its registers, bus speed, etc. Device tree is now the preferred way of handling this.Unless you have a good reason not to use device tree (the main one being you need serial support in SPL and don’t have enough SRAM for the cut-down device tree and libfdt libraries) you should stay away from platform data.
Platform data is like Linux platform data, if you are familiar with that. It provides the board-specific information to start up a device.
在代码中调用U_BOOT_DEVICE宏来定义设备资源,即是一个设备实例。以tegra gpio为例:
arch/arm/mach-at91/arm926ejs/at91sam9260_devices.c:
/* Platform data for the GPIOs */
static const struct at91_port_platdata at91sam9260_plat[] = {
{ ATMEL_BASE_PIOA, "PA" },
{ ATMEL_BASE_PIOB, "PB" },
{ ATMEL_BASE_PIOC, "PC" },
};
U_BOOT_DEVICES(at91sam9260_gpios) = {
{ "gpio_at91", &at91sam9260_plat[0] },
{ "gpio_at91", &at91sam9260_plat[1] },
{ "gpio_at91", &at91sam9260_plat[2] },
};
该定义使用struct driver_info进行定义,除了特殊的情况,这种方式基本不用。
/**
* struct driver_info - Information required to instantiate a device
*
* NOTE: Avoid using this except in extreme circumstances, where device tree
* is not feasible (e.g. serial driver in SPL where <8KB of SRAM is
* available). U-Boot's driver model uses device tree for configuration.
*/
struct driver_info {
const char *name;
const void *platdata;
#if CONFIG_IS_ENABLED(OF_PLATDATA)
uint platdata_size;
#endif
};
3.2.2.2.设备数方式(Binding with Device Tree)
将设备描述信息写在对应的DTS文件中,DTS文件被编译成DTB文件,然后跟uboot 二进制文件合并在一起,uboot启动的时候,会解析DTB文件,将所有的设备描述信息解析出来,该方式定义设备资源信息是目前比较流行的方案。
了解这两种方式请参考- ./doc/driver-model/of-plat.txt.
3.2.2.3.参数的方式
通过命令行或者接口将设备资源信息传递进来,非常灵活。
3.3.DMSoftware Framework
如上图所示:
3.3.1.GD中和DM相关的部分
//include/asm-generic/global_data.h
typedef struct global_data {
#ifdef CONFIG_DM
struct udevice *dm_root; /* Root instance for Driver Model */ // DM中的根设备,也是uboot中第一个创建的udevice,也就对应了dts里的根节点。
struct udevice *dm_root_f; /* Pre-relocation root instance */ // 在relocation之前DM中的根设备
struct list_head uclass_root; /* Head of core tree */ // uclass链表,所有被udevice匹配的uclass都会被挂载到这个链表上
#endif
} gd_t;
4.DM四个主要组成部分
4.1.uclass id
每一种uclass都有唯一的ID number,在uclass_driver中进行定义,该值必须与udevice的driver中的uclass id一一对应, 这个id也是用来获取uclass的标志。uclass id定义如下:
include/dm/uclass-id.h:
enum uclass_id {
/* These are used internally by driver model */
UCLASS_ROOT = 0,
UCLASS_DEMO,
UCLASS_CLK, /* Clock source, e.g. used by peripherals */
UCLASS_PINCTRL, /* Pinctrl (pin muxing/configuration) device */
UCLASS_SERIAL, /* Serial UART */
...
}
4.2. struct uclass
1>.结构体:
struct uclass {
void *priv; // uclass的私有数据指针
struct uclass_driver *uc_drv; // 对应的uclass driver
struct list_head dev_head; // 链表头,连接所属的所有udevice
struct list_head sibling_node; // 链表节点,用于把uclass连接到uclass_root链表上
};
2>.获取uclass
int uclass_get(enum uclass_id key, struct uclass **ucp);
直接遍历链表gd->uclass_root链表,并且根据uclass id来获取到相应的uclass。 详细实现如下所示:
struct uclass *uclass_find(enum uclass_id key)
{
struct uclass *uc;
if (!gd->dm_root)
return NULL;
list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
if (uc->uc_drv->id == key)
return uc;
}
return NULL;
}
2>.如何定义
uclass是uboot自动生成,并且不是所有uclass都会生成,有对应uclass driver并且有被udevice匹配到的uclass才会生成。
3>.存放位置
所有生成的uclass都会被挂载gd->uclass_root链表上。
4.3.uclass_driver
1>.结构体:
include/dm/uclass.h:
struct uclass_driver {
const char *name; // 该uclass_driver的命令
enum uclass_id id; // 对应的uclass id
/* 以下函数指针主要是调用时机的区别 */
int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用
int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用
int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用
int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用
int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用
int (*child_post_bind)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备被绑定到该udevice之后调用
int (*child_pre_probe)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备进行probe之前调用
int (*init)(struct uclass *class); // 安装该uclass的时候调用
int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用
int priv_auto_alloc_size; // 需要为对应的uclass分配多少私有数据
int per_device_auto_alloc_size; //
int per_device_platdata_auto_alloc_size; //
int per_child_auto_alloc_size; //
int per_child_platdata_auto_alloc_size; //
const void *ops; //操作集合
uint32_t flags; // 标识为
};
#define UCLASS_DRIVER(__name) \
ll_entry_declare(struct uclass_driver, __name, uclass)
#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)))
2>.定义以serial-uclass为例:
UCLASS_DRIVER(serial) = {
.id = UCLASS_SERIAL,
.name = "serial",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = serial_post_probe,
.pre_remove = serial_pre_remove,
.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};
UCLASS_DRIVER展开最终得到如下结构体(存放在.u_boot_list_2_uclass_2_serial段):
struct uclass_driver _u_boot_list_2_uclass_2_serial = {
.id = UCLASS_SERIAL,
.name = "serial",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = serial_post_probe,
.pre_remove = serial_pre_remove,
.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
}
查看生成的u-boot.map:
.u_boot_list_2_uclass_2_serial
0x23e36970 0x48 drivers/serial/built-in.o
0x23e36970 _u_boot_list_2_uclass_2_serial // serial uclass driver的符号
.u_boot_list_2_uclass_2_simple_bus
0x23e369b8 0x48 drivers/built-in.o
0x23e369b8 _u_boot_list_2_uclass_2_simple_bus
最终,所有uclass driver结构体以列表的形式被放在.u_boot_list_2_uclass_1和.u_boot_list_2_uclass_3的区间中。
3>.获取API
想要获取uclass_driver需要先获取uclass_driver table,通过以下宏来获取
struct uclass_driver *uclass =
ll_entry_start(struct uclass_driver, uclass);
// 会根据.u_boot_list_2_uclass_1的段地址来得到uclass_driver table的地址
const int n_ents = ll_entry_count(struct uclass_driver, uclass);
// 获得uclass_driver table的长度
接着通过遍历这个uclass_driver table,得到相应的uclass_driver。 有如下API:
struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
// 从uclass_driver table中获取uclass id为id的uclass_driver。
4.4.udevice
uboot 通过两种方法来添加设备
4.4.1直接定义平台设备
struct driver_info {
const char *name;
const void *platdata;
#if CONFIG_IS_ENABLED(OF_PLATDATA)
uint platdata_size;
#endif
};
#define U_BOOT_DEVICE(__name) \
ll_entry_declare(struct driver_info, __name, driver_info)
/* Declare a list of devices. The argument is a driver_info[] array */
#define U_BOOT_DEVICES(__name) \
ll_entry_declare_list(struct driver_info, __name, driver_info)
除了根设备使用外,其他基本不使用:
drivers/core/root.c :
static const struct driver_info root_info = {
.name = "root_driver",
};
/* This is the root driver - all drivers are children of this */
U_BOOT_DRIVER(root_driver) = {
.name = "root_driver",
.id = UCLASS_ROOT,
};
/* This is the root uclass */
UCLASS_DRIVER(root) = {
.name = "root",
.id = UCLASS_ROOT,
};
4.4.2.Device tree
While platdata is useful, a more flexible way of providing device data is by using device tree. In U-Boot you should use this where possible. Avoid sending patches which make use of the U_BOOT_DEVICE() macro unless strictly necessary.
1>.数据结构:
include/dm/device.h
struct udevice {
const struct driver *driver; // 该udevice对应的driver
const char *name; // 设备名
void *platdata; // 该udevice的平台数据
void *parent_platdata; // 提供给父设备使用的平台数据
void *uclass_platdata; // 提供给所属uclass使用的平台数据
int of_offset; // 该udevice的dtb节点偏移,代表了dtb里面的这个节点node
ulong driver_data; // 驱动数据
struct udevice *parent; // 父设备
void *priv; // 私有数据的指针
struct uclass *uclass; // 所属uclass
void *uclass_priv; // 提供给所属uclass使用的私有数据指针
void *parent_priv; // 提供给其父设备使用的私有数据指针
struct list_head uclass_node; // 用于连接到其所属uclass的链表上
struct list_head child_head; // 链表头,连接其子设备
struct list_head sibling_node; // 用于连接到其父设备的链表上
uint32_t flags; // 标识
int req_seq;
int seq;
#ifdef CONFIG_DEVRES
struct list_head devres_head;
#endif
};
2>.定义
在dtb存在的情况下,由uboot解析dtb后动态生成。
3>.存放位置
4>.获取API
从uclass中获取udevice 遍历uclass->dev_head,获取对应的udevice。有如下API:
#define uclass_foreach_dev(pos, uc) \
list_for_each_entry(pos, &uc->dev_head, uclass_node)
#define uclass_foreach_dev_safe(pos, next, uc) \
list_for_each_entry_safe(pos, next, &uc->dev_head, uclass_node)
int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); // 通过索引从uclass中获取udevice
int uclass_get_device_by_name(enum uclass_id id, const char *name, // 通过设备名从uclass中获取udevice
struct udevice **devp);
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enum uclass_id id, int node,
struct udevice **devp);
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
const char *name, struct udevice **devp);
int uclass_first_device(enum uclass_id id, struct udevice **devp);
int uclass_first_device_err(enum uclass_id id, struct udevice **devp);
int uclass_next_device(struct udevice **devp);
int uclass_resolve_seq(struct udevice *dev);
4.5.driver
1>.数据结构
include/dm/device.h:
struct driver {
char *name;
enum uclass_id id;
const struct udevice_id *of_match;
int (*bind)(struct udevice *dev);
int (*probe)(struct udevice *dev);
int (*remove)(struct udevice *dev);
int (*unbind)(struct udevice *dev);
int (*ofdata_to_platdata)(struct udevice *dev);
int (*child_post_bind)(struct udevice *dev);
int (*child_pre_probe)(struct udevice *dev);
int (*child_post_remove)(struct udevice *dev);
int priv_auto_alloc_size;
int platdata_auto_alloc_size;
int per_child_auto_alloc_size;
int per_child_platdata_auto_alloc_size;
const void *ops; /* driver-specific operations */
uint32_t flags;
};
2>.如何定义
以s5pv210为例:
driver/serial/serial_s5p.c
U_BOOT_DRIVER(serial_s5p) = {
.name = "serial_s5p",
.id = UCLASS_SERIAL,
.of_match = s5p_serial_ids,
.ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
.probe = s5p_serial_probe,
.ops = &s5p_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
};
The driver will contain a structure located in a separate section, which will allow linker to create a list of compiled-in drivers at compile time. Let’s call this list “driver_list”. You can refer to the symobl in the u-boot.map after compilation.
#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)))
/* Declare a new U-Boot driver */
#define U_BOOT_DRIVER(__name) \
ll_entry_declare(struct driver, __name, driver)
最终得到结构体:(存放在.u_boot_list_2_driver_2_serial_s5p段)
struct driver _u_boot_list_2_driver_2_serial_s5p= {
.name = "serial_s5p",
.id = UCLASS_SERIAL,
.of_match = s5p_serial_ids,
.ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
.probe = s5p_serial_probe,
.ops = &s5p_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
};
3>.获取API
想要获取driver需要先获取driver table。 可以通过以下宏来获取driver table
struct driver *drv =
ll_entry_start(struct driver, driver);
// 会根据.u_boot_list_2_driver_1的段地址来得到uclass_driver table的地址
const int n_ents = ll_entry_count(struct driver, driver);
// 获得driver table的长度
接着通过遍历这个driver table,得到相应的driver。
struct driver *lists_driver_lookup_name(const char *name)
// 从driver table中获取名字为name的driver。
代码参考:
doc/driver-model/README.txt
include/dm/device.h
include/dm/uclass.h
include/dm/uclass-id.h
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数