我们阐述了在启动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的其他方面,我们可以在其他文章里再探讨。
全网最详细的分布式一致性方案讲解
游戏帧率翻倍AMD演示双路显卡神优化
5G时代即将到来,如何让芯片变得足够小
关于我们对电缆常见理解误区的详细解释
英国投资5600万英镑用于南非电池存储
鸿蒙HDF架构:DeviceManager如何生成所有的host配套设施
2018年全球最权威的汽车零部件供应商排行榜,你了解吗?
曼彻斯特大学研究人员制造出油墨可印刷2D半导体
单刀双掷开关的设计理论和方法及在无线局域网中的应用研究
玩出新花样,拥有三个接口的U盘!
研华工控机型号大全_研华工控机选型
电气火灾监控装置套什么定额
电子管的Tuning Indicator——调谐指示管
智能垃圾桶的制作教程
英国皇家国家剧院计划为2019年度所有的表演 全面提供AR字幕眼镜服务
NVIDIA 知乎精彩问答甄选 | 了解 NVIDIA 生成式 AI 相关技术如何驱动各行业发展
可快速储存热量的创新技术 为电动汽车座舱供暖
Ultra-Thin DC-DC Converters Su
内网渗透:获取Windows内Hash密码的方法
上京东预定vivo新机可享众多权益!vivo Z6和iQOO 3等你pick