0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙HDF架构:DeviceManager如何生成所有的host配套设施

鸿蒙系统HarmonyOS 来源:oschina 作者:悠然红茶侯亮 2020-10-14 14:01 次阅读

我们阐述了在启动DeviceManager这个核心服务时,是如何生成所有的host配套设施的,下面我们来进一步剖析细节。

我们已经知道,一个Host对应一个DevHostServiceClnt和一个DevHostService,很明显主要行为都包含在后者内部。当后者启动时,会执行到DriverInstallerStartDeviceHost(),该函数又会调用DevHostServiceStartServie(),这些内容在前一篇文章里都说过。

我们不用去想太多调用细节,反正说起来就是要让一个DevHostServiceClnt和一个DevHostService“挂接”(attach)起来,挂接的动作里会进一步在DevHostService里安装设备驱动。这个挂接动作具体对应的函数就是DevmgrServiceClntAttachDeviceHost()。在上一篇文章里,我们没有展开讲这个函数,现在就从它说起。为了便于阅读,我将挂接动作的调用顺序先绘制出来,如下图所示:

1.挂接device Host

我用黄色框表达了DevmgrServiceClntAttachDeviceHost()一步,该函数代码截选如下:

【drivers/hdf/frameworks/core/host/src/Devmgr_service_clnt.c】

int DevmgrServiceClntAttachDeviceHost(uint16_t hostId, struct IDevHostService *hostService)

{

struct IDevmgrService *devMgrSvcIf = NULL;

。 . 。 . 。 .

devMgrSvcIf = inst-》devMgrSvcIf;

。 . 。 . 。 .

// 实际调用的是DevmgrServiceAttachDeviceHost()

return devMgrSvcIf-》AttachDeviceHost(devMgrSvcIf, hostId, hostService);

}

最后一句实际调用的是DevmgrServiceAttachDeviceHost(),代码截选如下:

【drivers/hdf/frameworks/core/manager/src/Devmgr_service.c】

static int DevmgrServiceAttachDeviceHost(

struct IDevmgrService *inst, uint16_t hostId, struct IDevHostService *hostService)

{

struct DevHostServiceClnt *hostClnt = DevmgrServiceFindDeviceHost(inst, hostId);

。 . 。 . 。 .

hostClnt-》deviceInfos = HdfAttributeManagerGetDeviceList(hostClnt-》hostId, hostClnt-》hostName);

。 . 。 . 。 .

hostClnt-》hostService = hostService;

return DevHostServiceClntInstallDriver(hostClnt);

}

首先,遍历DevmgrService的hosts列表,根据hostId找到对应的DevHostServiceClnt对象,并给该DevHostServiceClnt对象的deviceInfos域和hostService域赋值,然后调用重头戏DevHostServiceClntInstallDriver()。

在获取这个host范畴的所有device信息时,也是去查询上一篇文章提到的配置树,树节点的类型为DeviceResourceNode,只不过上一次系统是去查找具有“hdf_manager”属性的节点,而此次是查找名字为hostName的节点,这个节点里包含着若干设备的信息,现在这些设备信息会被组织成一个HdfDeviceInfo链表。最终形成下面图中的结构:

1.1安装host范畴内的设备驱动

1.1.1在每个host的DevHostService里添加设备

Attach动作的最后一步就是安装驱动啦,我们看一下这个DevHostServiceClntInstallDriver()函数:

【drivers/hdf/frameworks/core/manager/src/Devhost_service_clnt.c】

int DevHostServiceClntInstallDriver(struct DevHostServiceClnt *hostClnt)

{

。 . 。 . 。 .

struct HdfSListIterator it;

struct HdfDeviceInfo *deviceInfo = NULL;

struct IDevHostService *devHostSvcIf = NULL;

。 . 。 . 。 .

devHostSvcIf = (struct IDevHostService *)hostClnt-》hostService;

。 . 。 . 。 .

HdfSListIteratorInit(&it, hostClnt-》deviceInfos);

while (HdfSListIteratorHasNext(&it)) {

deviceInfo = (struct HdfDeviceInfo *)HdfSListIteratorNext(&it);

if ((deviceInfo == NULL) || (deviceInfo-》preload != DEVICE_PRELOAD_ENABLE)) {

continue;

}

// 实际调用的是 DevHostServiceAddDevice()

ret = devHostSvcIf-》AddDevice(devHostSvcIf, deviceInfo);

。 . 。 . 。 .

}

return HDF_SUCCESS;

}

