OpenHarmony HDF 平台驱动框架介绍——GPIO模块适配
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方法实现,以便后续扩展引用计数机制