/** **************************************************************************************** * * @file app_totac_cmd_handler.c * * @date 24th April 2018 * * @brief The framework of the tota command handler * * Copyright (C) 2017 * * **************************************************************************************** */ #include "string.h" #include "cmsis_os.h" #include "hal_trace.h" #include "hal_timer.h" #include "apps.h" #include "stdbool.h" #include "app_tota.h" #include "app_tota_cmd_handler.h" #include "app_tota_cmd_code.h" //#include "rwapp_config.h" #include "app_spp_tota.h" #include "app_tota_ble.h" #include "app_spp_tota_general_service.h" #include "tota_stream_data_transfer.h" #define APP_TOTA_CMD_HANDLER_WAITING_RSP_TIMEOUT_SUPERVISOR_COUNT 8 /** * @brief waiting response timeout supervision data structure * */ typedef struct { uint16_t entryIndex; /**< The command waiting for the response */ uint16_t msTillTimeout; /**< run-time timeout left milliseconds */ } APP_TOTA_CMD_WAITING_RSP_SUPERVISOR_T; /** * @brief tota command handling environment * */ typedef struct { APP_TOTA_CMD_WAITING_RSP_SUPERVISOR_T waitingRspTimeoutInstance[APP_TOTA_CMD_HANDLER_WAITING_RSP_TIMEOUT_SUPERVISOR_COUNT]; uint32_t lastSysTicks; uint8_t timeoutSupervisorCount; osTimerId supervisor_timer_id; osMutexId mutex; } APP_TOTA_CMD_HANDLER_ENV_T; static APP_TOTA_CMD_HANDLER_ENV_T tota_cmd_handler_env; osMutexDef(app_tota_cmd_handler_mutex); static void app_tota_cmd_handler_rsp_supervision_timer_cb(void const *n); osTimerDef (APP_TOTA_CMD_HANDLER_RSP_SUPERVISION_TIMER, app_tota_cmd_handler_rsp_supervision_timer_cb); static void app_tota_cmd_handler_remove_waiting_rsp_timeout_supervision(uint16_t entryIndex); static void app_tota_cmd_handler_add_waiting_rsp_timeout_supervision(uint16_t entryIndex); /** * @brief Callback function of the waiting response supervisor timer. * */ static void app_tota_cmd_handler_rsp_supervision_timer_cb(void const *n) { uint32_t entryIndex = tota_cmd_handler_env.waitingRspTimeoutInstance[0].entryIndex; app_tota_cmd_handler_remove_waiting_rsp_timeout_supervision(entryIndex); // it means time-out happens before the response is received from the peer device, // trigger the response handler TOTA_COMMAND_PTR_FROM_ENTRY_INDEX(entryIndex)->cmdRspHandler(TOTA_WAITING_RSP_TIMEOUT, NULL, 0); } APP_TOTA_CMD_INSTANCE_T* app_tota_cmd_handler_get_entry_pointer_from_cmd_code(APP_TOTA_CMD_CODE_E cmdCode) { for (uint32_t index = 0; index < ((uint32_t)__tota_handler_table_end-(uint32_t)__tota_handler_table_start)/sizeof(APP_TOTA_CMD_INSTANCE_T);index++) { if (TOTA_COMMAND_PTR_FROM_ENTRY_INDEX(index)->cmdCode == cmdCode) { return TOTA_COMMAND_PTR_FROM_ENTRY_INDEX(index); } } return NULL; } uint16_t app_tota_cmd_handler_get_entry_index_from_cmd_code(APP_TOTA_CMD_CODE_E cmdCode) { for (uint32_t index = 0; index < ((uint32_t)__tota_handler_table_end-(uint32_t)__tota_handler_table_start)/sizeof(APP_TOTA_CMD_INSTANCE_T);index++) { if (TOTA_COMMAND_PTR_FROM_ENTRY_INDEX(index)->cmdCode == cmdCode) { return index; } } return INVALID_TOTA_ENTRY_INDEX; } void app_tota_get_cmd_response_handler(APP_TOTA_CMD_CODE_E funcCode, uint8_t* ptrParam, uint32_t paramLen) { // parameter length check if (paramLen > sizeof(APP_TOTA_CMD_RSP_T)) { return; } if (0 == tota_cmd_handler_env.timeoutSupervisorCount) { return; } APP_TOTA_CMD_RSP_T* rsp = (APP_TOTA_CMD_RSP_T *)ptrParam; uint16_t entryIndex = app_tota_cmd_handler_get_entry_index_from_cmd_code((APP_TOTA_CMD_CODE_E)(rsp->cmdCodeToRsp)); if (INVALID_TOTA_ENTRY_INDEX == entryIndex) { return; } // remove the function code from the time-out supervision chain app_tota_cmd_handler_remove_waiting_rsp_timeout_supervision(entryIndex); APP_TOTA_CMD_INSTANCE_T* ptCmdInstance = TOTA_COMMAND_PTR_FROM_ENTRY_INDEX(entryIndex); // call the response handler if (ptCmdInstance->cmdRspHandler) { ptCmdInstance->cmdRspHandler((APP_TOTA_CMD_RET_STATUS_E)(rsp->cmdRetStatus), rsp->rspData, rsp->rspDataLen); } } /** * @brief Refresh the waiting response supervisor list * */ static void app_tota_cmd_refresh_supervisor_env(void) { // do nothing if no supervisor was added if (tota_cmd_handler_env.timeoutSupervisorCount > 0) { uint32_t currentTicks = GET_CURRENT_TICKS(); uint32_t passedTicks; if (currentTicks >= tota_cmd_handler_env.lastSysTicks) { passedTicks = (currentTicks - tota_cmd_handler_env.lastSysTicks); } else { passedTicks = (hal_sys_timer_get_max() - tota_cmd_handler_env.lastSysTicks + 1) + currentTicks; } uint32_t deltaMs = TICKS_TO_MS(passedTicks); APP_TOTA_CMD_WAITING_RSP_SUPERVISOR_T* pRspSupervisor = &(tota_cmd_handler_env.waitingRspTimeoutInstance[0]); for (uint32_t index = 0;index < tota_cmd_handler_env.timeoutSupervisorCount;index++) { if (pRspSupervisor[index].msTillTimeout > deltaMs) { pRspSupervisor[index].msTillTimeout -= deltaMs; } else { pRspSupervisor[index].msTillTimeout = 0; } } } tota_cmd_handler_env.lastSysTicks = GET_CURRENT_TICKS(); } /** * @brief Remove the time-out supervision of waiting response * * @param entryIndex Entry index of the command table * */ static void app_tota_cmd_handler_remove_waiting_rsp_timeout_supervision(uint16_t entryIndex) { ASSERT(tota_cmd_handler_env.timeoutSupervisorCount > 0, "%s The BLE tota command time-out supervisor is already empty!!!", __FUNCTION__); osMutexWait(tota_cmd_handler_env.mutex, osWaitForever); uint32_t index; for (index = 0;index < tota_cmd_handler_env.timeoutSupervisorCount;index++) { if (tota_cmd_handler_env.waitingRspTimeoutInstance[index].entryIndex == entryIndex) { memcpy(&(tota_cmd_handler_env.waitingRspTimeoutInstance[index]), &(tota_cmd_handler_env.waitingRspTimeoutInstance[index + 1]), (tota_cmd_handler_env.timeoutSupervisorCount - index - 1)*sizeof(APP_TOTA_CMD_WAITING_RSP_SUPERVISOR_T)); break; } } // cannot find it, directly return if (index == tota_cmd_handler_env.timeoutSupervisorCount) { goto exit; } tota_cmd_handler_env.timeoutSupervisorCount--; //TOTA_LOG_DBG(1,"decrease the supervisor timer to %d", tota_cmd_handler_env.timeoutSupervisorCount); if (tota_cmd_handler_env.timeoutSupervisorCount > 0) { // refresh supervisor environment firstly app_tota_cmd_refresh_supervisor_env(); // start timer, the first entry is the most close one osTimerStart(tota_cmd_handler_env.supervisor_timer_id, tota_cmd_handler_env.waitingRspTimeoutInstance[0].msTillTimeout); } else { // no supervisor, directly stop the timer osTimerStop(tota_cmd_handler_env.supervisor_timer_id); } exit: osMutexRelease(tota_cmd_handler_env.mutex); } /** * @brief Add the time-out supervision of waiting response * * @param entryIndex Index of the command entry * */ static void app_tota_cmd_handler_add_waiting_rsp_timeout_supervision(uint16_t entryIndex) { ASSERT(tota_cmd_handler_env.timeoutSupervisorCount < APP_TOTA_CMD_HANDLER_WAITING_RSP_TIMEOUT_SUPERVISOR_COUNT, "%s The tota command response time-out supervisor is full!!!", __FUNCTION__); osMutexWait(tota_cmd_handler_env.mutex, osWaitForever); // refresh supervisor environment firstly app_tota_cmd_refresh_supervisor_env(); APP_TOTA_CMD_INSTANCE_T* pInstance = TOTA_COMMAND_PTR_FROM_ENTRY_INDEX(entryIndex); APP_TOTA_CMD_WAITING_RSP_SUPERVISOR_T waitingRspTimeoutInstance[APP_TOTA_CMD_HANDLER_WAITING_RSP_TIMEOUT_SUPERVISOR_COUNT]; uint32_t index = 0, insertedIndex = 0; for (index = 0;index < tota_cmd_handler_env.timeoutSupervisorCount;index++) { uint32_t msTillTimeout = tota_cmd_handler_env.waitingRspTimeoutInstance[index].msTillTimeout; // in the order of low to high if ((tota_cmd_handler_env.waitingRspTimeoutInstance[index].entryIndex != entryIndex) && (pInstance->timeoutWaitingRspInMs >= msTillTimeout)) { waitingRspTimeoutInstance[insertedIndex++] = tota_cmd_handler_env.waitingRspTimeoutInstance[index]; } else if (pInstance->timeoutWaitingRspInMs < msTillTimeout) { waitingRspTimeoutInstance[insertedIndex].entryIndex = entryIndex; waitingRspTimeoutInstance[insertedIndex].msTillTimeout = pInstance->timeoutWaitingRspInMs; insertedIndex++; } } // biggest one? then put it at the end of the list if (tota_cmd_handler_env.timeoutSupervisorCount == index) { waitingRspTimeoutInstance[insertedIndex].entryIndex = entryIndex; waitingRspTimeoutInstance[insertedIndex].msTillTimeout = pInstance->timeoutWaitingRspInMs; insertedIndex++; } // copy to the global variable memcpy((uint8_t *)&(tota_cmd_handler_env.waitingRspTimeoutInstance), (uint8_t *)&waitingRspTimeoutInstance, insertedIndex*sizeof(APP_TOTA_CMD_WAITING_RSP_SUPERVISOR_T)); tota_cmd_handler_env.timeoutSupervisorCount = insertedIndex; //TOTA_LOG_DBG(1,"increase the supervisor timer to %d", tota_cmd_handler_env.timeoutSupervisorCount); osMutexRelease(tota_cmd_handler_env.mutex); // start timer, the first entry is the most close one osTimerStart(tota_cmd_handler_env.supervisor_timer_id, tota_cmd_handler_env.waitingRspTimeoutInstance[0].msTillTimeout); } /** * @brief Receive the data from the peer device and parse them * * @param ptrData Pointer of the received data * @param dataLength Length of the received data * * @return APP_TOTA_CMD_RET_STATUS_E */ APP_TOTA_CMD_RET_STATUS_E app_tota_cmd_received(uint8_t* ptrData, uint32_t dataLength) { TOTA_LOG_DBG(0,"TOTA Receive data:"); TOTA_LOG_DUMP("0x%02x ", ptrData, dataLength); APP_TOTA_CMD_PAYLOAD_T* pPayload = (APP_TOTA_CMD_PAYLOAD_T *)ptrData; // check command code if (pPayload->cmdCode >= OP_TOTA_COMMAND_COUNT || pPayload->cmdCode < OP_TOTA_STRING) { TOTA_LOG_DBG(1,"[%s]error TOTA_INVALID_CMD",__func__); return TOTA_INVALID_CMD; } // check parameter length if (pPayload->paramLen > sizeof(pPayload->param)) { TOTA_LOG_DBG(1,"[%s]error TOTA_PARAM_LEN_OUT_OF_RANGE",__func__); return TOTA_PARAM_LEN_OUT_OF_RANGE; } APP_TOTA_CMD_INSTANCE_T* pInstance = app_tota_cmd_handler_get_entry_pointer_from_cmd_code((APP_TOTA_CMD_CODE_E)(pPayload->cmdCode)); // execute the command handler pInstance->cmdHandler((APP_TOTA_CMD_CODE_E)(pPayload->cmdCode), pPayload->param, pPayload->paramLen); return TOTA_NO_ERROR; } #if defined(APP_ANC_TEST) #endif static void app_tota_cmd_send(APP_TOTA_TRANSMISSION_PATH_E path, APP_TOTA_CMD_PAYLOAD_T* ptPayLoad) { TOTA_LOG_DBG(1,"Send tota cmd: size=%u", (uint32_t)(&(((APP_TOTA_CMD_PAYLOAD_T *)0)->param)) + ptPayLoad->paramLen); TOTA_LOG_DUMP("%02x ", (uint8_t *)ptPayLoad, (uint32_t)(&(((APP_TOTA_CMD_PAYLOAD_T *)0)->param)) + ptPayLoad->paramLen); app_tota_send_data_via_spp((uint8_t *)ptPayLoad, (uint32_t)(&(((APP_TOTA_CMD_PAYLOAD_T *)0)->param)) + ptPayLoad->paramLen); #if 0 switch (path) { case APP_TOTA_VIA_SPP: app_tota_send_cmd_via_spp((uint8_t *)ptPayLoad, (uint32_t)(&(((APP_TOTA_CMD_PAYLOAD_T *)0)->param)) + ptPayLoad->paramLen); break; #ifdef BLE_TOTA_ENABLED case APP_TOTA_VIA_NOTIFICATION: app_tota_send_notification((uint8_t *)ptPayLoad, (uint32_t)(&(((APP_TOTA_CMD_PAYLOAD_T *)0)->param)) + ptPayLoad->paramLen); break; case APP_TOTA_VIA_INDICATION: app_tota_send_indication((uint8_t *)ptPayLoad, (uint32_t)(&(((APP_TOTA_CMD_PAYLOAD_T *)0)->param)) + ptPayLoad->paramLen); break; #endif case APP_TOTA_GEN_VIA_SPP: app_tota_gen_send_cmd_via_spp((uint8_t *)ptPayLoad, (uint32_t)(&(((APP_TOTA_CMD_PAYLOAD_T *)0)->param)) + ptPayLoad->paramLen); break; default: break; } #endif } /** * @brief Send response to the command request * * @param responsedCmdCode Command code of the responsed command request * @param returnStatus Handling result * @param rspData Pointer of the response data * @param rspDataLen Length of the response data * @param path Path of the data transmission * * @return APP_TOTA_CMD_RET_STATUS_E */ APP_TOTA_CMD_RET_STATUS_E app_tota_send_response_to_command (APP_TOTA_CMD_CODE_E responsedCmdCode, APP_TOTA_CMD_RET_STATUS_E returnStatus, uint8_t* rspData, uint32_t rspDataLen, APP_TOTA_TRANSMISSION_PATH_E path) { TOTA_LOG_DBG(1,"[%s]",__func__); // check responsedCmdCode's validity if (responsedCmdCode >= OP_TOTA_COMMAND_COUNT || responsedCmdCode < OP_TOTA_RESPONSE_TO_CMD) { return TOTA_INVALID_CMD; } APP_TOTA_CMD_PAYLOAD_T payload; APP_TOTA_CMD_RSP_T* pResponse = (APP_TOTA_CMD_RSP_T *)&(payload.param); // check parameter length if (rspDataLen > sizeof(pResponse->rspData)) { return TOTA_PARAM_LEN_OUT_OF_RANGE; } pResponse->cmdCodeToRsp = responsedCmdCode; pResponse->cmdRetStatus = returnStatus; pResponse->rspDataLen = rspDataLen; memcpy(pResponse->rspData, rspData, rspDataLen); payload.paramLen = 3*sizeof(uint16_t) + rspDataLen; payload.cmdCode = OP_TOTA_RESPONSE_TO_CMD; app_tota_cmd_send(path, &payload); return TOTA_NO_ERROR; } /** * @brief Send the tota command to the peer device * * @param cmdCode Command code * @param ptrParam Pointer of the output parameter * @param paramLen Length of the output parameter * @param path Path of the data transmission * * @return APP_TOTA_CMD_RET_STATUS_E */ APP_TOTA_CMD_RET_STATUS_E app_tota_send_command(APP_TOTA_CMD_CODE_E cmdCode, uint8_t* ptrParam, uint32_t paramLen, APP_TOTA_TRANSMISSION_PATH_E path) { // check cmdCode's validity if (cmdCode >= OP_TOTA_COMMAND_COUNT) { TOTA_LOG_DBG(0, "OP_TOTA_COMMAND_COUNT"); return TOTA_INVALID_CMD; } APP_TOTA_CMD_PAYLOAD_T payload; // check parameter length if (paramLen > sizeof(payload.param)) { TOTA_LOG_DBG(0, "TOTA_PARAM_LEN_OUT_OF_RANGE"); return TOTA_PARAM_LEN_OUT_OF_RANGE; } uint16_t entryIndex = app_tota_cmd_handler_get_entry_index_from_cmd_code(cmdCode); if (INVALID_TOTA_ENTRY_INDEX == entryIndex) { TOTA_LOG_DBG(0, "TOTA_INVALID_CMD"); return TOTA_INVALID_CMD; } /* cmd filter */ #if TOTA_ENCODE if ( !is_tota_connected() ) { if (cmdCode > OP_TOTA_CONN_CONFIRM) { TOTA_LOG_DBG(0, "COMMAMD NOT PERMIT. PERMISSION DENIED"); return TOTA_INVALID_CMD; } } else { // may encrypt here } #endif APP_TOTA_CMD_INSTANCE_T* pInstance = TOTA_COMMAND_PTR_FROM_ENTRY_INDEX(entryIndex); // wrap the command payload payload.cmdCode = cmdCode; payload.paramLen = paramLen; memcpy(payload.param, ptrParam, paramLen); // send out the data app_tota_cmd_send(path, &payload); // insert into time-out supervison if (pInstance->isNeedResponse) { app_tota_cmd_handler_add_waiting_rsp_timeout_supervision(entryIndex); } return TOTA_NO_ERROR; } /** * @brief Initialize the tota command handler framework * */ void app_tota_cmd_handler_init(void) { memset((uint8_t *)&tota_cmd_handler_env, 0, sizeof(tota_cmd_handler_env)); tota_cmd_handler_env.supervisor_timer_id = osTimerCreate(osTimer(APP_TOTA_CMD_HANDLER_RSP_SUPERVISION_TIMER), osTimerOnce, NULL); tota_cmd_handler_env.mutex = osMutexCreate((osMutex(app_tota_cmd_handler_mutex))); } TOTA_COMMAND_TO_ADD(OP_TOTA_RESPONSE_TO_CMD, app_tota_get_cmd_response_handler, false, 0, NULL );