其实就是遍历一下该host范畴内的所有HdfDeviceInfo节点,如果节点的preload是“使能”的,就执行对应的AddDevice操作,即DevHostServiceAddDevice()函数,其代码截选如下:

【drivers/hdf/frameworks/core/host/src/Devhost_service.c】

static int DevHostServiceAddDevice(struct IDevHostService *inst,

const struct HdfDeviceInfo *deviceInfo)

{

int ret = HDF_FAILURE;

struct HdfDevice *device = NULL;

struct HdfDeviceNode *devNode = NULL;

struct DevHostService *hostService = (struct DevHostService *)inst;

struct IDriverLoader *driverLoader = HdfDriverLoaderGetInstance();

。 . 。 . 。 .

device = DevHostServiceGetDevice(hostService, deviceInfo-》deviceId);

。 . 。 . 。 .

// 实际调用的是 HdfDriverLoaderLoadNode()

devNode = driverLoader-》LoadNode(driverLoader, deviceInfo);

。 . 。 . 。 .

devNode-》hostService = hostService;

// 实际调用的是 HdfDeviceAttach()

ret = device-》super.Attach(&device-》super, devNode);

。 . 。 . 。 .

return HDF_SUCCESS;

。 . 。 . 。 .

}

在这个函数里,先调用DevHostServiceGetDevice()尝试从DevHostService的devices列表里查找与deviceId匹配的节点,如果找不到就创建一个新HdfDevice节点,并插入该列表。

当然,一开始devices列表是个空列表,此时只会创建新节点。反正经此一步,我们一般可以拿到一个可用的HdfDevice对象。接着利用驱动加载器加载一个和deviceInfo匹配的HdfDeviceNode节点。最后还需把得到的HdfDevice和HdfDeviceNode挂接起来。

1.1.1.1加载HdfDeviceNode

加载HdfDeviceNode的动作实际上是HdfDriverLoaderLoadNode(),代码截选如下:

【drivers/hdf/frameworks/core/host/src/Hdf_driver_loader.c】

static struct HdfDeviceNode *HdfDriverLoaderLoadNode(

struct IDriverLoader *loader, const struct HdfDeviceInfo *deviceInfo)

{

struct HdfDriverEntry *driverEntry = NULL;

struct HdfDeviceNode *devNode = NULL;

。 . 。 . 。 .

// 实际调用的是 HdfDriverLoaderGetDriverEntry()

driverEntry = loader-》GetDriverEntry(deviceInfo);

。 . 。 . 。 .

devNode = HdfDeviceNodeNewInstance();

。 . 。 . 。 .

devNode-》driverEntry = driverEntry;

devNode-》deviceInfo = deviceInfo;

devNode-》deviceObject.property = HcsGetNodeByMatchAttr(HcsGetRootNode(),

deviceInfo-》deviceMatchAttr);

。 . 。 . 。 .

if ((deviceInfo-》policy == SERVICE_POLICY_PUBLIC) || (deviceInfo-》policy == SERVICE_POLICY_CAPACITY)) {

。 . 。 . 。 .

if (driverEntry-》Bind(&devNode-》deviceObject) != 0) {

HDF_LOGE(“bind driver failed”);

HdfDeviceNodeFreeInstance(devNode);

return NULL;

}

}

return devNode;

}

HdfDeviceNode的定义如下:

【drivers/hdf/frameworks/core/host/include/Hdf_device_node.h】

struct HdfDeviceNode {

struct IDeviceNode super;

struct HdfSListNode entry;

struct PowerStateToken *powerToken;

struct DevHostService *hostService;

struct HdfDeviceObject deviceObject;

struct IHdfDeviceToken *token;

struct HdfDriverEntry *driverEntry;

const struct HdfDeviceInfo *deviceInfo;

};

可以看到,驱动加载器在创建HdfDeviceNode节点时,还是有一些工作要做的:

1)得加载相应设备的驱动程序入口,最终体现为HdfDriverEntry;

2)创建一个HdfDeviceNode对象,经过研究,我们可以看到最终创建的其实是HdfDeviceNode的派生类(DeviceNodeExt)对象;

3)把HdfDeviceNode节点和设备驱动程序绑定起来;

1.1.1.1.1获取驱动入口

驱动加载器获取HdfDriverEntry的实际动作是HdfDriverLoaderGetDriverEntry():

【drivers/hdf/lite/manager/src/Lite_driver_loader.c】

struct HdfDriverEntry *HdfDriverLoaderGetDriverEntry(

const struct HdfDeviceInfo *deviceInfo)

