想了解如何提供 API,我们先看看 CMN 模块是如何使用 API 的,在文件module/cmn700/src/mod_cmn700.c我们可以看到如下代码:

// module/cmn700/src/mod_cmn700.c
static struct mod_system_info_get_info_api *system_info_api;

int cmn700_start(fwk_id_t id)
{
    ...
    status = system_info_api->get_system_info(&system_info);
    if (status == FWK_SUCCESS) {
        chip_id = system_info->chip_id;
        multi_chip_mode = system_info->multi_chip_mode;
    }
    ...
}

他调用了system_info_api->get_system_info这个函数,实际这就是 mod_system_info 暴露给 mod_cmn700 的一个 API。用于获取系统信息。

为何这个静态变量system_info_api就能调用到mod_system_info的函数呢?我们继续搜索代码,可以看到如下代码:

// module/cmn700/src/mod_cmn700.c
static int cmn700_bind(fwk_id_t id, unsigned int round)
{
    ...
    if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
        /* Bind to system info module to obtain multi-chip info */
        status = fwk_module_bind(
            FWK_ID_MODULE(FWK_MODULE_IDX_SYSTEM_INFO),
            FWK_ID_API(FWK_MODULE_IDX_SYSTEM_INFO, MOD_SYSTEM_INFO_GET_API_IDX),
            &system_info_api);
        return status;
    }
    ...
}

cmn700_bind函数中,调用了fwk_module_bind函数,这个函数的作用是绑定一个模块的 API,这样就可以通过这个 API 调用模块的函数。fwk_module_bind函数的第一个参数是提供 API 的模块的 ID,比如当前是模块system_info的 ID,如果是要用 USB 的 API,那么就是 USB 模块的 ID。第二个参数是 API 的 ID,这个 ID 是在模块的头文件中定义的,比如mod_system_info.h中定义了MOD_SYSTEM_INFO_GET_API_IDX,这个宏定义的值就是 API 的 ID。第三个参数是 API 的指针,这个指针就是开头提到的静态变量的地址,它是定义在使用 API 的模块中的。这样就可以通过 API 的指针调用模块的函数。对于 USB 模块,我们就需要在使用到 USB 的模块中定义一个静态变量。

fwk_module_bind做了什么呢?进入该函数,可以看到它回调了mod_system_infoprocess_bind_request函数,也就是system_info_process_bind_request函数。

// framework/src/fwk_module.c
int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api)
{
    ...
    status = fwk_mod_ctx->desc->process_bind_request(
        fwk_module_ctx.bind_id, target_id, api_id, (const void **)api);
    ...
}

system_info_process_bind_request函数的作用是根据 API 的 ID 返回 API 的指针。它把get_system_info_api的地址赋值给了api。这就实现了 API 的暴露。经过这个函数,system_info_api就指向了get_system_info_api。实际就是我们在cmn700_bind函数中使用system_info_api就是在调用get_system_info_api

// module/system_info/src/mod_system_info.c
static int system_info_process_bind_request(fwk_id_t requester_id,
        fwk_id_t targer_id, fwk_id_t api_id, const void **api)
{
    switch (fwk_id_get_api_idx(api_id)) {
    case MOD_SYSTEM_INFO_GET_API_IDX:
        *api = &get_system_info_api;
        break;
    default:
        return FWK_E_PARAM;
    }
    return FWK_SUCCESS;
}

解释完这个 bind 函数,它是在哪被调用的?它在框架初始化时就会被调用,分析fwk_arch_init函数,可以看到在fwk_module_start函数中会对每一个模块调用bind函数。函数调用流程参考图片文件。在此就不再赘述了。

经过以上分析,提供 API 的模块需要做的事情就是:

  1. 在模块的头文件中定义 API 的 ID,它有一定的格式,具体可以参考 MSCP 文档doc/framework.mdAPIs章节。

    enum mod_modulename_api {
        MOD_MODULENAME_API_A,
        MOD_MODULENAME_API_B,
    };
    
  2. 在模块的源文件中定义 API 结构体,用于在其他模块中使用。

    struct mod_system_info_get_info_api {
        int (*get_system_info)(const struct mod_system_info **sys_info);
    };
    
  3. 在模块原文件中提供process_bind_request函数,用于根据 API 的 ID 返回 API 的指针。

    static int system_info_process_bind_request(fwk_id_t requester_id,
        fwk_id_t targer_id, fwk_id_t api_id, const void **api)
    {
        switch (fwk_id_get_api_idx(api_id)) {
        case MOD_SYSTEM_INFO_GET_API_IDX:
            *api = &get_system_info_api;
            break;
        default:
            return FWK_E_PARAM;
        }
        return FWK_SUCCESS;
    }
    
  4. 在模块的源文件中定义 API 的指针,用于提供 API 的地址。

    static struct mod_system_info_get_info_api get_system_info_api = {
        .get_system_info = system_info_get_system_info,
    };
    
  5. 实现需要提供的 API 函数。

    static int system_info_get_system_info(const struct mod_system_info **sys_info)
    {
        *sys_info = &system_info;
        return FWK_SUCCESS;
    }
    

以上就是大致的移植流程了。

Responsive Image