开源项目 OpenHarmony是每个人的 OpenHarmony
OpenHarmony系统平台驱动概述
OpenHarmony系统平台驱动(PlatformDriver),即平台设备驱动,它用于驱动平台设备(PlatformDevice),为系统及外设驱动提供访接口。这里的平台设备,泛指I2C/UART等总线、以及GPIO/RTC等SOC片内硬件资源。
OpenHarmony系统平台驱动框架是OpenHarmony系统驱动框架的重要组成部分,它基于HDF驱动框架、操作系统适配层(OSAL, operating system abstraction layer)以及驱动配置管理机制,为各类平台设备驱动的实现提供标准模型。
OpenHarmony系统平台驱动框架为外设提供了标准的平台设备访问接口,使其不必关注具体硬件及OS平台;同时为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制。
为实现这个目标,OpenHarmony系统平台驱动框架满足如下特性:
统一的平台设备访问接口:对平台设备操作接口进行统一封装,屏蔽不同SOC平台硬件差异以及不同OS形态差异。
统一的平台驱动适配接口:为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制,而不必关注设备管理及公共业务流程。
提供设备注册、管理、访问控制等与SOC无关的公共能力。
OpenHarmony系统平台驱动框架目前支持的设备类型包括但不限于:I2C/SPI/UART/MIPI_DSI/SDIO/GPIO/PWM/WATCHDOG/RTC/DMA
OpenHarmony平台驱动框架介绍
OpenHarmony系统平台驱动框架组成
OpenHarmony系统平台驱动框架主要由平台接口层、平台核心层以及平台适配层三个部分组成。
1)平台接口层 以API的形式提供标准的平台设备访问接口。
平台接口层以设备句柄加配套API的形式对外提供统一的、标准的访问接口。
设备句柄是DevHandle类型的实例,通过不同设备模块提供的Open/Close方法进行获取、释放。成功获取设备句柄后,即可使用相应的API执行设备操作。例如通过I2cTransfer完成一次I2C数据传输。
这是一种代理模式,即接口层API不直接引用实际设备对象,而是通过DevHandle作为代理,间接访问设备;而所有来自外设驱动的访问,都建议走接口层,以获得最佳的稳定性。
不同类型设备的API使用,请参考如下官方文档的平台驱动章节:
https://device.OpenHarmony系统.com/cn/docs/develop/drive/oem_drive_hdfdev-0000001051715456
2)平台核心层 提供平台设备模型及公共业务框架。
提供统一适配接口:定义了标准的设备对象模型,驱动程序仅需关注标准对象模型的适配。
抽取公共业务框架:将不同设备模块的公共流程、算法加以抽取,使得具体设备驱动更加轻薄。
设备管理:设备注册、注销、设备查找、访问控制。
3)平台适配层 提供特定平台设备的适配驱动,并遵守核心层约束。
驱动具体平台设备硬件,并创建对应的设备模型对象,注册到核心层纳入统一管理。
平台接口层分析
前面说过,在接口层,我们用DevHandle类型的设备句柄表示一个平台设备对象,然后针对不同类型设备提供一套标准的API方法用于设备访问。那么设备句柄和真实的设备对象如何关联呢?
查看DevHandle的定义,发现它就是一个void类型指针:
/**
* @brief Defines the common device handle of the platform driver.
*
* The handle is associated with a specific platform device and is used as the
* first input parameter for all APIs of the platform driver.
*
* @since 1.0
*/
typedef void* DevHandle;
实际上,在内核态,这个指针可以直接指向实际设备对象,但是对于某些类型的平台设备,我们需要在用户态提供同样的DevHandle类型及配套API,而实际设备对象在内核空间,我们无法直接获取和使用内核空间的地址。
我们的解决办法是将平台设备对象实现为一个HDF设备服务,这样借助HDF DeviceManager的设备服务机制,可以在用户态、内核态同时获取到设备服务,而用户态同内核态通信的问题交由HDF DeviceManager处理。此时,DevHandle只需要关联到这个设备服务即可,而void*类型保证了足够的灵活性。
根据DevHandle和设备对象关联方式的不同,接口层的设计有三种模式,下面将一一讲解。
1.独立服务模式
这种模式用于需要在用户态和内核态同时提供API的设备类型,DevHandle同设备对象的关联方式为:
用户态:关联到平台设备对应的设备服务
内核态度:关联到实际平台设备对象或其设备服务(在内核态两者可互相转换)
这样,每一个设备对象,会独立发布一个设备服务,来处理外部访问,服务收到API的访问请求之后,通过提取请求参数,并调用实际设备对象的相应内部方法。
这种方式的优点是管理比较简单,因为它借助了HDF DeviceManager的服务管理能力;但是缺点是需要为每一个设备对象配置设备节点,以便发布设备服务。
这种模式的典型实践是UART模块,这在后面介绍驱动适配时会详细介绍。
2.统一服务模式
有时候,同一类型的设备对象可能会很多,例如I2C模块,可能同时有十几个控制器。如果采用独立服务的模式,每一个控制器,作为一个平台设备,为其创建一个设备服务,那么将会有十几个服务被创建,不光要配置很多设备节点,而且这些服务还会占用内存资源。
这时,我们可以为一类设备对象,创建一个平台设备管理器(PlatformManager)对象,并同时对外发布一个管理器服务,由这个管理器服务来统一处理外部访问。当用户需要打开某个设备时,先通过HDF DeviceManager获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备,并返回一个设备描述符,而这个描述符仍然可以由DevHandle类型表示。
这种模式的实践代表是I2C模块,PlatformManager实现为I2cManager,而PlatformDevice则是I2cCntlr,感兴趣的读者可以阅读一下drivers/framework下的i2c_if.c/i2c_core.c一探究竟。
3.无服务模式
这种模式用于不需要在用户态提供API的设备类型或者没有用户态、内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址。而PlatformManager的实现比较自由,它不需要实现设备服务,只需做好某种类型的设备管理即可,甚至在C语言中,由于无法进行OOP编程,很多模块直接将这个功能面向过程化了,使得没有一个具体的结构体与之对应。但是,我们仍然强调PlatformManager这个概念,也期望随着后续平台驱动框架的演进,逐步完善、规范化,形成更加统一的编程风格。
平台核心层分析
平台核心层的作用是承上启下,其主要内容包括:
提供适配接口:为具体的平台设备驱动提供统一的适配接口
平台驱动框架为不同设备类型,定义了标准的设备对象模型,具体设备驱动只需要关注标准设备对象的适配即可
提供设备管理:提供设备的注册、注销、查找等功能、访问控制等能力
核心层会提供一系列内部方法,用于设备的注册、注销,设备对象的查找、获取、释放,以及处理多线程访问。例如当我们向核心层注册一个I2C控制器对象时,使用I2cCntlrAdd;当希望获取一个I2C控制器对象时,通过I2cCntlrGet并指定控制器编号;当不再使用这个对象时,还需要通过I2cCntlrPut释放。这样做的好处是将每一个具体的操作步骤高度抽象化,减小同平台接口层及平台适配层的耦合面,便于业务解耦、演进。如果后续,我们由于业务需求需要对I2cCntlr对象进行引用计数,那么只需要修改I2cCntlrGet/Put这对方法的实现即可,并不会影响平台接口层和平台适配层。
公共业务实现:抽取公共的业务流程、算法
凡是跟特定硬件无关的业务逻辑,都会被抽取到核心层,例如RTC时钟的时间格式转换算法,GPIO模块的线程中断实现等等。
平台适配层实现
适配层提供具体平台硬件设备的驱动,按照核心层定义的模型创建设备对象,并完成对象的初始化(包括必要的成员变量初始化以及钩子方法挂接,以及相关的硬件初始化操作),最后使用核心层提供的注册方法将设备对象注册到核心层纳入统一管理。
OpenHarmony系统平台驱动适配介绍
下面以uart/i2c/gpio三个典型模块为例介绍平台驱动适配的一般方法
UART模块的适配
UART模块适配的核心环节,是UartHost对象的创建、初始化及注册。
UART模块采用的是独立服务模式,要求每一个UartHost对象关联一个HDF设备服务,因此:
1). device_info.hcs中为每一个UART控制器配置一个HDF设备节点
device_uart :: device {
device0 :: deviceNode {
policy = 1;
priority = 40;
permission = 0644;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_0";
deviceMatchAttr = "hisilicon_hi35xx_uart_0";
}
device1 :: deviceNode {
policy = 2;
permission = 0644;
priority = 40;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_1";
deviceMatchAttr = "hisilicon_hi35xx_uart_1";
}
}
服务policy大于等于1(如需对用户态可见为2,仅内核态可见为1);
moduleName需要与驱动Entry中moduleName 保持一致;
ServiceName必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号
deviceMatchAttr用于配置控制器私有数据,要与uart_config.hcs中对应控制器保持一致,如不需要则忽略
2). uart_config.hcs中为每一个UART控制器配置私有数据
如果控制器需要配置一些私有数据,例如寄存器基地址,初始化波特率等等,可以在uart_config.hcs中配置,该文件将在产品配置目录的hdf.hcs中导入,具体路径可由产品自由配置。
root {
platform {
template uart_controller {
match_attr = "";
num = 0;
baudrate = 115200;
fifoRxEn = 1;
fifoTxEn = 1;
flags = 4;
regPbase = 0x120a0000;
interrupt = 38;
iomemCount = 0x48;
}
controller_0x120a0000 :: uart_controller {
match_attr = "hisilicon_hi35xx_uart_0";
}
controller_0x120a1000 :: uart_controller {
num = 1;
baudrate = 9600;
regPbase = 0x120a1000;
interrupt = 39;
match_attr = "hisilicon_hi35xx_uart_1";
}
}
要注意的一点是每个控制器要独立配置一个uart_controller节点,并且其match_attr要与device_info.hcs中的deviceMatchAttr一致。
3).驱动的Entry结构需要有Bind方法,用于绑定服务
struct HdfDriverEntry g_hdfUartDevice = {
.moduleVersion = 1,
.moduleName = "HDF_PLATFORM_UART",
.Bind = HdfUartDeviceBind,
.Init = HdfUartDeviceInit,
.Release = HdfUartDeviceRelease,
};
HDF_INIT(g_hdfUartDevice);
注意:在Bind方法中,要使用UartHostCreate创建控制器对象并完成服务绑定。
static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device)
{
HDF_LOGI("%s: entry", __func__);
if (device == NULL) {
return HDF_ERR_INVALID_OBJECT;
}
return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
}
4).UartHostCreate的实现屏蔽了一些细节
struct UartHost *UartHostCreate(struct HdfDeviceObject *device)
{
struct UartHost *host = NULL;
host = (struct UartHost *)OsalMemCalloc(sizeof(*host));
host->device = device;
device->service = &(host->service);
host->device->service->Dispatch = UartIoDispatch;
OsalAtomicSet(&host->atom, 0);
host->priv = NULL;
host->method = NULL;
return host;
}
该方法中将UartHost对象同HdfDeviceObject进行了关联:关键环节是为HdfDeviceObject的service成员进行赋值,使其指向UartHost的IDeviceIoService类型的成员对象;同时为service成员的Dispatch方法赋值。这样:
为HdfDeviceObject对象绑定了IDeviceIoService类型的服务对象
UartHost和其IDeviceIoService类型的成员对象service可以相互转换
通过UartHost对象即可获取HdfDeviceObject对象
通过HdfDeviceObject对象即可间接获取UartHost对象(先获取service再转为host)
5).在Init方法中完成UartHost对象的初始化
int32_t HdfUartDeviceInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct UartHost *host = NULL;
host = UartHostFromDevice(device);
ret = Hi35xxAttach(host, device);
host->method = &g_uartHostMethod;
return ret;
}
这里通过UartHostFromDevice从HdfDeviceObject对象获取之前关联的UartHost对象,然后调用Hi35xxAttach方法完成host对象的初始化
最后为host对象挂接钩子方法,这里不再一一分析这些钩子方法,建议读者直接去查看源码。
struct UartHostMethod g_uartHostMethod = {
.Init = Hi35xxInit,
.Deinit = Hi35xxDeinit,
.Read = Hi35xxRead,
.Write = Hi35xxWrite,
.SetBaud = Hi35xxSetBaud,
.GetBaud = Hi35xxGetBaud,
.SetAttribute = Hi35xxSetAttribute,
.GetAttribute = Hi35xxGetAttribute,
.SetTransMode = Hi35xxSetTransMode,
.pollEvent = Hi35xxPollEvent,
};
总结
UART适配的关键是要在驱动Entry的Bind方法中创建UartHost对象,而且是使用UartHostCreate创建。这个创建动作同时也是注册的动作,因为它将UartHost以HDF设备服务的形式同HdfDeviceObject进行绑定,这样就完成了服务的发布,HDF Manager对设备服务的管理也就是对UartHost的管理,核心层可以通过HDF提供的服务获取接口来访问UartHost。
UART适配采用独立服务模式,每一个UartHost对象同时也是一个设备服务,其优点是可以直接利用HDF Manager进行管理;缺点是需要在device_info.hcs为每一个UartHost对象定义设备节点。
说明
UART模块适配涉及到的代码示例片段来自device/hisilicon/drivers/uart/
I2C模块适配
I2C模块适配的核心环节是I2cCntlr对象的创建、初始化及注册。
I2C采用的是统一服务模式,需要一个设备服务来作为I2C模块的管理器,统一处理外部访问。
1、 device_info.hcs配置
device_i2c :: device {
device0 :: deviceNode {
policy = 2;
priority = 50;
permission = 0644;
moduleName = "HDF_PLATFORM_I2C_MANAGER";
serviceName = "HDF_PLATFORM_I2C_MANAGER";
deviceMatchAttr = "hdf_platform_i2c_manager";
}
device1 :: deviceNode {
policy = 0;
priority = 55;
permission = 0644;
moduleName = "hi35xx_i2c_driver";
serviceName = "HI35XX_I2C_DRIVER";
deviceMatchAttr = "hisilicon_hi35xx_i2c";
}
}
首先第一个设备节点必须是I2C管理器,其各项参数必须如上一样设置。其中:
policy:这个同UART,具体配置为1或2取决于是否对用户态可见
moduleName: 固定为HDF_PLATFORM_I2C_MANAGER
serviceName: 固定为HDF_PLATFORM_I2C_MANAGER
deviceMatchAttr: 没有使用,可忽略
而从第二个设备节点开始,配置具体I2C控制器信息。这里device1 并不表示某一路I2C控制器,而是一个资源性质设备,用于描述一类I2C控制器的信息。
服务policy等于0,不需要发布服务;
moduleName用于指定驱动成语,需要与期望的驱动Entry中的moduleName一致;
ServiceName不需要使用,可忽略;
deviceMatchAttr用于配置控制器私有数据,要与i2c_config.hcs中对应控制器保持
同样,具体的控制器信息在i2c_config.hcs中,由具体产品在hdf.hcs中导入。
2、i2c_config.hcs中可配置多个控制器信息
root {
platform {
i2c_config {
match_attr = "hisilicon_hi35xx_i2c";
template i2c_controller {
bus = 0;
reg_pbase = 0x120b0000;
reg_size = 0xd1;
irq = 0;
freq = 400000;
clk = 50000000;
}
controller_0x120b0000 :: i2c_controller {
bus = 0;
}
controller_0x120b1000 :: i2c_controller {
bus = 1;
reg_pbase = 0x120b1000;
}
}
}
}
可以看到,这里配置了多个控制器的信息,而这些信息将在驱动程序员进行逐一解析、处理,生成对应的I2cCntlr对象。
3、I2C管理器服务的驱动由核心层实现
struct HdfDriverEntry g_i2cManagerEntry = {
.moduleVersion = 1,
.Bind = I2cManagerBind,
.Init = I2cManagerInit,
.Release = I2cManagerRelease,
.moduleName = "HDF_PLATFORM_I2C_MANAGER",
};
HDF_INIT(g_i2cManagerEntry);
驱动适配人员无需关注这个驱动的实现,有兴趣的可以去阅读源码。
drivers/framework/support/platform/src/i2c_core.c
4、适配驱动Entry只需要实现Init方法
struct HdfDriverEntry g_i2cDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxI2cInit,
.Release = Hi35xxI2cRelease,
.moduleName = "hi35xx_i2c_driver",
};
HDF_INIT(g_i2cDriverEntry);
static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device)
{
int32_t ret;
const struct DeviceResourceNode *childNode = NULL;
ret = HDF_SUCCESS;
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = Hi35xxI2cParseAndInit(device, childNode);
if (ret != HDF_SUCCESS) {
break;
}
}
return ret;
}
在Init方法中会将i2c_config.hcs中定义的每一个节点取出,分别进行初始化
5、Hi35xxI2cParseAndInit里面很自由
static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{
int32_t ret;
struct Hi35xxI2cCntlr *hi35xx = NULL;
(void)device;
hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx));
ret = Hi35xxI2cReadDrs(hi35xx, node);
hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize);
Hi35xxI2cCntlrInit(hi35xx);
hi35xx->cntlr.priv = (void *)node;
hi35xx->cntlr.busId = hi35xx->bus;
hi35xx->cntlr.ops = &g_method;
hi35xx->cntlr.lockOps = &g_lockOps;
(void)OsalSpinInit(&hi35xx->spin);
ret = I2cCntlrAdd(&hi35xx->cntlr);
....
}
Hi35xxI2cParseAndInit会处理一个具体的I2C控制器的初始化工作,包括:
1)I2cCntlr对象的分配,Hi35xxI2cCntlr头部内嵌了一个I2cCntlr,这是一种继承。
struct Hi35xxI2cCntlr {
struct I2cCntlr cntlr;
OsalSpinlock spin;
volatile unsigned char *regBase;
uint16_t regSize;
int16_t bus;
uint32_t clk;
uint32_t freq;
uint32_t irq;
uint32_t regBasePhy;
};
2)在Hi35xxI2cReadDrs中完成节点属性的读取,并填充进hi35xx对象
3)映射寄存器基地址OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize);
4)调用Hi35xxI2cCntlrInit完成控制器的初始化
5)I2cCntlr对象的填充及调用I2cCntlrAdd注册
这里唯一形式化约束是必须创建一个合法的I2cCntlr并调用I2cCntlrAdd注册到核心层。其他Hi35xx开头的函数都是驱动适配者自由封装的,并无形式化要求。
总结
采用统一服务模式,其优点是不用为每一个I2C控制器定义一个设备节点,控制器对象的创建和注册比较自由;而缺点是要创建一个I2C管理器服务,以及一个虚拟的资源描述设备。
说明
I2C模块适配涉及到的代码示例片段来自device/hisilicon/drivers/i2c/
GPIO模块适配
GPIO模块由于目前不向用户态提供能力,所以不需要发布设备服务。其hcs配置同uart相似,这里不再赘述,唯一不同是device_info.hcs中设备节点的policy为0,表示不发布设备服务。
device_gpio :: device {
device0 :: deviceNode {
policy = 0;
priority = 10;
permission = 0644;
moduleName = "hisi_pl061_driver";
deviceMatchAttr = "hisilicon_hi35xx_pl061";
}
}
同样的原因,GPIO的驱动Entry不需要实现Bind方法,仅需要在Init方法中创建并初始化一个GpioCntlr,再调用GpioCntlrAdd完成注册即可。
struct HdfDriverEntry g_gpioDriverEntry = {
.moduleVersion = 1,
.Bind = Pl061GpioBind,
.Init = Pl061GpioInit,
.Release = Pl061GpioRelease,
.moduleName = "hisi_pl061_driver",
};
HDF_INIT(g_gpioDriverEntry);
这里虽然给Bind方法赋值,但其实现为空。
static int32_t Pl061GpioInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct Pl061GpioCntlr *pl061 = &g_pl061;
ret = Pl061GpioReadDrs(pl061, device->property);
pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);
ret = Pl061GpioInitCntlrMem(pl061);
pl061->cntlr.count = pl061->groupNum * pl061->bitNum;
pl061->cntlr.priv = (void *)device->property;
pl061->cntlr.ops = &g_method;
pl061->cntlr.device = device;
ret = GpioCntlrAdd(&pl061->cntlr);
}
Init方法里面注意是对GpioCntlr的初始化,以及通过GpioCntlrAdd完成注册。这里Pl061GpioCntlr内嵌了一个GpioCntlr,又是继承。
GpioCntlr的钩子方法实现,请感兴趣的读者自己去阅读源码,这里仅对每个钩子方法作用做简单注释:
static struct GpioMethod g_method = {
.request = NULL, //暂时不用
.release = NULL, //暂时不用
.write = Pl061GpioWrite, //写管脚
.read = Pl061GpioRead, //读管脚
.setDir = Pl061GpioSetDir, //设置管脚方向
.getDir = Pl061GpioGetDir, //获取管脚方向
.toIrq = NULL, //暂时不用
.setIrq = Pl061GpioSetIrq, //设置管脚中断段,如不具备此能力可忽略
.unsetIrq = Pl061GpioUnsetIrq, //取消管脚中断设置,如不具备此能力可忽略
.enableIrq = Pl061GpioEnableIrq, //使能管脚中断,如不具备此能力可忽略
.disableIrq = Pl061GpioDisableIrq, //禁止管脚中断,如不具备此能力可忽略
};
总结
GPIO适配在hcs配置及驱动Entry编写上比I2C/UART都简单,但是无法向用户态提供能力支撑。
最后,对于其他模块的适配,基本都是这三种方式之一,读者可集合核心层代码及现有驱动案例对号入座,采取相应的适配方式。
说明:GPIO模块适配涉及到的代码示例片段来自device/hisilicon/drivers/gpio/
OpenHarmony系统平台驱动适配Linux内核
不知道读者有没有思考过,如果在Linux内核下,我们如何适配一个平台设备呢?因为HDF驱动框架是跨平台的,所以我们仍然可以按照前面介绍的方式进行驱动适配。但是,Linux内核有大量现存驱动,它们一起支撑着大量SOC芯片,如果要将这些驱动按照HDF的方式重新适配,且不说难度,就工作量来说是巨大的。
那么有没有办法将这些现存驱动利用起来呢?答案就是适配器模式,它可以将一个类的接口转换成用户希望的另外一个接口,通过增加一点点工作量,使得现有对象在新环境中应用起来。在Linux内核,HDF驱动框架时新环境,而Linux原生的设备对象,例如i2c_adapter, spi_dev,gpio_chip等等,都是旧对象,我们需要通过适配器模式,将其适配成HDF平台驱动框架定义的I2cCntlr、SpiCntlr、GpioCntlr等新对象,从而快速将其应用起来。
具体的适配方式是,针对Linux内核每一种平台设备,提供一个适配驱动,将Linux原生设备对象封装成HDF平台驱动框架定义的设备对象,其他流程同正常硬件驱动适配一样,由于是直接基于现有对象封装,硬件初始化工作,甚至hcs配置都可以省略。
以I2C为例,在Linux下,我们将I2cCntlr直接关联到i2c_adapter,而其钩子方法的实现也是通过调用i2c_adapter相应的配套方法实现的,其对应关系如下:
HDF平台驱动 | Linux内核 | 说明 |
I2cCntlr | i2c_adapter | 通过I2cCntlr的priv成员关联 |
I2cTransfer | i2c_transfer | |
I2cMsg | i2c_msg | 需要进行参数转换 |
有兴趣的读者,可以通过阅读下面的源码,来了解Linux内核下对各类平台设备的适配。
drivers/adapter_del/khdf/linux/platform
总结与展望
OpenHarmony系统平台驱动框架基于HDF驱动框架,为外设驱动提供标准的平台设备访问接口,同时为平台设备驱动提供统一的适配接口,使得外设驱动仅需“关心”自身业务,而使设备驱动仅“关心”自身硬件。
为此,平台驱动框架采用平台接口层、平台核心层、平台适配层的三层结构,并抽象出平台设备(PlatformDevice)、平台设备管理器(PlatformManager)、等概念,形成平台驱动框架特有的编程风格和设计思想。然而有些概念和思想,在框架实现中并不明显,随着架构的演进,有些概念将会越来越明确,他们包括:
统一的平台设备对象模型:目前各类型的设备对象模型,由各模块自己定义,例如I2cCntlr、UartHost,他们并没有公共的父类型,而这一公共类型,是必要的。所以,将来会抽象出PlatformDevice这一公共设备对象模型。
设备管理器:每一类设备有自己的管理器,它即充当容器,也充当管理者,在需要用户态支持的情况下,还充当设备服务。虽然目前没有明确的数据结构与之对应,但是随着架构演进,这个概念的体现一定会愈加明确。
引用计数:设备对象需要进行生命周期管理,设备的获取和释放需要计数,虽然当前没有实现这个机制,但是我们要求每一类设备的获取通过Get/Put方法实现,以便后续扩展引用计数机制
编辑:jq
全部0条评论
快来发表一下你的评论吧 !