{

int count = (int) (((uint8_t *)(HDF_DRIVER_END()) - (uint8_t *)(HDF_DRIVER_BEGIN())) / sizeof(size_t));

size_t *addrBegin = (size_t*)(HDF_DRIVER_BEGIN());

if ((deviceInfo == NULL) || (deviceInfo-》moduleName == NULL) || (deviceInfo-》svcName == NULL)) {

HDF_LOGE(“Hdf get device entry failed, input deviceInfo is NULL!”);

return NULL;

}

for (int i = 0; i 《 count; i++) {

struct HdfDriverEntry *driverEntry = (struct HdfDriverEntry *)(*addrBegin);

if (strcmp(deviceInfo-》moduleName, driverEntry-》moduleName) == 0) {

return driverEntry;

}

addrBegin++;

}

HDF_LOGE(“Hdf get %s device entry failed!”, deviceInfo-》svcName);

return NULL;

}

其中,HdfDriverEntry的定义如下:

【drivers/hdf/frameworks/include/core/Hdf_device_desc.h】

struct HdfDriverEntry {

int32_t moduleVersion;

const char *moduleName;

int32_t (*Bind)(struct HdfDeviceObject *deviceObject);

int32_t (*Init)(struct HdfDeviceObject *deviceObject);

void (*Release)(struct HdfDeviceObject *deviceObject);

};

现在我们来解释一下,HdfDriverLoaderGetDriverEntry()到底在干什么。我们设想,HDF会先加载需要的所有驱动程序,每个驱动程序内部都会构造一个HdfDriverEntry对象,而且会填好那个Bind域,这其实就是在填写一个回调函数指针,当然,也只有驱动程序自己知道该填写哪个函数指针。

HDF会把加载的所有驱动的HdfDriverEntry对象的起始地址汇总起来,形成一个类似地址数组的东西,这个数组的第一项的地址对应上面代码中的HDF_DRIVER_BEGIN(),最后一项的地址对应HDF_DRIVER_END()(最后一项不填内容)。示意图如下:

获取驱动入口时,就是在遍历这个指针数组,查询与moduleName匹配的节点。

1.1.1.1.2 创建HdfDeviceNode对象

接着尝试创建HdfDeviceNode对象,此时调用的HdfDeviceNodeNewInstance()函数如下:

【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

struct HdfDeviceNode *HdfDeviceNodeNewInstance()

{

return (struct HdfDeviceNode *)HdfObjectManagerGetObject(HDF_OBJECT_ID_DEVICE_SERVICE);

}

又需要去查我们熟悉的对象创建表(g_liteObjectCreators),最终查到会调用DeviceNodeExtCreate():

【drivers/hdf/lite/manager/src/Hdf_device_node_ext.c】

struct HdfObject *DeviceNodeExtCreate()

{

struct DeviceNodeExt *instance =

(struct DeviceNodeExt *)OsalMemCalloc(sizeof(struct DeviceNodeExt));

if (instance != NULL) {

DeviceNodeExtConstruct(instance);

instance-》ioService = NULL;

}

return (struct HdfObject *)instance;

}

可以看到,实际创建的是一个DeviceNodeExt对象。DeviceNodeExt继承于HdfDeviceNode,定义如下:

【drivers/hdf/lite/include/manager/Hdf_device_node_ext.h】

struct DeviceNodeExt {

struct HdfDeviceNode super;

struct HdfIoService *ioService;

};

其构造函数如下:

【drivers/hdf/lite/manager/src/Hdf_device_node_ext.c】

static void DeviceNodeExtConstruct(struct DeviceNodeExt *inst)

{

struct IDeviceNode *nodeIf = (struct IDeviceNode *)inst;

if (nodeIf != NULL) {

HdfDeviceNodeConstruct(&inst-》super);

nodeIf-》PublishService = DeviceNodeExtPublishService;

}

}

注意,它修改了继承来的PublishService域,将函数指针设为DeviceNodeExtPublishService了。

HdfDriverLoaderLoadNode()会给DeviceNodeExt的driverEntry域、deviceInfo域、deviceObject.property赋值,那么在进行绑定之前,DeviceNodeExt的示意图大概是这样的:

1.1.1.1.3 绑定驱动入口

接下来要将刚刚创建的DeviceNodeExt节点和驱动入口绑定起来:

driverEntry-》Bind(&devNode-》deviceObject)

