一、平台驱动概述

    OpenHarmony平台驱动(Platform Driver),即平台设备(Platform Device)驱动,为系统及外设驱动提供访问接口。这里的平台设备,泛指I2C/UART等总线、以及GPIO/RTC等特定硬件资源。平台驱动框架是OpenHarmony驱动框架的重要组成部分,它基于HDF驱动框架、操作系统适配层以及驱动配置管理机制,为各类平台设备驱动的实现提供标准模型。平台驱动框架为外设提供了标准的平台设备访问接口,使其不必关注具体硬件;同时为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制。

    不同的芯片平台都会提供一些基础的硬件操控能力,比如I2C、UART等,Openharmony驱动框架对这些平台相关的硬件操控能力进行了统一的适配和抽象,给开发者提供操作设备的接口,屏蔽了具体硬件之间的差异。

平台驱动框架提供如下特性:

  • 统一的平台设备访问接口:对平台设备操作接口进行统一封装,屏蔽不同SoC平台硬件差异以及不同OS形态差异。

  • 统一的平台驱动适配接口:为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制,而不必关注设备管理及公共业务流程。

  • 提供设备注册、管理、访问控制等与SoC无关的公共能力。

图 1 驱动架构图

 

平台驱动运作机制

统一服务模式:ADC、DAC、GPIO、I2C、I3C、PIN

独立服务模式:HDMI、MMC、PWM、Regulator、RTC、SDIO、SPI、UART、WatchDog

无服务模式:MIPI-CSI、MIPI-DSI

二、统一服务模式——I2C

1、概述

I2C以主从方式工作,通常有一个主设备和一个或者多个从设备,主从设备通过SDA(SerialData)串行数据线以及SCL(SerialClock)串行时钟线两根线相连

I2C数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。数据传输以字节为单位,高位在前,逐个bit进行传输。

I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址,当主设备需要和某一个从设备通信时,通过广播的方式,将从设备地址写到总线上,如果某个从设备符合此地址,将会发出应答信号,建立传输。

2、运行机制

I2C控制器会出现很多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个设备时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备。I2C管理器服务的驱动由核心层实现。

在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型控制器/配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。

在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。

图2 平台驱动统一服务模式结构图

3、I2C模块各分层的作用

  • 接口层:提供打开设备,数据传输以及关闭设备的能力。

  • 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。

  • 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。

图 3 I2C时序流程图

4、I2C驱动启动过程

图 4 I2C驱动启动过程

    在驱动启动阶段,首先启动i2cManager驱动,Bind方法创建了一个i2cManager管理器,并对外发布dispatch服务用来统一处理外部访问,在i2c控制器驱动中就不需要再发布服务了;然后i2cDriver驱动启动,在Init方法中首先遍历系统中所有的i2c_adapter,对所有探测到的i2c_adapter执行LinuxI2cProbe回调,在回调中首先创建一个i2cCntlr对象,并读取i2c_adapter中对应信息绑定到i2cCntlr对象,然后给i2cCntlr对象绑定具体的I2cMethod方法,最后将初始化好的i2cCntlr对象添加到i2cManager中进行统一管理。

//vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs
device_i2c :: device {
    device0 :: deviceNode {
        policy = 2;//驱动服务发布的策略,I2C管理器具体配置为2,表示驱动对内核态和用户态都发布服务
        priority = 50;//驱动启动优先级(0-200),值越大优先级越低。I2C管理器具体配置为50
        permission = 0644;//驱动创建设备节点权限,I2C管理器具体配置为0664
        moduleName = "HDF_PLATFORM_I2C_MANAGER";//驱动名称,I2C管理器固定为HDF_PLATFORM_I2C_MANAGER
        serviceName = "HDF_PLATFORM_I2C_MANAGER";//驱动对外发布服务的名称,I2C管理器服务名设置为HDF_PLATFORM_I2C_MANAGER
        deviceMatchAttr = "hdf_platform_i2c_manager";//驱动私有数据匹配的关键字,I2C管理器设置为hdf_platform_i2c_manager
    }
    device1 :: deviceNode {
        policy = 0; //等于0,不需要发布服务。
        priority = 55;//驱动启动优先级。
        permission = 0644;
        moduleName = "linux_i2c_adapter";
        deviceMatchAttr = "linux_i2c_adapter";
    }
}
  • 根据配置文件中的描述,HDF_PLATFORM_I2C_MANAGER驱动先启动,然后linux_i2c_adapter驱动启动。

