一套軟硬件跑起來的樣子就像上面圖里面的一樣,it works。對應我們的SCP固件中,有那些框架來支撐這個系統運行起來,這里就需要一套基于M核或者單片機的通用框架程序,市面上的這種系統并不少見,例如freeRTOS等。
為了強調安全、簡單等特性,適配ARM的控制系統固件,ARM又搞了這套通用的框架,適合在M核或者R核上工作,甚至A核的某些特權系統例如OPTEE中。安全的核心就是隔離,隔離就是按功能形成module或者domain,模塊之間禁止無權限的訪問。
1. module介紹
SCP的每個功能都實現為一個單獨的module,module間耦合性盡量低,確保安全特性,通常固件所需的整體功能應來自模塊之間的交互。module間隔離就像上圖中的狗咬架,一旦伸手產生交互就禍福不能預測了,所以加上欄桿,規定好那些module間可以交互伸手,這都是通過API函數實現的,在系統初始化的時候設定死,下面模塊間綁定章節會講到。
SCP中的module分為兩部分:在代碼根目錄module文件夾下,共77個公共模塊,另外每個產品下面還有module,小100個可真不少。
一個固件只包含一部分module,在Firmware.cmake中定義,gen_module_code.py腳本生成源碼
這些module在framework啟動時候初始化啟動運行。
公共的module比較有通用性,產品自己的module一般是驅動需要進行定制
模塊類型及軟件協議棧:
這個協議棧就是SCP軟件跟外界交互的流程,一般消息都是通過驅動-》HAL層上來,然后處理的過程就是服務-》協議-》HAL-》驅動再操作硬件做出反應,這次交互就算結束了。具體如下:
2.framework框架流程
framework框架負責固件的通用流程實現,包括系統初始化,module初始化,中斷服務提供,event服務提供等。這樣module就可以專注于自己功能和對外交互api的實現。SCP framework初始化流程圖如下:
備注:這里的framework框架流程適用于scp_romfw和scp_ramfw,兩者區別只是包含module不同,定義包含了那些module在其目錄下的Firmware.cmake文件中。
編譯過程中gen_module_code.py腳本會生成module信息和配置信息的代碼,過程如下:SCP/MCP 軟件構建系統由一個頂級 Makefile :Makefile.cmake和一組 .mk 文件組成,例如juno產品product/juno/product.mk
BS_PRODUCT_NAME := juno BS_FIRMWARE_LIST := scp_romfw scp_romfw_bypass scp_ramfw |
模塊可以在項目根目錄的 modules/ 目錄下實現,也可以是產品特定的并在product/
gen_module_code.py腳本會根據
product/juno/scp_romfw/Firmware.cmake中SCP_MODULES變量
list(APPEND SCP_MODULES "juno-ppu") list(APPEND SCP_MODULES "juno-rom") list(APPEND SCP_MODULES "juno-soc-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "bootloader") |
生成
?fwk_module_idx.h:包含構成固件的模塊索引的枚舉。fwk_module_idx.h中枚舉中模塊索引的順序保證遵循固件firmware.mk 文件中 BS_FIRMWARE_MODULES列表中模塊名稱的順序。當執行涉及迭代固件中存在的所有模塊的操作時,框架在運行時使用相同的順序,例如 fwk_module.c 中的 init_modules() 函數。
enum fwk_module_idx { FWK_MODULE_IDX_JUNO_PPU = 0, FWK_MODULE_IDX_JUNO_ROM = 1, FWK_MODULE_IDX_JUNO_SOC_CLOCK = 2, FWK_MODULE_IDX_CLOCK = 3, FWK_MODULE_IDX_GTIMER = 4, FWK_MODULE_IDX_SDS = 5, FWK_MODULE_IDX_BOOTLOADER = 6, FWK_MODULE_IDX_COUNT = 7, }; |
?fwk_module_list.c:包含一個指向模塊描述符的指針表,每個模塊對應一個作為固件一部分構建的模塊。該文件及其內容由框架內部使用,通常不應由其他單元(如模塊)使用。
const struct fwk_module *module_table[FWK_MODULE_IDX_COUNT] = { &module_juno_ppu, &module_juno_rom, &module_juno_soc_clock, &module_clock, &module_gtimer, &module_sds, &module_bootloader, }; const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = { &config_juno_ppu, &config_juno_rom, &config_juno_soc_clock, &config_clock, &config_gtimer, &config_sds, &config_bootloader, }; |
module_table和module_config_table用于模塊初始化。
固件的Firmware.cmake文件中可以對配置開關進行聲明,例如:
set(SCP_FIRMWARE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}") set(SCP_GENERATE_FLAT_BINARY TRUE) set(SCP_ARCHITECTURE "arm-m") |
framework.md-固件相關配置文件說明
產品始終包含定義一個或多個固件目標的product.mk文件。
在一個產品中,總會有至少一個固件。
對于每個固件,必須在fmw_memory.h文件中提供鏈接器信息,例如product/juno/scp_romfw/fmw_memory.h中:
#define FMW_MEM_MODE ARCH_MEM_MODE_DUAL_REGION_RELOCATION /* ROM */ #define FMW_MEM0_SIZE SCP_ROM_SIZE #define FMW_MEM0_BASE SCP_ROM_BASE /* RAM */ #define FMW_MEM1_SIZE (16 * 1024) #define FMW_MEM1_BASE (SCP_RAM_BASE + SCP_RAM_SIZE - FMW_MEM1_SIZE) |
如果使用雙區域內存配置,則還必須定義FMW_MEM1_BASE和 FMW_MEM1_SIZE 。
Toolchain-*.cmake 中定義image的體系結構目標
product/juno/scp_romfw/Toolchain-GNU.cmake
set(CMAKE_SYSTEM_PROCESSOR "cortex-m3") set(CMAKE_TOOLCHAIN_PREFIX "arm-none-eabi-") ... |
product/juno/scp_romfw/CMakeLists.txt 定義編譯范圍和目標,cmake會使用生成makefile文件。
編譯選項說明:
cmake_readme.md-構建配置選項:
?SCP_ENABLE_NOTIFICATIONS:啟用/禁用 SCP 固件中的通知。
?SCP_ENABLE_SCMI_NOTIFICATIONS:啟用/禁用 SCMI 通知。
?SCP_ENABLE_RESOURCE_PERMISSIONS:啟用/禁用資源權限設置。
單獨配置編譯:
配置生效命令:
cmake -B /tmp/build -DSCP_FIRMWARE_SOURCE_DIR:PATH=juno/scp_romfw -DSCP_ENABLE_DEBUG_UNIT=TRUE |
然后就是編譯命令:
cmake --build /tmp/build |
在編譯文件中配置,例如在product/juno/scp_romfw/Firmware.cmake中
set(SCP_ENABLE_NOTIFICATIONS TRUE) |
修改后需要clean下,再繼續編譯。
2.1 平臺初始化
arch/arm/arm-m/CMakeLists.txt中,arch-arm-m庫的入口是arch_exception_reset()函數:
if(CMAKE_C_COMPILER_ID STREQUAL "ARMClang") target_link_options(arch-arm-mPUBLIC "LINKER:--entry=arch_exception_reset")endif()
arch_exception_reset()函數在arch/arm/arm-m/src/arch.ld.S鏈接文件中也被定位了入口函數
其實現在arch/arm/arm-m/src/arch_handlers.c:
noreturn void arch_exception_reset(void) { extern noreturn void __main(void); __main(); }
__main在c運行時調用main函數,對于M核實現來說,arch/arm/arm-m/src/arch_main.c中有main()函數
int main(void) { ////初始化 ARM Cortex-M 系列芯片的 Configuration Control Register (CCR)。 //其中,通過設置 SCB_CCR_DIV_0_TRP_Msk 來啟用除以零的異常處理 arch_init_ccr(); //scp 入口及應用函數 return fwk_arch_init(&arch_init_driver); } |
scp 入口及應用為fwk_arch_init函數,在framework/src/fwk_arch.c中
int fwk_arch_init(const struct fwk_arch_init_driver *driver) { //scp 框架初始化,完成module_table、module_config_table所有模塊信息的初始化 //scp/module目錄下的模塊的初始化 fwk_module_init(); //這里構建了一個全局的fwk_io_stdin、 fwk_io_stdout, 在后面的終端輸出有用 status = fwk_io_init(); //初始化日志輸出方式 status = fwk_log_init(); //中斷gic初始化 status = fwk_arch_interrupt_init(driver->interrupt); //所有模塊初始化,開始任務 status = fwk_module_start(); //循環等待處理隊列事件 __fwk_run_main_loop(); } |
2.2 module初始化
fwk_module_init函數,在framework/src/fwk_module.c中實現
在系統構建章節中module_table和module_config_table是由配置文件Firmware.cmake生成的fwk_module_list.c中定義。
module見module介紹章節
module_config_table就是模塊的上下文信息
void fwk_module_init(void) { for (uint32_t i = 0U; i < ?(uint32_t)FWK_MODULE_IDX_COUNT; i++) { //獲取模塊的上下文信息 struct fwk_module_context *ctx = &fwk_module_ctx.module_ctx_table[i]; fwk_id_t id = FWK_ID_MODULE(i); const struct fwk_module *desc = module_table[i]; const struct fwk_module_config *config = module_config_table[i]; //給模塊上下文信息賦值 *ctx = (struct fwk_module_context){ .id = id, .desc = desc, .config = config, }; //初始化模塊的鏈表 fwk_list_init(&ctx->delayed_response_list); if (config->elements.type == FWK_MODULE_ELEMENTS_TYPE_STATIC) { size_t notification_count = 0; #ifdef BUILD_HAS_NOTIFICATION notification_count = desc->notification_count; #endif fwk_module_init_element_ctxs( ctx, config->elements.table, notification_count); } #ifdef BUILD_HAS_NOTIFICATION if (desc->notification_count > 0) { fwk_module_init_subscriptions( &ctx->subscription_dlist_table, desc->notification_count); } #endif } } |
2.3 中斷初始化
static int fwk_arch_interrupt_init(int (*interrupt_init_handler)( const struct fwk_arch_interrupt_driver **driver)) { const struct fwk_arch_interrupt_driver *driver; status = interrupt_init_handler(&driver); /* Initialize the interrupt management component */ status = fwk_interrupt_init(driver); return FWK_SUCCESS; } |
interrupt_init_handler是入參回調函數,對應為arch_init_driver
static const struct fwk_arch_init_driver arch_init_driver = { .interrupt = arch_nvic_init, }; |
在arch_nvic_init中有*driver =&arch_nvic_driver;
static const struct fwk_arch_interrupt_driver arch_nvic_driver = { .global_enable = global_enable, .global_disable = global_disable, .is_enabled = is_enabled, .enable = enable, .disable = disable, .is_pending = is_pending, .set_pending = set_pending, .clear_pending = clear_pending, .set_isr_irq = set_isr_irq, .set_isr_irq_param = set_isr_irq_param, .set_isr_nmi = set_isr_nmi, .set_isr_nmi_param = set_isr_nmi_param, .set_isr_fault = set_isr_fault, .get_current = get_current, .is_interrupt_context = is_interrupt_context, }; |
拿到driver的值后,執行fwk_interrupt_init(driver);
int fwk_interrupt_init(const struct fwk_arch_interrupt_driver *driver) { //校驗driver fwk_interrupt_driver = driver; initialized = true; return FWK_SUCCESS; } |
fwk_interrupt_driver 全局變量用于中斷處理。
模塊使用中斷時,需要調用對外接口在framework/include/fwk_interrupt.h中,
例如開啟中斷fwk_interrupt_enable函數的實現:
int fwk_interrupt_enable(unsigned int interrupt) { if (!initialized) { return FWK_E_INIT; } return fwk_interrupt_driver->enable(interrupt); } |
2.4 module啟動
fwk_module_start()在framework/src/fwk_module.c中定義
int fwk_module_start(void) { //初始化任務列表 status = __fwk_init(FWK_MODULE_EVENT_COUNT); fwk_module_ctx.stage = MODULE_STAGE_INITIALIZE; //從功能方面初始化所有module fwk_module_init_modules(); fwk_module_ctx.stage = MODULE_STAGE_BIND; //調用模塊.bind回調函數完成所有模塊的綁定。(此處共進行兩輪調用fwk_module_bind_module(round=0 1), //每輪都將分別綁定模塊module和模塊的元素element) for (bind_round = 0; bind_round <= FWK_MODULE_BIND_ROUND_MAX; bind_round++) { status = fwk_module_bind_modules(bind_round); if (status != FWK_SUCCESS) { return status; } } fwk_module_ctx.stage = MODULE_STAGE_START; //啟動模塊 status = start_modules(); fwk_module_ctx.initialized = true; return FWK_SUCCESS; } |
fwk_module_init_modules函數調用fwk_module_init_module對每個模塊進行功能初始化
//初始化模塊元素上下文(element_ctxs), //調用模塊的config->elements.generator,獲取element信息,加入模塊上下文表 elements = config->elements.generator(ctx->id); fwk_module_init_element_ctxs(ctx, elements, notification_count); //調用模塊的init函數,傳入element_count,config->dat status = desc->init(ctx->id, ctx->element_count, config->data); //初始化模塊元素(element),調用模塊回調函數.element_init將模塊element->data配置信息導入到模塊內部 fwk_module_init_elements(ctx); |
start_modules函數調用fwk_module_start_module對每個模塊進行啟動
module = fwk_mod_ctx->desc; //調用模塊.start回調函數 module->start(fwk_mod_ctx->id); |
例如在juno_rom的.start回調函數函數中,通過event和notification機制,到達juno_rom模塊的相應回調函數,在juno_rom中,通過ctx.bootloader_api->load_image()調用mod_bootloader的api,從安全內存拷貝到指定位置,在該bootloader模塊api中加載跳轉scp_ramfw。(注mod_bootloader_boot為匯編實現,依賴arm指令)。在product/juno/module/juno_rom/src/mod_juno_rom.c中:
const struct fwk_module module_juno_rom = { .type = FWK_MODULE_TYPE_SERVICE, .event_count = (unsigned int)MOD_JUNO_ROM_EVENT_COUNT, .notification_count = (unsigned int)MOD_JUNO_ROM_NOTIFICATION_COUNT, .init = juno_rom_init, .bind = juno_rom_bind, .start = juno_rom_start, .process_event = juno_rom_process_event, .process_notification = juno_rom_process_notification, }; |
2.5 運行狀態機
scp-firmware在完成了所有的初始化操作后,進入死循環,處理隊列里面的事件或者休眠等待事件到來。
noreturn void __fwk_run_main_loop(void) { for (;;) { fwk_process_event_queue(); if (fwk_log_unbuffer() == FWK_SUCCESS) { fwk_arch_suspend(); } } } |
fwk_process_event_queue主要處理三個重要的鏈表:free_event_queue, event_queue, isr_event_queue所有的操作都是圍繞這三個隊列展開。
void fwk_process_event_queue(void) { for (;;) { while (!fwk_list_is_empty(&ctx.event_queue)) { process_next_event(); } if (!process_isr()) { break; } } } |
event_queue中根據target_id找到對應module,然后調用module->process_event進行處理,詳細見module中說明。
process_next_event中調用duplicate_event會處理free_event_queue隊列中的事件
process_isr從中斷isr_event_queue隊列中取到事件,然后加入到event_queue中
3.module對外接口
在scp代碼中,所有的功能都由一個個模塊提供。每個模塊以api枚舉及其結構體的方式對外提供該模塊的功能,并在模塊通用結構體fwk_module中提供,例如
module/scmi_power_domain/src/mod_scmi_power_domain.c中,
/* SCMI Power Domain Management Protocol Definition */ const struct fwk_module module_scmi_power_domain = { .api_count = 1, .type = FWK_MODULE_TYPE_PROTOCOL, .init = scmi_pd_init, .bind = scmi_pd_bind, .start = scmi_pd_start, .process_bind_request = scmi_pd_process_bind_request, .event_count = (unsigned int)SCMI_PD_EVENT_IDX_COUNT, .process_event = scmi_pd_process_event, #ifdef BUILD_HAS_MOD_DEBUG .process_notification = scmi_pd_process_notification, #endif }; |
.init(模塊初始化)
.bind(獲取綁定別的模塊的api)
.process_bind_request(被其他模塊依賴的api的獲取并綁定請求函數)等通用接口。
.start模塊啟動
.process_event事件處理
.process_notification通知處理
初始化模塊:
模塊在初始化時由fwk_module.c 中fwk_module_start函數,調用回調函數.init,.bind,.start
?模塊初始化:調用模塊API的init()函數指針
?元素初始化:調用框架模塊API的element_init()函數指針
?后初始化:元素初始化后,模塊交互之前的一些可選處理操作
?綁定:模塊必須綁定好才能調用對方的api
?開始
運行時:
一旦運行前階段成功完成,固件將開始處理模塊或中斷引發的事件。默認情況下,固件將永遠循環等待新事件在運行前階段結束時處理,但當事件列表為空時,可以在處理未決事件后返回。
模塊配置:
模塊初始化的時候,模塊配置被讀入存放到模塊上下文中:
const struct fwk_module_config *config = module_config_table[i]; //給模塊上下文信息賦值 *ctx = (struct fwk_module_context){ .id = id, .desc = desc, .config = config, }; |
在module_config_table在fwk_module_list.c中定義,這里以config_juno_ppu為例:
const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = { &config_juno_ppu, struct fwk_module_config config_juno_ppu = { .data = &(struct mod_juno_ppu_config){ .timer_alarm_id = FWK_ID_SUB_ELEMENT_INIT( FWK_MODULE_IDX_TIMER, 0, JUNO_PPU_ALARM_IDX), }, .elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_element_table), }; #define FWK_MODULE_DYNAMIC_ELEMENTS(GENERATOR) { .type = FWK_MODULE_ELEMENTS_TYPE_DYNAMIC, .generator = (GENERATOR), } |
如果類型為FWK_MODULE_ELEMENTS_TYPE_STATIC ,框架使用表指針中給出的靜態表來訪問產品為模塊提供的元素表。
如果類型為 FWK_MODULE_ELEMENTS_TYPE_DYNAMIC ,則框架使用生成器函數指針。
get_element_table對應一個配置結構體數組:
static struct fwk_element element_table[] = { [JUNO_PPU_DEV_IDX_BIG_SSTOP] = { .name = "", .data = &(const struct mod_juno_ppu_element_config) { .reg_base = PPU_BIG_SSTOP_BASE, .timer_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_TIMER, 0), .pd_type = MOD_PD_TYPE_CLUSTER, }, }, .... enum juno_ppu_idx { JUNO_PPU_DEV_IDX_BIG_CPU0, JUNO_PPU_DEV_IDX_BIG_CPU1, JUNO_PPU_DEV_IDX_BIG_SSTOP, JUNO_PPU_DEV_IDX_LITTLE_CPU0, JUNO_PPU_DEV_IDX_LITTLE_CPU1, JUNO_PPU_DEV_IDX_LITTLE_CPU2, JUNO_PPU_DEV_IDX_LITTLE_CPU3, JUNO_PPU_DEV_IDX_LITTLE_SSTOP, JUNO_PPU_DEV_IDX_GPUTOP, JUNO_PPU_DEV_IDX_SYSTOP, JUNO_PPU_DEV_IDX_DBGSYS, JUNO_PPU_DEV_IDX_COUNT, }; |
struct fwk_element結構體表示元素,里面有名字,子元素個數和數據
元素:
元素表示由模塊擁有或管理的資源。每個元素將表示模塊與之交互和/或負責的對象。
例如,驅動程序類型的模塊可能具有表示它所控制的硬件設備的元素。因為元素配置數據靈活多變,使用通用的方式const void *data實現。
子元素表示由元素擁有或管理的資源。子元素僅由它們的索引和/或標識符表示。
索引和標識符:
由于框架設計為模塊化,因此需要一種標準化方法來識別和引用模塊、元素、子元素、事件、通知和 API。該框架為此定義了兩個組件:indices和identifiers。
indices:
模塊索引由構建系統為每個固件生成,并放在fwk_module_idx.h頭文件中。
enum fwk_module_idx { FWK_MODULE_IDX_JUNO_PPU = 0, FWK_MODULE_IDX_JUNO_ROM = 1, ...... |
identifiers:
標識符有一個類型,這決定了標識符中包含的信息。在內部,標識符始終包含模塊的索引,并且可能包含在該模塊的上下文中標識項目的附加索引。也在fwk_module_idx.h頭文件中,有宏和變量兩部分定義,值是一樣的:
#define FWK_MODULE_ID_JUNO_PPU FWK_ID_MODULE(FWK_MODULE_IDX_JUNO_PPU) #define FWK_ID_MODULE(MODULE_IDX) ((fwk_id_t)FWK_ID_MODULE_INIT(MODULE_IDX)) #define FWK_ID_MODULE_INIT(MODULE_IDX) { .common = { .type = (uint32_t)__FWK_ID_TYPE_MODULE, .module_idx = (uint32_t)MODULE_IDX, }, } static const fwk_id_t fwk_module_id_juno_ppu = FWK_MODULE_ID_JUNO_PPU_INIT; #define FWK_MODULE_ID_JUNO_PPU_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_JUNO_PPU) |
可用的標識符類型有:
?模塊:僅由模塊索引組成
?元素:由模塊索引和模塊內元素的索引組成
?子元素:由模塊索引、模塊內元素的索引和該元素擁有的子元素的索引組成。
?API:由模塊索引和模塊提供的API的索引組成
?事件:由模塊索引和模塊可能產生的事件的索引組成
?通知:由模塊索引和模塊可能生成的通知索引組成。
日志:
日志記錄功能定義并實現了該組件的公共接口。該接口的文檔可以在 fwk_log.h 中找到。
#include FWK_LOG_ERR("[ROM] ERROR: Failed to turn on LITTLE cluster."); # define FWK_LOG_ERR(...) fwk_log_printf(__VA_ARGS__) |
fwk_log_printf()函數在framework/src/fwk_log.c中定義。
4. event事件
模塊可以給自己或者別的模塊發送event事件,事件的參數是結構化消息structfwk_event。
static int juno_rom_process_event( const struct fwk_event *event, struct fwk_event *resp) { truct fwk_event { struct fwk_slist_node slist_node; fwk_id_t source_id; fwk_id_t target_id; uint32_t cookie; bool is_response; bool response_requested; bool is_notification; bool is_delayed_response; fwk_id_t id; alignas(max_align_t) uint8_t params[FWK_EVENT_PARAMETERS_SIZE]; }; |
該事件包含一個response_requested 屬性,該屬性指示源實體是否期望對其事件的響應。為了響應這個事件,接收實體填寫響應參數,框架發出一個事件,該事件以發出原始事件的實體為目標。
事件的is_response屬性用于指示新生成的事件是對原始事件的響應。
例如在juno_rom固件初始化時,初始化juno_rom模塊,product/juno/module/juno_rom/src/mod_juno_rom.c
會執行.start回調函數函數juno_rom_start(),給自己發了一個event,如下:
static int juno_rom_start(fwk_id_t id) { struct fwk_event event = { .source_id = fwk_module_id_juno_rom, .target_id = fwk_module_id_juno_rom, .id = mod_juno_rom_event_id_run, }; ..... return fwk_put_event(&event); } #define fwk_put_event(event) _Generic((event), struct fwk_event * : __fwk_put_event, struct fwk_event_light * : __fwk_put_event_light)(event) |
fwk_put_event把event分為兩類,fwk_event_light 是輕量級的攜帶不攜帶額外數據參數。這里我們用fwk_event 則處理函數為:
__fwk_put_event --》put_event(event, intr_state, FWK_EVENT_TYPE_STD); --》fwk_list_push_tail(&ctx.event_queue, &allocated_event->slist_node);
固件狀態機運行的時候會循環執行framework/src/fwk_core.c中process_next_event()函數
static void process_next_event(void) { ctx.current_event = event = FWK_LIST_GET( fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node); module = fwk_module_get_ctx(event->target_id)->desc; process_event = event->is_notification ? module->process_notification : module->process_event; status = process_event(event, &async_response_event); |
這里找到模塊juno_rom,然后取出其event處理函數process_event并執行,實際執行的是juno_rom_process_event(),其發了一條通知消息如下:
static int juno_rom_process_event( const struct fwk_event *event, struct fwk_event *resp) { .... /* Send SYSTOP ON notification */ systop_on_event = (struct fwk_event){ .response_requested = true, .id = mod_juno_rom_notification_id_systop, .source_id = FWK_ID_NONE }; notification_params = (void *)systop_on_event.params; notification_params->state = (unsigned int)MOD_PD_STATE_ON; //發notification消息 status = fwk_notification_notify(&systop_on_event, &ctx.notification_count); if (!fwk_expect(status == FWK_SUCCESS)) { return FWK_E_PANIC; } //通過ctx.bootloader_api->load_image()調用mod_bootloader的api,從安全內存拷貝到指定位置, //在該bootloader 模塊api中加載跳轉scp_ramfw。 if (ctx.notification_count == 0) { return deferred_setup(); } |
fwk_notification_notify的解釋見notification章節
5.motificaiont通知
notification涉及到兩個模塊的通信,跟event的區別是:
?event是一個模塊發給另外一個模塊或者發給自己,比較確定
?notification是發給訂閱了這個模塊的所有模塊,算廣播,需要先進行訂閱
notification接口:
?fwk_notification_subscribe//訂閱指定模塊指定通知
?fwk_notification_unsubscribe//取消訂閱通知
?fwk_notification_notify//向訂閱該通知的模塊發送通知
在實現上notification使用event的消息傳遞機制,只在發消息和處理消息的時候做微小改動。
例如上面例子中使用fwk_notification_notify()函數發送的通知
int fwk_notification_notify(struct fwk_event *notification_event,unsigned int *count){ send_notifications(notification_event, count); |
通知的參數沿用event的struct fwk_event ,發送通知的時候,需要先找到訂閱鏈表,然后進行過濾
static void send_notifications(struct fwk_event *notification_event, unsigned int *count) { //根據id和source_id找到訂閱的鏈表 subscription_dlist = get_subscription_dlist(notification_event->id, notification_event->source_id); notification_event->is_response = false; notification_event->is_notification = true; for (node = fwk_list_head(subscription_dlist); node != NULL; node = fwk_list_next(subscription_dlist, node)) { subscription = FWK_LIST_GET(node, struct __fwk_notification_subscription, dlist_node); //對比源id如果相同就進行發送 if (!fwk_id_is_equal( subscription->source_id, notification_event->source_id)) { continue; } notification_event->target_id = subscription->target_id; status = __fwk_put_notification(notification_event); if (status == FWK_SUCCESS) { (*count)++; } } } |
get_subscription_dlist函數中source_id 決定是模塊上下文還是元素上下文
.id = mod_juno_rom_notification_id_systop, .source_id = FWK_ID_NONE }; static const fwk_id_t mod_juno_rom_notification_id_systop = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_JUNO_ROM, MOD_JUNO_ROM_NOTIFICATION_IDX_SYSTOP); |
拿到subscription_dlist訂閱列表后,就進行過濾發送通知
int __fwk_put_notification(struct fwk_event *event) { event->is_response = false; event->is_notification = true; return put_event(event, UNKNOWN_STATE, FWK_EVENT_TYPE_STD); } |
這里就使用了event進行實現。然后系統狀態機在處理event的時候,
static void process_next_event(void) { ctx.current_event = event = FWK_LIST_GET( fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node); module = fwk_module_get_ctx(event->target_id)->desc; process_event = event->is_notification ? module->process_notification : module->process_event; |
根據is_notification 就可以知道是notification 了,然后調用process_notification 進行處理
6.模塊綁定
一個模塊或元素可以綁定到另一個模塊或模塊內的元素。目標是相同的 - 獲取指向可在后續階段使用的 API 的指針。當嘗試綁定到模塊內的元素(而不是模塊本身)時,主要區別在于接收和處理綁定請求的模塊能夠根據目標元素更改其行為。例如,可以允許請求綁定的模塊僅綁定到處理請求的模塊內的元素子集。
思路:A模塊要與B模塊通信,A模塊的全局變量要拿到B模塊的回調函數。
A模塊在初始化的時候,會調用自己的bind函數,
bind--》fwk_module_bind--》B模塊的process_bind_request()函數,從而拿到api
scmi_power_domain模塊調用scmi模塊的api函數示例圖
scmi_pd_ctx.scmi_api賦值為scmi模塊的處理函數,在.bind = scmi_pd_bind中,
static int scmi_pd_bind(fwk_id_t id, unsigned int round) { status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI), FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL), &scmi_pd_ctx.scmi_api); |
fwk_module_bind調用依賴模塊提供的process_bind_request函數來獲取依賴模塊的api,并綁定。
int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api) { fwk_mod_ctx = fwk_module_get_ctx(target_id); status = fwk_mod_ctx->desc->process_bind_request( fwk_module_ctx.bind_id, target_id, api_id, (const void **)api); |
target_id是FWK_MODULE_IDX_SCMI,對應SCMI模塊,fwk_mod_ctx 是SCMI模塊的上下文
/* SCMI module definition */ const struct fwk_module module_scmi = { .api_count = (unsigned int)MOD_SCMI_API_IDX_COUNT, .event_count = 1, #ifdef BUILD_HAS_NOTIFICATION .notification_count = (unsigned int)MOD_SCMI_NOTIFICATION_IDX_COUNT, #endif .type = FWK_MODULE_TYPE_SERVICE, .init = scmi_init, .element_init = scmi_service_init, .bind = scmi_bind, .start = scmi_start, .process_bind_request = scmi_process_bind_request, .process_event = scmi_process_event, #ifdef BUILD_HAS_NOTIFICATION .process_notification = scmi_process_notification, #endif }; |
scmi_process_bind_request調用到
static int scmi_process_bind_request(fwk_id_t source_id, fwk_id_t target_id, fwk_id_t api_id, const void **api) { unsigned int api_idx; struct scmi_service_ctx *ctx; enum mod_scmi_api_idx api_id_type; api_idx = fwk_id_get_api_idx(api_id); api_id_type = (enum mod_scmi_api_idx)api_idx; switch (api_id_type) { case MOD_SCMI_API_IDX_PROTOCOL: if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) { return FWK_E_SUPPORT; } if (scmi_ctx.protocol_count >= scmi_ctx.config->protocol_count_max) { return FWK_E_NOMEM; } scmi_ctx.protocol_table[PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT + scmi_ctx.protocol_count++].id = source_id; *api = &scmi_from_protocol_api; break; |
到此scmi_power_domain模塊拿到了scmi模塊的處理函數,放入&scmi_pd_ctx.scmi_api
幾個關鍵的模塊間api調用示例:
mod_scmi_power_domain模塊中scmi消息收發:
?scmi模塊綁定各個scmi協議模塊的protocol_api,根據id來找到此模塊api,執行該protocol;
?scmi_power_domain模塊綁定scmi模塊的api,通過調用scmi_api(mod_scmi.c中)來回復該protocol;
mod_scmi.c/transport_api調用mod_smt中的transport相關功能來完成scmi協議的transport層(scmi 數據收發及解析);
mod_smt.c/driver_api調用scmi更下一級的channel來產生中斷(scmi消息通知中斷產生和處理)。
審核編輯:劉清
-
ARM處理器
+關注
關注
6文章
360瀏覽量
41733 -
耦合器
+關注
關注
8文章
725瀏覽量
59705 -
FreeRTOS
+關注
關注
12文章
484瀏覽量
62157 -
狀態機
+關注
關注
2文章
492瀏覽量
27538 -
MCP
+關注
關注
0文章
254瀏覽量
13904
原文標題:ARM SCP入門-framework框架代碼分析
文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論