前文我们已经说了,每个程序会实现自己的Bind动作,而HDF只负责回调Bind。注意,回调时HDF需要传入DeviceNodeExt节点的deviceObject部分的指针,因为需要驱动程序填写其中的域。当然,我们从上图中可以看到,deviceObject部分只剩下service域(IDeviceIoService*)需要填写。那么很明显,一个驱动程序要能被HDF使用,那么它就得包含一个IDeviceIoService对象。IDeviceIoService的定义如下:

【drivers/hdf/frameworks/include/core/Hdf_device_desc.h】

struct IDeviceIoService {

struct HdfObject object;

int32_t (*Open)(struct HdfDeviceIoClient *client);

int32_t (*Dispatch)(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data,

struct HdfSBuf *reply);

void (*Release)(struct HdfDeviceIoClient *client);

};

现在我们可以基于前文示意图,绘制一张DeviceNodeExt和驱动程序绑定后的示意图了,如下图:

1.1.1.2 挂接HdfDeviceNode

DevHostServiceAddDevice()在加载好DeviceNodeExt之后,调用了一句Attach:

ret = device-》super.Attach(&device-》super, devNode);

尝试把HdfDevice节点和DeviceNodeExt联系起来,这一句其实是调用HdfDeviceAttach(),相关代码如下:

【drivers/hdf/frameworks/core/host/include/Hdf_device.h】

struct IHdfDevice {

struct HdfObject object;

int (*Attach)(struct IHdfDevice *, struct HdfDeviceNode *);

};

struct HdfDevice {

struct IHdfDevice super;

struct HdfSListNode node;

struct HdfSList services;

uint16_t deviceId;

uint16_t hostId;

};

【drivers/hdf/frameworks/core/host/src/Hdf_device.c】

static int HdfDeviceAttach(struct IHdfDevice *devInst, struct HdfDeviceNode *devNode)

{

struct HdfDevice *device = (struct HdfDevice *)devInst;

struct IDeviceNode *nodeIf = (struct IDeviceNode *)devNode;

。 . 。 . 。 .

HdfSListAdd(&device-》services, &devNode-》entry);

// 实际调用的是 HdfDeviceLaunchNode()

return nodeIf-》LaunchNode(devNode, devInst);

}

代码里先将DeviceNodeExt添加进HdfDevice的services列表里,然后调用了HdfDeviceLaunchNode()。

我们前文已经说过,HdfDevice节点在之前已经添加进DevHostService的devices列表了,现在它又和DeviceNodeExt联系起来了,再结合前文中的知识,我们可以画一张大一点儿的关系示意图了,如下:

至此,相信大家已经基本了解挂接设备host所形成的数据结构了,正如上图所示,每个host都会对应上图中红、绿、蓝三个范畴。大家不妨自己试着画画这张图,看看还会发现什么。至于HDF的其他方面,我们可以在其他文章里再探讨。
编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4304

    浏览量

    62415
  • 设备驱动
    +关注

    关注

    0

    文章

    68

    浏览量

    10875
  • 鸿蒙系统
    +关注

    关注

    183

    文章

    2634

    浏览量

    66204