//drivers/hdf_core/framework/support/platform/src/i2c/i2c_core.c
//i2cManager驱动入口
struct HdfDriverEntry g_i2cManagerEntry = {
    .moduleVersion = 1,
    .Bind = I2cManagerBind,
    .Init = I2cManagerInit,
    .Release = I2cManagerRelease,
    .moduleName = "HDF_PLATFORM_I2C_MANAGER",
};
HDF_INIT(g_i2cManagerEntry);
//驱动启动后会首先执行Bind函数,在Bind函数中创建I2cManager结构体对象并分配空间,然后给服务绑定dispatch方法用来处理用户态发下来的消息
static int32_t I2cManagerBind(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct I2cManager *manager = NULL;

    HDF_LOGI("I2cManagerBind: enter!");
    if (device == NULL) {
        HDF_LOGE("I2cManagerBind: device is null!");
        return HDF_ERR_INVALID_OBJECT;
    }

    manager = (struct I2cManager *)OsalMemCalloc(sizeof(*manager));
    if (manager == NULL) {
        HDF_LOGE("I2cManagerBind: malloc manager fail!");
        return HDF_ERR_MALLOC_FAIL;
    }

    ret = OsalMutexInit(&manager->lock);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("I2cManagerBind: mutex init fail, ret: %d!", ret);
        OsalMemFree(manager);
        return HDF_FAILURE;
    }

    manager->device = device;
#if defined(LOSCFG_USER_I2C_SUPPORT) || defined(CONFIG_USER_I2C_SUPPORT)
    device->service = &manager->service;
    device->service->Dispatch = I2cManagerDispatch;
#endif // USER_I2C_SUPPORT
    g_i2cManager = manager;
    return HDF_SUCCESS;
}
//dispatch方法中根据用户态传过来的命令再调用对应的函数
static int32_t I2cManagerDispatch(
    struct HdfDeviceIoClient *client, int cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    int32_t ret;

    (void)client;
    switch (cmd) {
        case I2C_IO_OPEN:
            return I2cManagerIoOpen(data, reply);
        case I2C_IO_CLOSE:
            return I2cManagerIoClose(data, reply);
        case I2C_IO_TRANSFER:
            return I2cManagerIoTransfer(data, reply);
        default:
            HDF_LOGE("I2cManagerDispatch: cmd %d is not support!", cmd);
            ret = HDF_ERR_NOT_SUPPORT;
            break;
    }
    return ret;
}
  • 然后i2cDriver驱动启动

//i2cDriver驱动入口
struct HdfDriverEntry g_i2cLinuxDriverEntry = {
    .moduleVersion = 1,
    .Bind = LinuxI2cBind,
    .Init = LinuxI2cInit,
    .Release = LinuxI2cRelease,
    .moduleName = "linux_i2c_adapter",
};
HDF_INIT(g_i2cLinuxDriverEntry);
//驱动启动后,先执行Bind函数,这个函数中没有什么操作,因为i2c采用的是统一服务模式,只需要在i2c管理器中绑定一个服务统一处理外部访问,因此设备不需要再发布服务。
//然后执行Init函数,在Init函数中会调用i2c_for_each_dev方法遍历系统中所有i2c控制器,并为每个i2c控制器调用一次LinuxI2cProbe回调
static int32_t LinuxI2cInit(struct HdfDeviceObject *device)
{
    int32_t ret;

    HDF_LOGI("LinuxI2cInit: enter!");
    if (device == NULL) {
        HDF_LOGE("LinuxI2cInit: device is null!");
        return HDF_ERR_INVALID_OBJECT;
    }

    ret = i2c_for_each_dev(NULL, LinuxI2cProbe);
    HDF_LOGI("LinuxI2cInit: done!");
    return ret;
}
//在LinuxI2cProbe中给所有的i2c adapter分配一个I2cCntlr结构体对象,并读取adapter身上的属性保存到cntlr对象中,包括总线编号,adapter指针,以及核心层定义适配层实现的i2cMethod,使上层可以。。。、、、、、//
//最后调用I2cCntlrAdd将cntlr对象添加到i2c管理器进行统一管理
static int LinuxI2cProbe(struct device *dev, void *data)
{
    int32_t ret;
    struct I2cCntlr *cntlr = NULL;
    struct i2c_adapter *adapter = NULL;

    (void)data;

    if (dev == NULL) {
        HDF_LOGE("LinuxI2cProbe: dev is null!");
        return HDF_ERR_INVALID_OBJECT;
    }

    if (dev->type != &i2c_adapter_type) {
        return HDF_SUCCESS; // continue probe
    }

    HDF_LOGI("LinuxI2cProbe: enter!");
    adapter = to_i2c_adapter(dev);
    cntlr = (struct I2cCntlr *)OsalMemCalloc(sizeof(*cntlr));
    if (cntlr == NULL) {
        HDF_LOGE("LinuxI2cProbe: malloc cntlr fail!");
        i2c_put_adapter(adapter);
        return HDF_ERR_MALLOC_FAIL;
    }

    cntlr->busId = adapter->nr;
    cntlr->priv = adapter;
    cntlr->ops = &g_method;
    ret = I2cCntlrAdd(cntlr);
    if (ret != HDF_SUCCESS) {
        i2c_put_adapter(adapter);
        OsalMemFree(cntlr);
        cntlr = NULL;
        HDF_LOGE("LinuxI2cProbe: add controller fail, ret: %d!", ret);
        return ret;
    }
    HDF_LOGI("LinuxI2cProbe: i2c adapter %d add success!", cntlr->busId);
    return HDF_SUCCESS;
}

5、I2C接口调用流程

图5 I2C接口调用流程图

图6 I2CTransfer读操作详细流程分析:

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