想了解如何提供 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_info
的process_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 的模块需要做的事情就是:
在模块的头文件中定义 API 的 ID,它有一定的格式,具体可以参考 MSCP 文档
doc/framework.md
的APIs
章节。enum mod_modulename_api { MOD_MODULENAME_API_A, MOD_MODULENAME_API_B, };
在模块的源文件中定义 API 结构体,用于在其他模块中使用。
struct mod_system_info_get_info_api { int (*get_system_info)(const struct mod_system_info **sys_info); };
在模块原文件中提供
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; }
在模块的源文件中定义 API 的指针,用于提供 API 的地址。
static struct mod_system_info_get_info_api get_system_info_api = { .get_system_info = system_info_get_system_info, };
实现需要提供的 API 函数。
static int system_info_get_system_info(const struct mod_system_info **sys_info) { *sys_info = &system_info; return FWK_SUCCESS; }
以上就是大致的移植流程了。