From 49ce9c3e646452553b5cff288c1944532b087dfa Mon Sep 17 00:00:00 2001 From: Erwan Gouriou Date: Tue, 13 Sep 2022 08:55:17 +0200 Subject: [PATCH] lib: stm32wba: hci: Add flash_manager when building controller library Flash manager is used to driver flash access coexistence between RF and user accesses. Signed-off-by: Erwan Gouriou --- lib/CMakeLists.txt | 7 + lib/stm32wba/hci/README | 8 + lib/stm32wba/hci/flash_driver.c | 132 +++++++ lib/stm32wba/hci/flash_driver.h | 90 +++++ lib/stm32wba/hci/flash_manager.c | 514 +++++++++++++++++++++++++++ lib/stm32wba/hci/flash_manager.h | 80 +++++ lib/stm32wba/hci/rf_timing_synchro.c | 205 +++++++++++ lib/stm32wba/hci/rf_timing_synchro.h | 53 +++ lib/stm32wba/hci/stm_list.c | 200 +++++++++++ lib/stm32wba/hci/stm_list.h | 56 +++ 10 files changed, 1345 insertions(+) create mode 100644 lib/stm32wba/hci/flash_driver.c create mode 100644 lib/stm32wba/hci/flash_driver.h create mode 100644 lib/stm32wba/hci/flash_manager.c create mode 100644 lib/stm32wba/hci/flash_manager.h create mode 100644 lib/stm32wba/hci/rf_timing_synchro.c create mode 100644 lib/stm32wba/hci/rf_timing_synchro.h create mode 100644 lib/stm32wba/hci/stm_list.c create mode 100644 lib/stm32wba/hci/stm_list.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 1003cb6d3..df782938d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -38,6 +38,13 @@ if(CONFIG_HAS_STM32LIB) zephyr_sources(stm32wba/hci/bpka.c) zephyr_sources(stm32wba/hci/power_table.c) zephyr_sources(stm32wba/hci/scm.c) + zephyr_sources(stm32wba/hci/log_module.c) + if(CONFIG_FLASH) + zephyr_sources(stm32wba/hci/flash_manager.c) + zephyr_sources(stm32wba/hci/flash_driver.c) + zephyr_sources(stm32wba/hci/stm_list.c) + zephyr_sources(stm32wba/hci/rf_timing_synchro.c) + endif() set(STM32WBA_BLE_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr/blobs/stm32wba/lib) set(STM32WBA_BLE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stm32wba/hci) diff --git a/lib/stm32wba/hci/README b/lib/stm32wba/hci/README index 3258540f1..49cb47f9f 100644 --- a/lib/stm32wba/hci/README +++ b/lib/stm32wba/hci/README @@ -46,12 +46,20 @@ Description: Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/Core/Inc/app_entry.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/Core/Inc/utilities_conf.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/Core/Inc/main.h + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/Flash/rf_timing_synchro.c + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/Flash/rf_timing_synchro.h + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/Flash/flash_driver.c + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/Flash/flash_driver.h + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/Flash/flash_manager.c + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/Flash/flash_manager.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/RTDebug/debug_signals.h Projects/NUCLEO-WBA55CG/Applications/BLE/BLE_HeartRate/System/Modules/RTDebug/RTDebug.c Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/RTDebug/RTDebug.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/RTDebug/local_debug_tables.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/scm.c Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/scm.h + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/stm_list.c + Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/stm_list.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Modules/utilities_common.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Interfaces/hw.h Projects/NUCLEO-WBA52CG/Applications/BLE/BLE_HeartRate/System/Interfaces/hw_aes.c diff --git a/lib/stm32wba/hci/flash_driver.c b/lib/stm32wba/hci/flash_driver.c new file mode 100644 index 000000000..8d705c09a --- /dev/null +++ b/lib/stm32wba/hci/flash_driver.c @@ -0,0 +1,132 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file flash_driver.c + * @author MCD Application Team + * @brief The Flash Driver module is the interface layer between Flash + * management modules and HAL Flash drivers + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include "flash_driver.h" +#include "utilities_conf.h" + +/* Global variables ----------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ +/* Private defines -----------------------------------------------------------*/ + +#define FD_CTRL_NO_BIT_SET (0UL) /* value used to reset the Flash Control status */ + +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ + +/** + * @brief variable used to represent the Flash Control status + */ +static volatile FD_Flash_ctrl_bm_t FD_Flash_Control_status = FD_CTRL_NO_BIT_SET; + +/* Private function prototypes -----------------------------------------------*/ +/* Functions Definition ------------------------------------------------------*/ + +/** + * @brief Update Flash Control status + * @param Flags_bm: Bit mask identifying the caller (1 bit per user) + * @param Status: Action requested (enable or disable flash access) + * @retval None + */ +void FD_SetStatus(FD_Flash_ctrl_bm_t Flags_bm, FD_FLASH_Status_t Status) +{ + UTILS_ENTER_CRITICAL_SECTION(); + + switch (Status) + { + case LL_FLASH_DISABLE: + { + FD_Flash_Control_status |= (1u << Flags_bm); + break; + } + case LL_FLASH_ENABLE: + { + FD_Flash_Control_status &= ~(1u << Flags_bm); + break; + } + default : + { + break; + } + } + + UTILS_EXIT_CRITICAL_SECTION(); +} + +/** + * @brief Write a block of 128 bits (4 32-bit words) in Flash + * @param Dest: Address where to write in Flash (128-bit aligned) + * @param Payload: Address of data to be written in Flash (32-bit aligned) + * @retval FD_FlashOp_Status_t: Success or failure of Flash write operation + */ +FD_FlashOp_Status_t FD_WriteData(uint32_t Dest, uint32_t Payload) +{ + FD_FlashOp_Status_t status = FD_FLASHOP_FAILURE; + + /* Check if RFTS OR Application allow flash access */ + if ((FD_Flash_Control_status & (1u << FD_FLASHACCESS_RFTS)) && + (FD_Flash_Control_status & (1u << FD_FLASHACCESS_RFTS_BYPASS))) + { /* Access not allowed */ + return status; + } + + /* Wait for system to allow flash access */ + while (FD_Flash_Control_status & (1u << FD_FLASHACCESS_SYSTEM)); + + if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, Dest, Payload) == HAL_OK) + { + status = FD_FLASHOP_SUCCESS; + } + return status; +} + +/** + * @brief Erase one sector of Flash + * @param Sect: Identifier of the sector to erase + * @retval FD_FlashOp_Status_t: Success or failure of Flash erase operation + */ +FD_FlashOp_Status_t FD_EraseSectors(uint32_t Sect) +{ + FD_FlashOp_Status_t status = FD_FLASHOP_FAILURE; + uint32_t page_error; + FLASH_EraseInitTypeDef p_erase_init; + + /* Check if LL allows flash access */ + if ((FD_Flash_Control_status & (1u << FD_FLASHACCESS_RFTS)) && + (FD_Flash_Control_status & (1u << FD_FLASHACCESS_RFTS_BYPASS))) + { /* Access not allowed */ + return status; + } + + /* Wait for system to allow flash access */ + while (FD_Flash_Control_status & (1u << FD_FLASHACCESS_SYSTEM)); + + p_erase_init.TypeErase = FLASH_TYPEERASE_PAGES; + p_erase_init.Page = Sect; + p_erase_init.NbPages = 1; + + if (HAL_FLASHEx_Erase(&p_erase_init, &page_error) == HAL_OK) + { + status = FD_FLASHOP_SUCCESS; + } + + return status; +} diff --git a/lib/stm32wba/hci/flash_driver.h b/lib/stm32wba/hci/flash_driver.h new file mode 100644 index 000000000..cef7b8500 --- /dev/null +++ b/lib/stm32wba/hci/flash_driver.h @@ -0,0 +1,90 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file flash_driver.h + * @author MCD Application Team + * @brief Header for flash_driver.c module + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef FLASH_DRIVER_H +#define FLASH_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "utilities_common.h" + +/* Exported types ------------------------------------------------------------*/ + +/* Bit mask to modify Flash Control status */ +typedef uint32_t FD_Flash_ctrl_bm_t; + +/* Flash operation status */ +typedef enum +{ + FD_FLASHOP_SUCCESS, + FD_FLASHOP_FAILURE +} FD_FlashOp_Status_t; + +/* Flash Driver commands to enable or disable flash access */ +typedef enum +{ + LL_FLASH_ENABLE, + LL_FLASH_DISABLE, +} FD_FLASH_Status_t; + +/** + * @brief Bit mask to modify Flash Control status + * + * @details Those bitmasks are used to enable/disable access to the flash: + * - System: + * -# FD_FLASHACCESS_SYSTEM: Determine whether or not the flash access is allowed from a system POV. + * This bit has a predominance over all the other bit masks, ie: No flash operation can + * be achieved without this to be set to 0, ie: LL_FLASH_ENABLE. + * - RFTS: + * -# FD_FLASHACCESS_RFTS: Determine whether or not the RF Timing Synchro allows flash access. This bit is set + * once a window has been allowed by the BLE LL, ie: set to 0. + * This bit has no impact when FD_FLASHACCESS_RFTS_BYPASS is set, ie: set to 0. + * -# FD_FLASHACCESS_RFTS_BYPASS: Nullify the impact of FD_FLASHACCESS_RFTS when enabled, ie: set to 0. Its role is + * to allow flash operation without the need to request a timing window to the RFTS, + * ie: Executing flash operation without the BLE LL. + * + */ +typedef enum FD_FlashAccess_bm +{ + /* System flash access bitfield */ + FD_FLASHACCESS_SYSTEM, + /* RF Timing Synchro flash access bitfield */ + FD_FLASHACCESS_RFTS, + /* Bypass of RF Timing Synchro flash access bitfield */ + FD_FLASHACCESS_RFTS_BYPASS, +}FD_FlashAccess_bm_t; + +/* Exported constants --------------------------------------------------------*/ +/* Exported variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +void FD_SetStatus(FD_Flash_ctrl_bm_t Flags_bm, FD_FLASH_Status_t Status); +FD_FlashOp_Status_t FD_WriteData(uint32_t Dest, uint32_t Payload); +FD_FlashOp_Status_t FD_EraseSectors(uint32_t Sect); + +#ifdef __cplusplus +} +#endif + +#endif /*FLASH_DRIVER_H */ diff --git a/lib/stm32wba/hci/flash_manager.c b/lib/stm32wba/hci/flash_manager.c new file mode 100644 index 000000000..d92ef06ca --- /dev/null +++ b/lib/stm32wba/hci/flash_manager.c @@ -0,0 +1,514 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file flash_manager.c + * @author MCD Application Team + * @brief The Flash Manager module provides an interface to write raw data + * from SRAM to FLASH + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "flash_manager.h" +#include "rf_timing_synchro.h" +#include "flash_driver.h" +#include "utilities_conf.h" + +#include "stm32wbaxx_hal.h" + +/* Debug */ +#include "log_module.h" + +/* Global variables ----------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ + +/* State of the background process */ +typedef enum FM_BackGround_States +{ + FM_BKGND_NOWINDOW_FLASHOP, + FM_BKGND_WINDOWED_FLASHOP, +}FM_BackGround_States_t; + +/* Flash operation type */ +typedef enum +{ + FM_WRITE_OP, + FM_ERASE_OP +} FM_FlashOp_t; + +/** + * @brief Flash operation configuration struct + */ +typedef struct FM_FlashOpConfig +{ + uint32_t *writeSrc; + uint32_t *writeDest; + int32_t writeSize; + uint32_t eraseFirstSect; + uint32_t eraseNbrSect; +}FM_FlashOpConfig_t; + +/* Private defines -----------------------------------------------------------*/ + +#define FLASH_WRITE_BLOCK_SIZE 4U +#define ALIGNMENT_32 0x00000003 +#define ALIGNMENT_128 0x0000000F + +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ + +/** + * @brief Semaphore on Flash + */ +static bool busy_flash_sem = FALSE; + +/** + * @brief Indicates if the flash manager module is available or not + */ +static bool flash_manager_busy = FALSE; + +/** + * @brief Parameters for Flash Write command + */ +static bool fm_window_granted = FALSE; + +/** + * @brief Callback node list for pending flash operation request + */ +static tListNode fm_cb_pending_list; + +/** + * @brief Flag indicating if pending node list has been initialized + */ +static bool fm_cb_pending_list_init = FALSE; + +/** + * @brief Pointer to current flash operation requester's callback + */ +static void (*fm_running_cb)(FM_FlashOp_Status_t); + +/** + * @brief Type of current flash operation (Write/Erase) + */ +static FM_FlashOp_t fm_flashop; + +/** + * @brief Parameters for Flash operation + */ +static FM_FlashOpConfig_t fm_flashop_parameters; + +/** + * @brief State of the Background process + */ +static FM_BackGround_States_t FM_CurrentBackGroundState; + +/* Private function prototypes -----------------------------------------------*/ + +static FM_Cmd_Status_t FM_CheckFlashManagerState(FM_CallbackNode_t *CallbackNode); +static void FM_WindowAllowed_Callback(void); + +/* Functions Definition ------------------------------------------------------*/ + +/** + * @brief Request the Flash Manager module to initiate a Flash Write operation + * @param Src: Address of the data to be stored in FLASH. It shall be 32bits aligned + * @param Dest: Address where the data shall be written. It shall be 128bits aligned + * @param Size: This is the size of data to be written in Flash. + The size is a multiple of 32bits (size = 1 means 32bits) + * @param CallbackNode: Pointer to the callback node for storage in list + * @retval FM_Cmd_Status_t: Status of the Flash Manager module + */ +FM_Cmd_Status_t FM_Write(uint32_t *Src, uint32_t *Dest, int32_t Size, FM_CallbackNode_t *CallbackNode) +{ + FM_Cmd_Status_t status; + + if (((uint32_t)Dest < FLASH_BASE) || ((uint32_t)Dest > (FLASH_BASE + FLASH_BANK_SIZE)) + || (((uint32_t)Dest + Size) > (FLASH_BASE + FLASH_BANK_SIZE))) + { + LOG_ERROR_SYSTEM("\r\nFM_Write - Destination address not part of the flash"); + + /* Destination address not part of the flash */ + return FM_ERROR; + } + + if (((uint32_t) Src & ALIGNMENT_32) || ((uint32_t) Dest & ALIGNMENT_128)) + { + LOG_ERROR_SYSTEM("\r\nFM_Write - Source or destination address not properly aligned"); + + /* Source or destination address not properly aligned */ + return FM_ERROR; + } + + status = FM_CheckFlashManagerState(CallbackNode); + + if (status == FM_OK) + { /* Flash manager is available */ + + /* Save Write parameters */ + fm_flashop_parameters.writeSrc = Src; + fm_flashop_parameters.writeDest = Dest; + fm_flashop_parameters.writeSize = Size; + + fm_flashop = FM_WRITE_OP; + + FM_CurrentBackGroundState = FM_BKGND_NOWINDOW_FLASHOP; + + /* Window request to be executed in background */ + FM_ProcessRequest(); + } + + LOG_INFO_SYSTEM("\r\nFM_Write - Returned value : %d", status); + + return status; +} + +/** + * @brief Request the Flash Manager module to initiate a Flash Erase operation + * @param FirstSect: Index of the first sector to erase + * @param NbrSect: Number of sector to erase + * @param CallbackNode: Pointer to the callback node for storage in list + * @retval FM_Cmd_Status_t: Status of the Flash Manager module + */ +FM_Cmd_Status_t FM_Erase(uint32_t FirstSect, uint32_t NbrSect, FM_CallbackNode_t *CallbackNode) +{ + FM_Cmd_Status_t status; + + if ((FirstSect > FLASH_PAGE_NB) || ((FirstSect + NbrSect) > FLASH_PAGE_NB)) + { + LOG_ERROR_SYSTEM("\r\nFM_Erase - Inconsistent request"); + + /* Inconsistent request */ + return FM_ERROR; + } + + if (NbrSect == 0) + { + LOG_ERROR_SYSTEM("\r\nFM_Erase - Inconsistent request"); + + /* Inconsistent request */ + return FM_ERROR; + } + + status = FM_CheckFlashManagerState(CallbackNode); + + if (status == FM_OK) + { /* Flash manager is available */ + + /* Save Erase parameters */ + fm_flashop_parameters.eraseFirstSect = FirstSect; + fm_flashop_parameters.eraseNbrSect = NbrSect; + + fm_flashop = FM_ERASE_OP; + + FM_CurrentBackGroundState = FM_BKGND_NOWINDOW_FLASHOP; + + /* Window request to be executed in background */ + FM_ProcessRequest(); + } + + LOG_INFO_SYSTEM("\r\nFM_Erase - Returned value : %d", status); + + return status; +} + +/** + * @brief Execute Flash Manager background tasks + * @param None + * @retval None + */ +void FM_BackgroundProcess (void) +{ + static uint32_t duration; + bool flashop_complete = false; + FD_FlashOp_Status_t fdReturnValue = FD_FLASHOP_SUCCESS; + FM_CallbackNode_t *pCbNode = NULL; + + switch (FM_CurrentBackGroundState) + { + case FM_BKGND_NOWINDOW_FLASHOP: + { + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_NOWINDOW_FLASHOP"); + + if (fm_flashop == FM_WRITE_OP) + { + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_NOWINDOW_FLASHOP - Write operation"); + + /* Update duration time value */ + duration = TIME_WINDOW_WRITE_REQUEST; + + /* Set the next possible state - App could stop at anytime no window operation */ + FM_CurrentBackGroundState = FM_BKGND_WINDOWED_FLASHOP; + + HAL_FLASH_Unlock(); + + while((fm_flashop_parameters.writeSize > 0) && + (fdReturnValue == FD_FLASHOP_SUCCESS)) + { + fdReturnValue = FD_WriteData((uint32_t) fm_flashop_parameters.writeDest, + (uint32_t) fm_flashop_parameters.writeSrc); + + if (fdReturnValue == FD_FLASHOP_SUCCESS) + { + fm_flashop_parameters.writeDest += FLASH_WRITE_BLOCK_SIZE; + fm_flashop_parameters.writeSrc += FLASH_WRITE_BLOCK_SIZE; + fm_flashop_parameters.writeSize -= FLASH_WRITE_BLOCK_SIZE; + } + } + + HAL_FLASH_Lock(); + + /* Is write over ? */ + if (fm_flashop_parameters.writeSize <= 0) + { + flashop_complete = true; + } + } + else + { + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_NOWINDOW_FLASHOP - Erase operation"); + + /* Update duration time value */ + duration = TIME_WINDOW_ERASE_REQUEST; + + /* Set the next possible state */ + FM_CurrentBackGroundState = FM_BKGND_WINDOWED_FLASHOP; + + HAL_FLASH_Unlock(); + + while((fm_flashop_parameters.eraseNbrSect > 0) && + (fdReturnValue == FD_FLASHOP_SUCCESS)) + { + fdReturnValue = FD_EraseSectors(fm_flashop_parameters.eraseFirstSect); + + if (fdReturnValue == FD_FLASHOP_SUCCESS) + { + fm_flashop_parameters.eraseNbrSect--; + fm_flashop_parameters.eraseFirstSect++; + } + } + + HAL_FLASH_Lock(); + + if (fm_flashop_parameters.eraseNbrSect == 0) + { + flashop_complete = true; + } + } + break; + } + + case FM_BKGND_WINDOWED_FLASHOP: + { + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_WINDOWED_FLASHOP"); + + if (fm_window_granted == false) + { + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_WINDOWED_FLASHOP - No time window granted yet, request one"); + + /* No time window granted yet, request one */ + RFTS_ReqWindow(duration, &FM_WindowAllowed_Callback); + } + else + { + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_WINDOWED_FLASHOP - Time window granted"); + + if (fm_flashop == FM_WRITE_OP) + { + /* Flash Write operation */ + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_WINDOWED_FLASHOP - Write operation"); + + HAL_FLASH_Unlock(); + + while((fm_flashop_parameters.writeSize > 0) && + (FD_WriteData((uint32_t) fm_flashop_parameters.writeDest, + (uint32_t) fm_flashop_parameters.writeSrc) == FD_FLASHOP_SUCCESS)) + { + fm_flashop_parameters.writeDest += FLASH_WRITE_BLOCK_SIZE; + fm_flashop_parameters.writeSrc += FLASH_WRITE_BLOCK_SIZE; + fm_flashop_parameters.writeSize -= FLASH_WRITE_BLOCK_SIZE; + } + + if (fm_flashop_parameters.writeSize <= 0) + { + flashop_complete = true; + } + + HAL_FLASH_Lock(); + + } + else + { + /* Flash Erase operation */ + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Case FM_BKGND_WINDOWED_FLASHOP - Erase operation"); + + HAL_FLASH_Unlock(); + + /* Erase only one sector in a single time window */ + if (FD_EraseSectors(fm_flashop_parameters.eraseFirstSect) == FD_FLASHOP_SUCCESS) + { + fm_flashop_parameters.eraseNbrSect--; + fm_flashop_parameters.eraseFirstSect++; + } + + if (fm_flashop_parameters.eraseNbrSect == 0) + { + flashop_complete = true; + } + + HAL_FLASH_Lock(); + } + + /* Release the time window */ + RFTS_RelWindow(); + + /* Indicate that there is no more window */ + fm_window_granted = false; + } + + break; + } + + default: + { + /* Nothing to do here */ + break; + } + } + + if (flashop_complete == true) + { + UTILS_ENTER_CRITICAL_SECTION(); + + /* Release semaphore on flash */ + busy_flash_sem = false; + + /* Set Flash Manager busy */ + flash_manager_busy = false; + + UTILS_EXIT_CRITICAL_SECTION(); + + /* Invoke the running callback if present */ + if (fm_running_cb != NULL) + { + fm_running_cb(FM_OPERATION_COMPLETE); + } + + /* notify pending requesters */ + while((LST_is_empty (&fm_cb_pending_list) == false) && + (busy_flash_sem == false) && (flash_manager_busy == false)) + { + LST_remove_head (&fm_cb_pending_list, (tListNode**)&pCbNode); + pCbNode->Callback(FM_OPERATION_AVAILABLE); + } + } + else + { + /* Flash operation not complete yet */ + LOG_INFO_SYSTEM("\r\nFM_BackgroundProcess - Flash operation not complete yet, request a new time window"); + + /* Request a new time window */ + RFTS_ReqWindow(duration, &FM_WindowAllowed_Callback); + } +} + +/** + * @brief Check if the Flash Manager is busy or available + * @param CallbackNode: Pointer to the callback node for storage in list + * @retval FM_Cmd_Status_t: Status of the Flash Manager module + */ +static FM_Cmd_Status_t FM_CheckFlashManagerState(FM_CallbackNode_t *CallbackNode) +{ + bool fm_process_cmd = false; + FM_Cmd_Status_t status = FM_ERROR; + + /* Check if semaphore on flash is available */ + UTILS_ENTER_CRITICAL_SECTION(); + + /* Initialize pending list if not done */ + if (fm_cb_pending_list_init == false) + { + LST_init_head(&fm_cb_pending_list); + fm_cb_pending_list_init = true; + } + /* Check if semaphore on flash is available */ + if (busy_flash_sem == false) + { /* Check if Flash Manager is already busy */ + if (flash_manager_busy == false) + { + busy_flash_sem = true; /* Get semaphore on flash */ + flash_manager_busy = true; /* Set Flash Manager busy */ + fm_process_cmd = true; + } + else + { + fm_process_cmd = false; + } + } + else + { + fm_process_cmd = false; + } + UTILS_EXIT_CRITICAL_SECTION(); + + if (fm_process_cmd == false) + { /* Flash manager busy */ + + /* Append callback to the pending list */ + if ((CallbackNode != NULL) && (CallbackNode->Callback != NULL)) + { + LST_insert_tail(&fm_cb_pending_list, &(CallbackNode->NodeList)); + } + + status = FM_BUSY; + } + else + { /* Flash manager is available */ + + if ((CallbackNode != NULL) && (CallbackNode->Callback != NULL)) + { + UTILS_ENTER_CRITICAL_SECTION(); + + fm_running_cb = CallbackNode->Callback; + + UTILS_EXIT_CRITICAL_SECTION(); + } + else + { + UTILS_ENTER_CRITICAL_SECTION(); + + fm_running_cb = NULL; + + UTILS_EXIT_CRITICAL_SECTION(); + } + + status = FM_OK; + } + return status; +} + +/** + * @brief Callback called by RF Timing Synchro module when a time window is available + * @param None + * @retval None + */ +static void FM_WindowAllowed_Callback(void) +{ + fm_window_granted = true; + + LOG_INFO_SYSTEM("\r\nFM_WindowAllowed_Callback"); + + /* Flash operation to be executed in background */ + FM_ProcessRequest(); +} diff --git a/lib/stm32wba/hci/flash_manager.h b/lib/stm32wba/hci/flash_manager.h new file mode 100644 index 000000000..4f8b58b8b --- /dev/null +++ b/lib/stm32wba/hci/flash_manager.h @@ -0,0 +1,80 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file flash_manager.h + * @author MCD Application Team + * @brief Header for flash_manager.c module + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef FLASH_MANAGER_H +#define FLASH_MANAGER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "utilities_common.h" +#include "stm_list.h" + +/* Exported types ------------------------------------------------------------*/ + +/* Flash Manager command status */ +typedef enum +{ + FM_OK, /* The Flash Manager is available and a window request is scheduled */ + FM_BUSY, /* The Flash Manager is busy and the caller will be called back when it is available */ + FM_ERROR /* An error occurred while processing the command */ +} FM_Cmd_Status_t; + +/* Flash operation status */ +typedef enum +{ + FM_OPERATION_COMPLETE, /* The requested flash operation is complete */ + FM_OPERATION_AVAILABLE /* A flash operation can be requested */ +} FM_FlashOp_Status_t; + +/** + * @brief Flash Manager callback node type to store them in a chained list + */ +typedef struct FM_CallbackNode +{ + tListNode NodeList; /* Next and previous nodes in the list */ + void (*Callback)(FM_FlashOp_Status_t Status); /* Callback function pointer for Flash Manager caller */ +}FM_CallbackNode_t; + +/* Exported constants --------------------------------------------------------*/ + +#define TIME_WINDOW_ERASE_DURATION 4000U /* Duration in us of the time window requested for Flash Erase */ +#define TIME_WINDOW_WRITE_DURATION 1000U /* Duration in us of the time window requested for Flash Write */ +#define TIME_WINDOW_MARGIN 100U /* Time margin added so to ensure timeout protection before actual closure */ + /* As timers use ms, amount below 1000 is removed at conversion */ +#define TIME_WINDOW_ERASE_REQUEST (TIME_WINDOW_ERASE_DURATION + TIME_WINDOW_MARGIN) +#define TIME_WINDOW_WRITE_REQUEST (TIME_WINDOW_WRITE_DURATION + TIME_WINDOW_MARGIN) + +/* Exported variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +FM_Cmd_Status_t FM_Write(uint32_t *Src, uint32_t *Dest, int32_t Size, FM_CallbackNode_t *CallbackNode); +FM_Cmd_Status_t FM_Erase(uint32_t FirstSect, uint32_t NbrSect, FM_CallbackNode_t *CallbackNode); +void FM_BackgroundProcess (void); +void FM_ProcessRequest (void); + +#ifdef __cplusplus +} +#endif + +#endif /*FLASH_MANAGER_H */ diff --git a/lib/stm32wba/hci/rf_timing_synchro.c b/lib/stm32wba/hci/rf_timing_synchro.c new file mode 100644 index 000000000..264d990df --- /dev/null +++ b/lib/stm32wba/hci/rf_timing_synchro.c @@ -0,0 +1,205 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file rf_timing_synchro.c + * @author MCD Application Team + * @brief The RF Timing Synchronization module provides an interface to + * synchronize the flash processing versus the RF activity to make + * sure the RF timing is not broken + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include + +#include "rf_timing_synchro.h" +#include "evnt_schdlr_gnrc_if.h" +#include "utilities_conf.h" +#include "stm32_timer.h" +#include "flash_driver.h" + +/* Global variables ----------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ +/* Private defines -----------------------------------------------------------*/ +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ + +/** + * @brief Pointer to time window requester's callback + */ +static void (*req_callback)(void); + +/** + * @brief Indicates if a time window has already been requested or not + */ +static bool rfts_window_req_pending = FALSE; + +/** + * @brief Timer used by the RFTS module to prevent time window overrun + */ +static UTIL_TIMER_Object_t rfts_timer; + +/** + * @brief Firmware Link Layer external event handler + */ +static ext_evnt_hndl_t ext_event_handler; + +/* Private function prototypes -----------------------------------------------*/ + +static void RFTS_WindowAllowed_Callback(void); +static void RFTS_Timeout_Callback(void* Argument); +static uint32_t event_started_callback(ext_evnt_hndl_t evnt_hndl, uint32_t slot_durn, void* priv_data_ptr); + +/* Functions Definition ------------------------------------------------------*/ + +/** + * @brief Request a time window to the Firmware Link Layer + * @param duration: Duration in us of the time window requested + * @param Callback: Callback to be called when time window is allocated + * @retval RFTS_Cmd_Status_t: Success or failure of the window request + */ +RFTS_Cmd_Status_t RFTS_ReqWindow(uint32_t Duration, void (*Callback)(void)) +{ + extrnl_evnt_st_t extrnl_evnt_config; + bool req_pending = false; + + if (Callback == NULL) + { /* Prevent use of uninitialized callback */ + return RFTS_WINDOW_REQ_FAILED; + } + + /* Check no request is already pending */ + UTILS_ENTER_CRITICAL_SECTION(); + if (rfts_window_req_pending == true) + { + req_pending = true; + } + else + { + rfts_window_req_pending = true; + } + UTILS_EXIT_CRITICAL_SECTION(); + if (req_pending == true) + { /* A window request is already pending */ + return RFTS_WINDOW_REQ_FAILED; + } + + /* Register requester's callback */ + req_callback = Callback; + + /* Submit request to Firmware Link Layer */ + extrnl_evnt_config.deadline = 0; + extrnl_evnt_config.strt_min = 0; + extrnl_evnt_config.strt_max = 0; + extrnl_evnt_config.durn_min = Duration; + extrnl_evnt_config.durn_max = 0; + extrnl_evnt_config.prdc_intrvl = 0; + extrnl_evnt_config.priority = PRIORITY_DEFAULT; + extrnl_evnt_config.blocked = STATE_NOT_BLOCKED; + extrnl_evnt_config.ptr_priv = NULL; + extrnl_evnt_config.evnt_strtd_cbk = &event_started_callback; + extrnl_evnt_config.evnt_blckd_cbk = NULL; + extrnl_evnt_config.evnt_abortd_cbk = NULL; + + UTIL_TIMER_Create(&rfts_timer, + (Duration/1000), + UTIL_TIMER_ONESHOT, + &RFTS_Timeout_Callback, + NULL); + + ext_event_handler = evnt_schdlr_rgstr_gnrc_evnt(&extrnl_evnt_config); + if (ext_event_handler == NULL) + { + UTILS_ENTER_CRITICAL_SECTION(); + rfts_window_req_pending = false; + UTILS_EXIT_CRITICAL_SECTION(); + + return RFTS_WINDOW_REQ_FAILED; + } + + return RFTS_CMD_OK; +} + +/** + * @brief Execute necessary tasks to allow the time window to be released + * @param None + * @retval RFTS_Cmd_Status_t: Success or error in the window release procedure + */ +RFTS_Cmd_Status_t RFTS_RelWindow(void) +{ + RFTS_Cmd_Status_t status; + + /* Stop RFTS module window overrun control timer */ + UTIL_TIMER_Stop(&rfts_timer); + + /* Inform Firmware Link Layer that time window can be released */ + if (evnt_schdlr_gnrc_evnt_cmplt(ext_event_handler) == 0) + { + status = RFTS_CMD_OK; + } + else + { + status = RFTS_WINDOW_REL_ERROR; + } + + /* Forbid flash operation */ + FD_SetStatus(FD_FLASHACCESS_RFTS, LL_FLASH_DISABLE); + + UTILS_ENTER_CRITICAL_SECTION(); + rfts_window_req_pending = false; + UTILS_EXIT_CRITICAL_SECTION(); + + return status; +} + +/** + * @brief Callback called by Firmware Link Layer when a time window is available + * @note This callback is supposed to be called under interrupt + * @param None + * @retval None + */ +static void RFTS_WindowAllowed_Callback(void) +{ + /* Allow flash operation */ + FD_SetStatus(FD_FLASHACCESS_RFTS, LL_FLASH_ENABLE); + + /* Start timer preventing window overrun */ + UTIL_TIMER_Start(&rfts_timer); + + /* Call back requester to inform time window is available */ + req_callback(); +} + +/** + * @brief Callback triggered by a timeout when the allocated window time is elapsed + * @note This callback is supposed to be called under interrupt + * @param None + * @retval None + */ +static void RFTS_Timeout_Callback(void* Argument) +{ + /* Forbid flash operation */ + FD_SetStatus(FD_FLASHACCESS_RFTS, LL_FLASH_DISABLE); + + UTILS_ENTER_CRITICAL_SECTION(); + rfts_window_req_pending = false; + UTILS_EXIT_CRITICAL_SECTION(); +} + +static uint32_t event_started_callback(ext_evnt_hndl_t evnt_hndl, uint32_t slot_durn, void* priv_data_ptr) +{ + RFTS_WindowAllowed_Callback(); + return 0; +} + diff --git a/lib/stm32wba/hci/rf_timing_synchro.h b/lib/stm32wba/hci/rf_timing_synchro.h new file mode 100644 index 000000000..8fa710ce9 --- /dev/null +++ b/lib/stm32wba/hci/rf_timing_synchro.h @@ -0,0 +1,53 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file rf_timing_synchro.h + * @author MCD Application Team + * @brief Header for rf_timing_synchro.c module + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef RF_TIMING_SYNCHRO_H +#define RF_TIMING_SYNCHRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "utilities_common.h" + +/* Exported types ------------------------------------------------------------*/ + +/* RFTS command status */ +typedef enum +{ + RFTS_CMD_OK, /* The RF Timing synchronization command was successfully executed */ + RFTS_WINDOW_REQ_FAILED, /* The RF Timing synchronization module failed to register the window request */ + RFTS_WINDOW_REL_ERROR /* An error occurred during the window release procedure */ +} RFTS_Cmd_Status_t; + +/* Exported constants --------------------------------------------------------*/ +/* Exported variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions prototypes ---------------------------------------------*/ +RFTS_Cmd_Status_t RFTS_ReqWindow(uint32_t Duration, void (*Callback)(void)); +RFTS_Cmd_Status_t RFTS_RelWindow(void); + +#ifdef __cplusplus +} +#endif + +#endif /*RF_TIMING_SYNCHRO_H */ diff --git a/lib/stm32wba/hci/stm_list.c b/lib/stm32wba/hci/stm_list.c new file mode 100644 index 000000000..dcc68454e --- /dev/null +++ b/lib/stm32wba/hci/stm_list.c @@ -0,0 +1,200 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm_list.c + * @author MCD Application Team + * @brief TCircular Linked List Implementation. + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/****************************************************************************** + * Include Files + ******************************************************************************/ +#include "utilities_common.h" + +#include "stm_list.h" + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ +void LST_init_head (tListNode * listHead) +{ + listHead->next = listHead; + listHead->prev = listHead; +} + +uint8_t LST_is_empty (tListNode * listHead) +{ + uint32_t primask_bit; + uint8_t return_value; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + if(listHead->next == listHead) + { + return_value = TRUE; + } + else + { + return_value = FALSE; + } + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ + + return return_value; +} + +void LST_insert_head (tListNode * listHead, tListNode * node) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + node->next = listHead->next; + node->prev = listHead; + listHead->next = node; + (node->next)->prev = node; + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +void LST_insert_tail (tListNode * listHead, tListNode * node) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + node->next = listHead; + node->prev = listHead->prev; + listHead->prev = node; + (node->prev)->next = node; + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +void LST_remove_node (tListNode * node) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + (node->prev)->next = node->next; + (node->next)->prev = node->prev; + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +void LST_remove_head (tListNode * listHead, tListNode ** node ) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + *node = listHead->next; + LST_remove_node (listHead->next); + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +void LST_remove_tail (tListNode * listHead, tListNode ** node ) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + *node = listHead->prev; + LST_remove_node (listHead->prev); + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +void LST_insert_node_after (tListNode * node, tListNode * ref_node) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + node->next = ref_node->next; + node->prev = ref_node; + ref_node->next = node; + (node->next)->prev = node; + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +void LST_insert_node_before (tListNode * node, tListNode * ref_node) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + node->next = ref_node; + node->prev = ref_node->prev; + ref_node->prev = node; + (node->prev)->next = node; + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +int LST_get_size (tListNode * listHead) +{ + int size = 0; + tListNode * temp; + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + temp = listHead->next; + while (temp != listHead) + { + size++; + temp = temp->next; + } + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ + + return (size); +} + +void LST_get_next_node (tListNode * ref_node, tListNode ** node) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + *node = ref_node->next; + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + +void LST_get_prev_node (tListNode * ref_node, tListNode ** node) +{ + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ + + *node = ref_node->prev; + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ +} + diff --git a/lib/stm32wba/hci/stm_list.h b/lib/stm32wba/hci/stm_list.h new file mode 100644 index 000000000..6cf9b9db2 --- /dev/null +++ b/lib/stm32wba/hci/stm_list.h @@ -0,0 +1,56 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm_list.h + * @author MCD Application Team + * @brief Header file for linked list library. + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +#ifndef STM_LIST_H +#define STM_LIST_H + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_wpan_common.h" + +typedef __PACKED_STRUCT _tListNode { + struct _tListNode * next; + struct _tListNode * prev; +} tListNode; + +void LST_init_head (tListNode * listHead); + +uint8_t LST_is_empty (tListNode * listHead); + +void LST_insert_head (tListNode * listHead, tListNode * node); + +void LST_insert_tail (tListNode * listHead, tListNode * node); + +void LST_remove_node (tListNode * node); + +void LST_remove_head (tListNode * listHead, tListNode ** node ); + +void LST_remove_tail (tListNode * listHead, tListNode ** node ); + +void LST_insert_node_after (tListNode * node, tListNode * ref_node); + +void LST_insert_node_before (tListNode * node, tListNode * ref_node); + +int LST_get_size (tListNode * listHead); + +void LST_get_next_node (tListNode * ref_node, tListNode ** node); + +void LST_get_prev_node (tListNode * ref_node, tListNode ** node); + +#endif /* STM_LIST_H */