OpenHarmony HDF 平台驱动框架介绍——GPIO模块适配

技术探索者
发布于 2022-3-16 17:27
浏览
0收藏

上篇指南:OpenHarmony HDF 平台驱动框架介绍——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方法实现,以便后续扩展引用计数机制

 

已于2022-3-16 17:27:56修改
1
收藏
回复
举报
回复
    相关推荐