pinebuds/services/tota/app_tota_cmd_handler.cpp

516 lines
15 KiB
C++

/**
****************************************************************************************
*
* @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 );