1 铺垫一下 鸿蒙系统终于公开源代码了,正可谓“千呼万唤始出来”。笔者也手痒下载了一套代码,并研读了一二。这里就先编写一篇关于hdf的文档。
其实,不同读码人都会有各自读代码的习惯和切入点,我之所以从hdf入手,完全是出于偶然。因为在一开始读官方文档时,看到说一部机器可以操作另一部机器的设备,为此,设备需要有一个重要的publishservice()函数。这种跨设备操作的能力也是鸿蒙的一大特色,应该比较有趣,于是就以这个publishservice()为切入点,开始研读代码,慢慢就涉及了hdf的更多知识,现在是时候整理出来了。
所谓hdf,应该是harmony driver fundation的缩写,说到底是鸿蒙形成的一套管理设备驱动的框架模型,也被称为“驱动子系统”。在官网的文档里介绍说这个驱动子系统具有以下重要能力:
弹性化的框架能力
规范化的驱动接口
组件化的驱动模型
归一化的配置界面
读完这四句话,不免让人觉得好像明白了什么,又好像什么都没明白。好吧,我还是按自己的习惯直接读代码吧。
为了便于理解代码,我习惯于把软件图形化。为此,我介绍一点我的图形表达方法。在我读java代码时,如果要表达a类对象的某个成员引用了另一个b类对象,我常常会这样绘制:
但是hdf的代码是用c写的,所以对应的图形表达法也要有所变化。我们要区分一下:
1)a结构以某成员组合了另一个b结构;
2)a结构某成员是个指向b的指针;
这两种情况可以分别表示为:
另外,有时候hdf会使用c语言的一些技巧进行链表表达或基类转换,那么上面的图形画出来就会很累赘,针对这种情况,我有时候会这样表达(以devmgrservice结构为例):
可以看出,devmgrservice继承于idevmgrservice,而idevmgrservice又在起始处组合了一个hdfdeviceobject(有时候也可以说是继承于hdfdeviceobject)。也就是说:
1)devmgrservice的起始地址;
2)devmgrservice内部idevmgrservice部分的起始地址;
3)idevmgrservice内部hdfdeviceobject部分的起始地址;
这3个起始地址其实是同一处。用这种表达法,我们就不必画出3个分离的框图了。
好了,铺垫部分就先写这么多,下面我们来看hdf的实际内容。
2 devmgrservice和dev host 我们以hi3516 dv300为例,其系统一启动,会运行到systeminit(),其中会调用devicemanagerstart()启动与hdf相关的部分:
【vendor/hisi/hi35xx/hi3516dv300/module_init/src/system_init.c】
void systeminit(void)
{
. . . . . .
#ifdef loscfg_drivers_hdf
if (devicemanagerstart()) {
print_warn(no drivers need load by hdf manager!);
}
#endif
. . . . . .
}
2.1 启动devicemanager
【drivers/hdf/lite/manager/src/devmgr_service_start.c】
int devicemanagerstart()
{
struct idevmgrservice *instance = devmgrservicegetinstance();
if (instance == null || instance->startservice == null) {
hdf_loge(device manager start failed, service instance is null!);
return hdf_failure;
}
struct hdfioservice *ioservice = hdfioservicebind(dev_mgr_node, dev_mgr_node_perm);
if (ioservice != null) {
static struct hdfiodispatcher dispatcher = {
.dispatch = devicemanagerdispatch,
};
ioservice->dispatcher = &dispatcher;
ioservice->target = (struct hdfobject *)&instance->object;
}
return instance->startservice(instance);
}
简单地说,要启动devicemanager服务,就得先获取一个devmgrservice实例,然后调用它的startservice(),又因为devmgrservice继承于idevmgrservice,所以可以强制转换成idevmgrservice*。
2.1.1 获取devmgrservice单例
获取实例时,其实用到了hdf机制提供的一个对象管理器,相关代码如下:
【drivers/hdf/frameworks/core/manager/src/devmgr_service.c】
struct idevmgrservice *devmgrservicegetinstance()
{
static struct idevmgrservice *instance = null; // 注意是static的,表示是个静态单例
if (instance == null) {
instance = (struct idevmgrservice *)
hdfobjectmanagergetobject(hdf_object_id_devmgr_service);
}
return instance;
}
以后我们会看到,这个hdfobjectmanagergetobject()会在多个地方调用,以便获取不同的hdf对象。说起来也简单,hdf机制里有一张表,记录着该如何创建、释放一些重要的hdf对象,该表格为g_liteobjectcreators:
【drivers/hdf/lite/manager/src/devlite_object_config.c】
static const struct hdfobjectcreator g_liteobjectcreators[]
基于读到的代码,我们可以画出这个表格:
概念还是比较简单的,如果系统中的devmgrservice单例对象已经存在,就使用之。否则就利用hdf对象管理器创建一个devmgrservice对象。对于hdf对象管理器而言,不同类型的hdf对象,需要用到不同的创建函数,所以要查一下上表。比如devmgrservice对应的创建函数就是devmgrservicecreate(),该函数代码如下:
【drivers/hdf/frameworks/core/manager/src/devmgr_service.c】
struct hdfobject *devmgrservicecreate()
{
static bool isdevmgrserviceinit = false;
static struct devmgrservice devmgrserviceinstance;
if (!isdevmgrserviceinit) {
if (!devmgrserviceconstruct(&devmgrserviceinstance)) {
return null;
}
isdevmgrserviceinit = true;
}
return (struct hdfobject *)&devmgrserviceinstance; // ?hdfobject,有小问题!
}
在“创建”时,如果发现是首次创建,则调用一个类似构造函数的devmgrserviceconstruct()函数,来初始化对象里的函数表。这种做法是用c语言实现面向对象概念的常用做法。不过,此处的代码有一个小bug,即最后那个强制转换,从目前看到的代码来说,devmgrservice间接继承于hdfdeviceobject,而hdfdeviceobject并不继承于hdfobject,所以是不应该这样强制转换的,除非hdfdeviceobject的第一个成员从“ideviceioservice*”改为“ideviceioservice”,我估计最早的代码就是ideviceioservice,后来因为某些原因,变成了指针形式,至于以后具体该怎么修正,这个就让鸿蒙的工程师去费脑筋吧。devmgrservice的构造函数如下:
【drivers/hdf/frameworks/core/manager/src/devmgr_service.c】
static bool devmgrserviceconstruct(struct devmgrservice *inst)
{
if (osalmutexinit(&inst->devmgrmutex) != hdf_success) {
hdf_loge(%s mutex init failed, __func__);
return false;
}
struct idevmgrservice *devmgrsvcif = (struct idevmgrservice *)inst;
if (devmgrsvcif != null) {
devmgrsvcif->attachdevice = devmgrserviceattachdevice;
devmgrsvcif->attachdevicehost = devmgrserviceattachdevicehost;
devmgrsvcif->startservice = devmgrservicestartservice;
devmgrsvcif->acquirewakelock = devmgrserviceacquirewakelock;
devmgrsvcif->releasewakelock = devmgrservicereleasewakelock;
hdfslistinit(&inst->hosts);
}
return true;
}
2.1.2 hdfioservicebind()
启动devicemanager时,第二个重要的动作是调用hdfioservicebind():
struct hdfioservice *ioservice = hdfioservicebind(dev_mgr_node, dev_mgr_node_perm);
这一步在做什么呢?我们可以这样理解,devmgrservice作为一个核心的系统服务,我们希望能像访问虚文件系统的文件那样打开它,并进一步向它传递诸如attachdevice、startservie......这样的语义。这些语义最终会执行到上面列举的devmgrserviceattachdevice、devmgrservicestartservice等函数。
我们不必列举太多代码,下面是我绘制的一张关于devicemanagerstart()的调用关系示意图,可供参考:
图中已经明确注明,devmgrservice在虚文件系统里对应的路径应该是“/dev/dev_mgr”,而上面调用hdfioservicebind()后,实际上建立了一个文件系统的inode节点,示意图如下:
hdfvnodeadapter的target在最后赋值为(struct hdfobject*)&instance->object,说到底其实就是指向了devmgrservice。
2.1.3 执行devmgrservice的startservice
接下来是启动devicemanager的第三步,调用instance->startservice(),这一步其实是在调用devmgrevicestartservice()函数。
【drivers/hdf/frameworks/core/manager/src/devmgr_service.c】
int devmgrservicestartservice(struct idevmgrservice *inst)
{
struct devmgrservice *dmservice = (struct devmgrservice *)inst;
if (dmservice == null) {
hdf_loge(start device manager service failed, dmservice is null);
return hdf_failure;
}
return devmgrservicestartdevicehosts(dmservice);
}
主要就是在调用一个devmgrservicestartdevicehosts()函数。这个函数应该算是个重量级函数,它会负责建立起devmgrservice内部主要的数据结构。我们先绘制一下该函数第一层次的调用关系,如下图:
在进一步深入代码细节之前,我们最好先大概说明一下。在鸿蒙hdf架构里,有一个“设备host”的概念,根据官方的文档,我们大概可以知道,一个host用于整合若干业务相近的设备,这个原则被称为相似相容原则。为了实现这个原则,hdf构造了一系列数据结构,我们列举一下:
1)hdfhostinfo
2)devhostserviceclnt
3)devhostservice
4)hdfdevice
5)hdfdevicenode
. . . . . .
我们当然没必要在一篇文档里列出所有的数据结构,只需先明白:
1)设备管理服务(devmgrservice)内部可以管理若干host;
2)每个host内部可以整合若干业务相近的设备;
3)每个host可以拆分成两个部分:devhostserviceclnt 和 devhostservice;
4)每个devhostservice可以添加多个设备;
从上面的调用关系图中,我们可以看到devmgrservicestartdevicehosts()函数的主要行为是:
1)先获取一个驱动安装器(单例)对象;
2)解析系统配置信息,将其转换成一个以hdfhostinfo为表项的列表,这个就对应着系统里所有的host;
3)遍历这张hdfhostinfo列表,为每个hdfhostinfo节点创建一个对应的devhostserviceclnt对象;
4)新创建的devhostserviceclnt节点会被插入devmgrservice的hosts列表中;
5)针对每个hdfhostinfo节点,利用刚刚获取的驱动安装器具体启动该host。
2.1.3.1获取驱动安装器
现在我们详细看上图中调用的关键函数。
installer = driverinstallergetinstance();
先拿到一个驱动安装器。
struct idriverinstaller *driverinstallergetinstance()
{
static struct idriverinstaller *installer = null;
if (installer == null) {
installer = (struct idriverinstaller *)hdfobjectmanagergetobject(hdf_object_id_driver_installer);
}
return installer;
}
又看到hdfobjectmanagergetobject(),于是我们查前文那张表,可以找到驱动安装器对应的创建函数是driverinstallercreate():
【drivers/hdf/frameworks/core/manager/src/hdf_driver_installer.c】
struct hdfobject *driverinstallercreate(void)
{
static bool isdriverinstinit = false;
static struct driverinstaller driverinstaller;
if (!isdriverinstinit) {
driverinstallerconstruct(&driverinstaller);
isdriverinstinit = true;
}
return (struct hdfobject *)&driverinstaller;
}
用的是一个单例的driverinstaller对象。
2.1.3.2 获取hdfhostinfo列表
启动所有hosts的第二步,是获取一个hdfhostinfo列表:
hdfattributemanagergethostlist(&hostlist)
我们摘选该函数的主要句子,如下:
【drivers/hdf/lite/manager/src/hdf_attribute_manager.c】
bool hdfattributemanagergethostlist(struct hdfslist *hostlist)
{
. . . . . .
hdfmanagernode = gethdfmanagernode(hcsgetrootnode());
. . . . . .
hostnode = hdfmanagernode->child;
while (hostnode != null) {
struct hdfhostinfo *hostinfo = hdfhostinfonewinstance();
. . . . . .
if (!gethostinfo(hostnode, hostinfo)) {
hdfhostinfofreeinstance(hostinfo);
hostinfo = null;
hostnode = hostnode->sibling;
continue;
}
hostinfo->hostid = hostid;
if (!hdfslistaddorder(hostlist, &hostinfo->node, hdfhostlistcompare)) {
hdfhostinfofreeinstance(hostinfo);
hostinfo = null;
hostnode = hostnode->sibling;
continue;
}
hostid++;
hostnode = hostnode->sibling;
}
return true;
}
我们稍微扩展一点知识来说明一下。在鸿蒙系统中,有一些系统级的配置文件,叫做hcs文件。系统可以利用类似hc-gen这样的工具,根据配置文件生成二进制码。当hdf启动时,它会将二进制信息传给driverconfig模块。该模块会将二进制码转换成配置树,并向开发者提供api去查询这棵树。
配置树的根节点是g_hcstreeroot,节点类型为deviceresourcenode。这棵配置树里有一个特殊的节点,具有“hdf_manager”属性,上面代码中调用gethdfmanagernode()一句,就是在获取这个特殊节点。接着,上面的代码里会尝试遍历该节点的所有child,并将每个child的信息整理进一个hdfhostinfo对象里。注意此时就会给hdfhostinfo分派一个hostid了,这个hostid后续还会用到。所有读出的hdfhostinfo节点会按照其内记录的优先级进行排序,并连成一个列表,优先级越高越靠近表头。
2.1.3.3 遍历hdfhostinfo列表
得到hdfhostinfo列表后,紧接着就会尝试遍历这张表。因为每个hdfhostinfo节点代表的就是一个host,所以每读取一个hdfhostinfo,就会对应地生成一个devhostserviceclnt对象。这些生成的devhostserviceclnt都会插入到devmgrservice的hosts列表中。
每读取一个hdfhostinfo信息后,就会利用驱动安装器,启动对应的host。
2.1.3.4启动host
启动host的动作是installer->startdevicehost()一步,它的调用关系如下:
大家还记得前文我说过,每个host可以拆分成两个部分:devhostserviceclnt 和 devhostservice。这个就体现在上面的调用关系里。
startdevicehost一开始就会创建一个devhostservice对象,
【drivers/hdf/frameworks/core/manager/src/hdf_driver_installer.c】
static int driverinstallerstartdevicehost(uint32_t devhostid, const char *devhostname)
{
struct idevhostservice *hostserviceif = devhostservicenewinstance(devhostid, devhostname);
if ((hostserviceif == null) || (hostserviceif->startservice == null)) {
hdf_loge(hostserviceif or hostserviceif->startservice is null);
return hdf_failure;
}
int ret = hostserviceif->startservice(hostserviceif);
if (ret != hdf_success) {
hdf_loge(start host service failed, ret is: %d, ret);
devhostservicefreeinstance(hostserviceif);
}
return ret;
}
随后调用的startservice,实际上对应devhostservicestartservice()函数:
【drivers/hdf/frameworks/core/host/src/devhost_service.c】
static int devhostservicestartservice(struct idevhostservice *service)
{
struct devhostservice *hostservice = (struct devhostservice*)service;
if (hostservice == null) {
hdf_loge(start device service failed, hostservice is null);
return hdf_failure;
}
return devmgrserviceclntattachdevicehost(hostservice->hostid, service);
}
此处调用的devmgrserviceclntattachdevicehost()函数,内部涉及的内容挺多,我打算在下一篇文档里再细说。现在我们已经对“启动devicemanager”的流程有了一点初步的认识,为了便于理解里面host的部分,我们画一张示意图总结一下,绘图如下:
AGP总线接口定义
向交通运输领域提供高性能北斗模块的SKYLAB
固态电容和钽电容区别
庞大的物联网商机2015即将启动,你准备好了?
高分子电容型湿敏元件应用及至电路
一文解析鸿蒙系统中的HDF架构
芯片去库存!谁先迎来自由曙光
达林顿管的应用及达林顿模块电路典型结构
一种电子电压表电路图
什么是补偿电缆_如何辨认补偿电缆
SD卡面包板插槽DIY图解
中国电信已在八大城市开展了5G SA+NSA混合组网的扩大试点
扩展汽车认证的器件阵容用于汽车功能电子化方案
MAXQ3120电能表基准
行业无龙头、市场空间大,机器视觉大幕已起
中移动“无线城市统一平台”实现全国联网
曝英特尔不仅搞定10nm工艺大规模量产 高性能GPU还将3D显存
5G商用给智能网联汽车带来了什么样的变化?
上海巨微专用蓝牙广播芯片具有更好的实时性和兼容性
云天励飞获2020人工智能行业新基建最佳贡献奖