收藏 人收藏

    评论

    相关推荐

    鸿蒙跨端实践-JS虚拟机架构实现

    类似的框架,我们需要自行实现以确保核心基础能力的完整。 鸿蒙虚拟机的开发经历了从最初 ArkTs2V8 到 JSVM + Roma新架构方案 。在此过程中,我们实现了完整的鸿蒙版的“J2V8”和 基于系统JSVM的JS虚拟机框架
    的头像 发表于 09-30 14:42 2322次阅读
    <b class='flag-5'>鸿蒙</b>跨端实践-JS虚拟机<b class='flag-5'>架构</b>实现

    聚徽触控-多媒体教学一体机需要哪些配套设施

    多媒体教学一体机需要的配套设施主要包括以下几个部分:
    的头像 发表于 07-23 09:44 257次阅读

    ESP32-WROOM-32UE为什么无法扫描所有的2.4G热点?

    id040cfab35db4270c443146b19febf6c59266d419问题点:扫描到的热点数量少(有些无法扫描到),同-环境下主控为imx8rm使用rt18723bu无线芯片能扫到周围所有的2.4G频段热点 2.
    发表于 06-05 06:18

    最新开源代码证实!“鸿蒙原生版”微信正在积极开发中

    , MMKV v1.3.5 首次正式支持 HarmonyOS NEXT, 目前大部分功能在该平台上可正常使用。 据介绍,MMKV 用于保持内存与文件同步,最大限度地利用原生平台来实现最佳性能,所有的更改都会
    发表于 05-08 17:08

    超融合基础架构的软硬件设计思路

    简单来说,超融合基础架构就是在业务节点上运行服务器虚拟化软件和存储虚拟化软件,将所有的计算节点通过网络聚合成一个统一的虚拟计算和虚拟存储资源池,以资源池的形式提供数据中心所需的IT基础设施
    的头像 发表于 04-25 10:28 1625次阅读
    超融合基础<b class='flag-5'>架构</b>的软硬件设计思路

    如何在Cubemx中添加所有的外设头文件?

    ,需要自己去添加相应的头文件,但自己添加有时还是会报错,而且是不知原因的报错。 所以,在这向各位开发者、大咖,请教一下,如何在Cubemx中添加所有的外设头文件,或者在配置完cubemx后如何高效的添加自己所需要的外设头文件
    发表于 04-01 07:46

    NVIDIA Edify多模态架构升级,引领视觉生成式AI新纪元

    NVIDIA近日宣布,其用于视觉生成式AI的多模态架构Edify迎来重大更新,为开发者和视觉内容提供商带来前所未有的新功能。其中,3D资产生成功能的引入,极大地提升了AI图像
    的头像 发表于 03-27 10:22 409次阅读

    SATA3.0 Host Controller IP介绍

    SATA3.0 Host IP不仅实现了SATA协议的PHY(物理层)、link(链路层)和TRN(传输层),并且实现了CMD(命令层)和APP(应用层),支持1.5、3和6Gbps传输速率
    发表于 02-27 15:53 0次下载

    Xilinx FPGA NVMe控制器,NVMe Host Controller IP

    NVMe Host Controller IP可以连接高速存储PCIe SSD,无需CPU和外部存储器,自动加速处理所有的NVMe协议命令,具备独立的数据写入AXI4-Stream/FIFO接口和数
    的头像 发表于 02-18 11:27 844次阅读
    Xilinx FPGA NVMe控制器,NVMe <b class='flag-5'>Host</b> Controller IP

    使用 Taro 开发鸿蒙原生应用 —— 快速上手,鸿蒙应用开发指南

    随着鸿蒙系统的不断完善,许多应用厂商都希望将自己的应用移植到鸿蒙平台上。最近,Taro 发布了 v4.0.0-beta.x 版本,支持使用 Taro 快速开发鸿蒙原生应用,也可将现有的
    的头像 发表于 02-02 16:09 819次阅读
    使用 Taro 开发<b class='flag-5'>鸿蒙</b>原生应用 —— 快速上手,<b class='flag-5'>鸿蒙</b>应用开发指南

    华为鸿蒙系统怎么样 华为鸿蒙系统和安卓系统的区别

    华为鸿蒙系统是华为公司自主研发的全场景分布式操作系统,于2019年8月首次发布。鸿蒙系统不同于传统的操作系统,它以分布式应用架构为基础,能够在各种终端设备上实现流畅的交互体验,并且具备更高的安全性
    的头像 发表于 02-02 14:54 1630次阅读

    鸿蒙OS和开源鸿蒙什么关系?

    开源鸿蒙(Open Harmony) 鸿蒙系统愿来的设计初衷,就是让所有设备都可以运行一个系统,但是每个设备的运算能力和功能都不同,所以内核的设计上,采用了微内核的设计,除了最基础的功能放在
    的头像 发表于 01-30 15:44 1079次阅读
    <b class='flag-5'>鸿蒙</b>OS和开源<b class='flag-5'>鸿蒙</b>什么关系?

    鸿蒙系统和安卓的区别 鸿蒙系统有什么特别之处

    鸿蒙系统是华为公司自主研发的一款全新操作系统,旨在替代安卓系统。鸿蒙系统与安卓系统在多个方面存在不同,下面将详细介绍鸿蒙系统与安卓系统的区别以及鸿蒙系统的特别之处。 一、
    的头像 发表于 01-18 11:45 1.1w次阅读

    鸿蒙物联网基础

    关于物联网架构以及鸿蒙第三方应用库的引用方式
    的头像 发表于 01-08 08:37 498次阅读
    <b class='flag-5'>鸿蒙</b>物联网基础

    excel把所有的NA替换成0

    在Excel中将所有的NA替换为0是一个常见的操作,特别是当我们处理大量数据时。本文将详细介绍如何使用Excel的功能来进行替换,以及一些替代方法。 首先,打开包含NA的Excel表格。NA
    的头像 发表于 12-01 10:04 1.8w次阅读