blob: 8c7792a25dd22ef66971d3ca99d8e69debe34619 [file] [log] [blame]
/*
* Copyright 2019-2020 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*!=================================================================================================
\file app_ota_server.c
\brief This is a public source file for the OTA server module
==================================================================================================*/
/*==================================================================================================
Include Files
==================================================================================================*/
/* Application */
#include <openthread/udp.h>
#include <openthread/coap.h>
#include <openthread/instance.h>
#include <openthread/error.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/alarm-micro.h>
#include <openthread/ip6.h>
#include <openthread/random_noncrypto.h>
#include "app_ota.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "network_utils.h"
/*==================================================================================================
Private macros
==================================================================================================*/
#ifndef OTA_SERVER_DEFAULT_PORT
#define OTA_SERVER_DEFAULT_PORT (61630)
#endif
#ifndef gOtaServer_DefaultTransferType_c
#define gOtaServer_DefaultTransferType_c gOtaMulticast_c
#endif
#define gOtaServer_MinDelayForEndRequestMs_c 20000
#define gOtaServer_MaxDelayForEndRequestMs_c 40000
#define gOtaServer_InvalidClientId_c 0xFFFF
#define gOtaServer_MaxOtaImages_c 0x01
#define gOtaServer_DelayForNextRequestMs_c 60000 /* 60 seconds */
#define gOtaServer_ClientSessionExpirationMs_c 30000 /* 30 seconds */
#define gOtaServer_MulticastInterval_c 500 /* 500 miliseconds */
#define gOtaServer_MulticastImgNtfInterval_c 1000 /* 1 second */
#define gOtaServer_MulticastImgNtfRetransmissions_c 4
#define gOtaServer_MulticastBlockRspInterval_c 300
#define gOtaServer_MulticastUpgradeEndDelay_c 1000
#define gOtaServer_MulticastNoOfBlockRsps_c 0
#define gOtaServer_MulticastWindowSize_c 32 /* Must be multiple of 8 */
#define gOtaServer_MulticastWindowRetries_c 0
#define gOtaServer_MulticastAckTimeout_c 300
#define gOtaFileVersionPolicies_Upgrade_c (1<<0)
#define gOtaFileVersionPolicies_Reinstall_c (1<<1)
#define gOtaFileVersionPolicies_Downgrade_c (1<<2)
#define gOtaFileVersionDefaultPolicies_c gOtaFileVersionPolicies_Upgrade_c | \
gOtaFileVersionPolicies_Reinstall_c | \
gOtaFileVersionPolicies_Downgrade_c
/*==================================================================================================
Private type definitions
==================================================================================================*/
/* ota server multicast state: */
typedef enum
{
gOtaServerMulticastState_NotInit_c = 0,
gOtaServerMulticastState_Idle_c,
gOtaServerMulticastState_SendImgNtf_c,
gOtaServerMulticastState_GenBlockReq_c,
gOtaServerMulticastState_WaitForAck_c,
gOtaServerMulticastState_SendUpgradeEnd_c,
gOtaServerMulticastState_ResetMulticast_c
} otaServerMulticastState_t;
/* ota server multicast state: */
typedef enum
{
otaServerClientImageTypeREED = 0x0000,
otaServerClientImageTypeED = 0x0001,
otaServerClientImageTypeLPED = 0x0002
} otaServerClientImageType;
typedef struct otaServerSetup_tag
{
otInstance *pOtInstance;
otUdpSocket *pOtaUdpSrvSocket;
bool_t isActive;
uint8_t fileVersionPolicy;
otaTransferType_t transferType;
uint16_t downloadPort;
/* Multicast parameters */
otaServerMulticastState_t multicastState;
uint8_t ackBitmask[4];
uint32_t currentWindowOffset;
uint8_t multicastNoOfImgNtf;
uint8_t multicastNoOfBlockRsp;
uint8_t multicastNoOfWindowRetries;
uint16_t multicastManufacturerCode;
uint16_t multicastImageType;
uint32_t multicastImageSize;
uint32_t multicastFileVersion;
} otaServerSetup_t;
typedef struct otaServerImageList_tag
{
uint16_t manufCode;
uint16_t imageType;
uint32_t fileSize;
uint32_t imageAddr;
uint32_t fileVersion;
bool_t isValidEntry;
} otaServerImageList_t;
typedef struct otaClientInfo_tag
{
otIp6Address remoteAddr;
otIp6Address sourceAddr;
uint16_t port;
uint32_t timeStamp;
uint32_t dataLen;
uint8_t pData[1];
} otaClientInfo_t;
typedef struct otaClientSessionInfo_tag
{
otIp6Address remoteAddr;
uint32_t timeStamp;
} otaClientSessionInfo_t;
typedef void ( *otaTmrCallback ) ( void *param );
/*==================================================================================================
Private function prototypes
==================================================================================================*/
/* OTA Server Coap command handlers */
static void OtaServer_ClientProcess(otaClientInfo_t *pOtaClientInfo);
static void OtaServer_CmdProcess(otaClientInfo_t *pOtaClientInfo);
static otaStatus_t OtaServer_CmdCheck(uint8_t otaCommand, uint32_t dataLen);
static otaStatus_t OtaServer_QueryImageReqHandler(otaClientInfo_t *pOtaClientInfo);
static otaStatus_t OtaServer_BlockReqHandler(otaClientInfo_t *pOtaClientInfo);
static otaStatus_t OtaServer_UpgradeEndReqHandler(otaClientInfo_t *pOtaClientInfo);
static otaStatus_t OtaServer_ServerDiscoveryHandler(otaClientInfo_t *pOtaClientInfo);
static void OtaServer_CoapCb(void *pCtx, otMessage *pMsg, const otMessageInfo *pMsgInfo);
/* OTA Server Coap commands */
static otaStatus_t OtaServer_SendImageNotifty(otaServer_ImageNotify_t *pImgNtf, otIp6Address *pAddr);
static otaStatus_t OtaServer_CoapSendImageNotify(otaServer_ImageNotify_t *pImageNotify, otIp6Address *pDestAddr);
static otaStatus_t OtaServer_CoapSendRsp(otaClientInfo_t *pOtaClientInfo, uint8_t *pData, uint32_t pDataLen);
static otaStatus_t OtaServer_CoapSendRspWaitAbortData(otaClientInfo_t *pOtaClientInfo, uint8_t status,
uint32_t delayInMs);
/* OTA Server utility functions */
static void OtaServer_SetTimeCallback(otaTmrCallback pFunc, void *pData, uint32_t setTime);
static void OtaServer_StopTimeCallback(void);
static bool_t OtaServer_IsClientValid(uint16_t clientId);
static bool_t OtaServer_RemoveClientFromPercentageInfo(uint16_t clientId);
static void OtaServer_ResetPercentageInfo(void);
static otaStatus_t OtaServer_CheckClientSessionTable(otaClientInfo_t *pOtaClientInfo);
static otaStatus_t OtaServer_HandleBlockSocket(bool_t onOff);
/* OTA Server standalone functions: */
static void OtaServer_InitStandaloneOpMode(void);
static uint8_t OtaServerStandalone_ValidateImage(uint16_t manufCode, uint16_t imageType, uint32_t fileVersion, bool_t serialProtocol);
static uint8_t OtaServerStandalone_KeepImageInfo(uint16_t manufCode, uint16_t imageType, uint32_t fileVersion, uint32_t fileSize);
static otaStatus_t OtaServerStandalone_QueryImageReqHandler(otaClientInfo_t *pOtaClientInfo);
static otaStatus_t OtaServerStandalone_BlockReqHandler(otaClientInfo_t *pOtaClientInfo);
static otaStatus_t OtaServerStandalone_ServerDiscoveryHandler(otaClientInfo_t *pOtaClientInfo);
/* OTA Server Multicast */
static otaStatus_t OtaServer_InitMulticast(void *pParam);
static void OtaServer_MulticastMngr(void *pParam);
static void OtaServer_MulticastTimeoutCb(void *pParam);
static otaStatus_t OtaServer_SendImgNtf(void *pParam);
static otaStatus_t OtaServer_ProcessAckTimeout(void *pParam);
static otaStatus_t OtaServer_MulticastUpgradeEnd(void *pParam);
static otaStatus_t OtaServer_GenerateBlockReq(void *pParam);
static void OtaServer_ResetMulticastModule(void *pParam);
/*==================================================================================================
Private global variables declarations
==================================================================================================*/
/* Ota server setup parameters */
static otaServerSetup_t mOtaServerSetup = {.pOtaUdpSrvSocket = NULL,
.downloadPort = OTA_SERVER_DEFAULT_PORT,
.multicastState = gOtaServerMulticastState_NotInit_c,
.transferType = gOtaServer_DefaultTransferType_c
};
/* Ota server standalone informations: */
static otaServerImageList_t mOtaServerImageList[gOtaServer_MaxOtaImages_c];
static uint32_t mOtaServerTempImageIdx = gOtaServer_MaxOtaImages_c;
/* Ota server percentages information */
static otaServerPercentageInfo_t mOtaServerPercentageInformation;
static otaClientSessionInfo_t mOtaClientSessionInfoTable[gOtaServer_MaxSimultaneousClients_c];
static otUdpSocket mOtaUdpSrvSocket;
static char binary_file_path[255];
/*==================================================================================================
Public global variables declarations
==================================================================================================*/
otCoapResource gOTA_CLIENT_URI_PATH = {.mUriPath = OTA_CLIENT_URI_PATH, .mHandler = NULL/*OtaServer_CoapCb*/, .mContext = NULL, .mNext = NULL };
otCoapResource gOTA_SERVER_URI_PATH = {.mUriPath = OTA_SERVER_URI_PATH, .mHandler = NULL/*OtaClient_CoapCb*/, .mContext = NULL, .mNext = NULL };
/* Parameters for simple timer callback */
uint32_t setMilliTime = 0;
otaTmrCallback gpFunction = NULL;
void *gpParameter = NULL;
bool_t callbackIsSet = false;
/*==================================================================================================
Public functions
==================================================================================================*/
/*!*************************************************************************************************
\fn void OtaServer_CheckTime(void)
\brief This function is used to check if a timer callback for OTA needs to be called.
***************************************************************************************************/
void OtaServer_CheckTime(void)
{
if((otPlatAlarmMilliGetNow() > setMilliTime) && (callbackIsSet == true) && (setMilliTime != 0))
{
setMilliTime = 0;
if(gpFunction != NULL)
{
callbackIsSet = false;
gpFunction(gpParameter);
}
}
}
/*!*************************************************************************************************
\fn otaStatus_t OtaServerInit(taskMsgQueue_t *pMsgQueue)
\brief Initialize OTA server application.
\param [in] pMsgQueue Pointer to task message queue
\return otaStatus_t Status of the operation
***************************************************************************************************/
otaStatus_t OtaServerInit
(
otInstance *pOtInstance
)
{
otaStatus_t otaStatus = gOtaStatus_Success_c;
if (pOtInstance == NULL)
{
otaStatus = gOtaStatus_InvalidInstance_c;
}
if (otaStatus == gOtaStatus_Success_c)
{
/* Register Services in COAP */
mOtaServerSetup.pOtInstance = pOtInstance;
otCoapStart(mOtaServerSetup.pOtInstance, OT_DEFAULT_COAP_PORT);
gOTA_CLIENT_URI_PATH.mContext = mOtaServerSetup.pOtInstance;
gOTA_CLIENT_URI_PATH.mHandler = OtaServer_CoapCb;
otCoapAddResource(mOtaServerSetup.pOtInstance, &gOTA_CLIENT_URI_PATH);
// Set operation mode to standalone
mOtaServerSetup.isActive = false;
OtaServer_ResetPercentageInfo();
memset(&mOtaClientSessionInfoTable, 0x00,
sizeof(otaClientSessionInfo_t) * gOtaServer_MaxSimultaneousClients_c);
mOtaServerSetup.fileVersionPolicy = gOtaFileVersionDefaultPolicies_c;
memset(&binary_file_path[0], 0x00, sizeof(binary_file_path));
if (true == mOtaServerSetup.isActive)
{
OtaServer_HandleBlockSocket(true);
}
}
return otaStatus;
}
/*!*************************************************************************************************
\public
\fn otaStatus_t OtaServer_StartOta()
\brief Start OTA process
\param [in] otaType Type of OTA process (unicast or multicast)
\param [in] pFilePath Path to binary
\return otaStatus_t Status of the operation
***************************************************************************************************/
otaStatus_t OtaServer_StartOta(uint8_t otaType, const char *pFilePath)
{
otaServer_ImageNotify_t *pImageNotify = NULL;
otaStatus_t status = gOtaStatus_Success_c;
if ((otaType != gOtaUnicast_c) && (otaType != gOtaMulticast_c))
{
status = gOtaStatus_Failed_c;
goto exit;
}
// Check if device is connected before starting OTA
if (otThreadGetDeviceRole(mOtaServerSetup.pOtInstance) < OT_DEVICE_ROLE_CHILD)
{
status = gOtaStatus_NotPermitted_c;
goto exit;
}
// Check if OTA process is already active
if (mOtaServerSetup.isActive == true)
{
status = gOtaStatus_AlreadyStarted_c;
goto exit;
}
if (pFilePath != NULL)
{
memcpy(binary_file_path, pFilePath, strlen(pFilePath));
if (!(access(binary_file_path, F_OK) != -1))
{
status = gOtaStatus_InvalidValue_c;
goto exit;
}
}
else
{
status = gOtaStatus_EmptyEntry_c;
goto exit;
}
// Set multicast addresses used in OTA process
NWKU_OtSetMulticastAddresses(mOtaServerSetup.pOtInstance);
mOtaServerSetup.transferType = otaType;
OtaServer_ResetPercentageInfo();
mOtaServerPercentageInformation.otaType = otaType;
/* clear current image entries */
for (uint8_t i = 0; i < gOtaServer_MaxOtaImages_c; i++)
{
mOtaServerImageList[i].isValidEntry = false;
}
mOtaServerTempImageIdx = gOtaServer_MaxOtaImages_c;
OtaServer_InitStandaloneOpMode();
if (mOtaServerImageList[mOtaServerTempImageIdx].imageType == otaServerClientImageTypeLPED)
{
if (otaType == gOtaMulticast_c)
{
mOtaServerSetup.transferType = gOtaUnicast_c;
mOtaServerPercentageInformation.otaType = gOtaUnicast_c;
status = gOtaStatus_TransferTypeNotSupported_c;
}
}
// OTA Multicast parameters. Not used for OTA unicast.
if (mOtaServerSetup.transferType == gOtaMulticast_c)
{
// OTA multicast.
pImageNotify = (otaServer_ImageNotify_t *)calloc(1, sizeof(otaServer_ImageNotify_t));
}
OtaServer_SendImageNotifty(pImageNotify, &in6addr_realmlocal_allthreadnodes);
if (mOtaServerSetup.transferType == gOtaMulticast_c)
{
OtaServer_SetTimeCallback(OtaServer_MulticastTimeoutCb, (void *)pImageNotify, 100);
}
exit:
return status;
}
/*!*************************************************************************************************
\fn otaResult_t OtaServer_StopOta(void)
\brief Process Stop OTA command received from an external application.
\return otaStatus_t Result of the operation
***************************************************************************************************/
otaStatus_t OtaServer_StopOta
(
void
)
{
otaStatus_t result = gOtaStatus_NoMem_c;
uint8_t size = sizeof(otaClientInfo_t) - 1 + sizeof(otaCmd_UpgradeEndRsp_t);
otaClientInfo_t *pOtaClientInfo;
pOtaClientInfo = calloc(1, size);
mOtaServerSetup.isActive = false;
/* clear current image entries */
for (uint8_t i = 0; i < gOtaServer_MaxOtaImages_c; i++)
{
mOtaServerImageList[i].isValidEntry = false;
}
mOtaServerTempImageIdx = gOtaServer_MaxOtaImages_c;
if (pOtaClientInfo)
{
otaCmd_QueryImageRsp_t queryRsp = {0};
uint32_t timeInMs = otPlatAlarmMilliGetNow();
uint32_t delayInMs = 0;
queryRsp.commandId = gOtaCmd_UpgradeEndRsp_c;
queryRsp.status = gOtaFileStatus_Abort_c;
memcpy(&pOtaClientInfo->remoteAddr, &in6addr_realmlocal_allthreadnodes, sizeof(otIp6Address));
memcpy(&pOtaClientInfo->sourceAddr, GET_OTA_ADDRESS(mOtaServerSetup.pOtInstance), sizeof(otIp6Address));
pOtaClientInfo->port = OTA_SERVER_DEFAULT_PORT;
pOtaClientInfo->dataLen = size - sizeof(otaClientInfo_t) + 1;
pOtaClientInfo->pData[0] = gOtaCmd_UpgradeEndRsp_c;
memcpy(&queryRsp.data.wait.currentTime, &timeInMs, sizeof(uint32_t));
timeInMs = timeInMs + delayInMs;
memcpy(&queryRsp.data.wait.requestTime, &timeInMs, sizeof(uint32_t));
// Send the Block Req to the OTA Server.
result = OtaServer_CoapSendRsp(pOtaClientInfo, (uint8_t *)&queryRsp, sizeof(queryRsp));
}
OtaServer_ResetMulticastModule(NULL);
return result;
}
/*!*************************************************************************************************
\fn void OtaServer_GetOtaStatus(otaServerPercentageInfo_t *pData)
\brief This function is used to check the status of the OTA transfer.
\param [in] pData Pointer to output structure
***************************************************************************************************/
void OtaServer_GetOtaStatus(otaServerPercentageInfo_t *pData)
{
memcpy(pData, (uint8_t *)&mOtaServerPercentageInformation, sizeof(mOtaServerPercentageInformation));
}
/*==================================================================================================
Private functions
==================================================================================================*/
/*!*************************************************************************************************
\private
\fn static void OtaServer_SetTimeCallback(otaTmrCallback pFunc, void *param, uint32_t setTime)
\brief This function sets a timer callback for OTA functions.
\param [in] pFunc Callback function
\param [in] pData Callback function parameter
\param [in] setTime Set time for callback in milliseconds
***************************************************************************************************/
static void OtaServer_SetTimeCallback
(
otaTmrCallback pFunc,
void *pData,
uint32_t setTime
)
{
if(mOtaServerSetup.isActive == true)
{
if(pFunc != NULL)
{
gpFunction = pFunc;
}
gpParameter = pData;
setMilliTime = otPlatAlarmMilliGetNow() + setTime;
callbackIsSet = true;
}
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_StopTimeCallback(void)
\brief This function stops and clears the timer callback for OTA functions.
***************************************************************************************************/
static void OtaServer_StopTimeCallback(void)
{
gpFunction = NULL;
gpParameter = NULL;
setMilliTime = 0;
callbackIsSet = false;
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_CoapCb(void *pCtx, otMessage *pMsg, const otMessageInfo *pMsgInfo)
\brief This function is the callback function for CoAP message.
\param [in] pCtx Pointer to OpenThread context
\param [in] pMsg Pointer to CoAP message
\param [in] pMsgInfo Pointer to CoAP message information
***************************************************************************************************/
static void OtaServer_CoapCb
(
void *pCtx,
otMessage *pMsg,
const otMessageInfo *pMsgInfo
)
{
uint16_t dataLen = otMessageGetLength(pMsg) - otMessageGetOffset(pMsg);
uint8_t otaCommand = gOtaCmd_Invalid_c;
otMessageRead(pMsg, otMessageGetOffset(pMsg), (void *)&otaCommand, sizeof(otaCommand));
if (gOtaStatus_Success_c == OtaServer_CmdCheck(otaCommand, dataLen))
{
otaClientInfo_t *pOtaClientInfo = (otaClientInfo_t *)calloc(1, sizeof(otaClientInfo_t) + dataLen);
if (NULL != pOtaClientInfo)
{
otIp6Address nullAddr = {0};
/* Save client info params */
otMessageRead(pMsg, otMessageGetOffset(pMsg), (void *)&pOtaClientInfo->pData, dataLen);
pOtaClientInfo->dataLen = dataLen;
memcpy(&pOtaClientInfo->remoteAddr, &pMsgInfo->mPeerAddr, sizeof(otIp6Address));
if (memcmp(&pOtaClientInfo->remoteAddr, &nullAddr, sizeof(otIp6Address)))
{
memcpy(&pOtaClientInfo->sourceAddr, &in6addr_realmlocal_allthreadnodes, sizeof(otIp6Address));
}
pOtaClientInfo->timeStamp = otPlatAlarmMilliGetNow();
OtaServer_ClientProcess(pOtaClientInfo);
}
}
(void)pCtx;
}
/*!*************************************************************************************************
\private
\fn static void OtaClient_UdpServerService(uint8_t *pInData)
\brief This function is the callback function for Ota server socket.
\param [in] pCtx Pointer to OpenThread context
\param [in] pMsg Pointer to CoAP message
\param [in] pMsgInfo Pointer to CoAP message information
***************************************************************************************************/
static void OtaClient_UdpServerService
(
void *pCtx,
otMessage *pMsg,
const otMessageInfo *pMsgInfo
)
{
uint16_t dataLen = otMessageGetLength(pMsg) - otMessageGetOffset(pMsg);
uint8_t otaCommand = gOtaCmd_Invalid_c;
otMessageRead(pMsg, otMessageGetOffset(pMsg), (void *)&otaCommand, sizeof(otaCommand));
if (gOtaCmd_BlockReq_c == otaCommand)
{
otaClientInfo_t *pOtaClientInfo = (otaClientInfo_t *)calloc(1, sizeof(otaClientInfo_t) + dataLen);
if (NULL != pOtaClientInfo)
{
/* Save client info params */
otMessageRead(pMsg, otMessageGetOffset(pMsg), (void *)pOtaClientInfo->pData, dataLen);
memcpy(&pOtaClientInfo->remoteAddr, (void *)&pMsgInfo->mPeerAddr, sizeof(otIp6Address));
memcpy(&pOtaClientInfo->sourceAddr, (void *)&pMsgInfo->mSockAddr, sizeof(otIp6Address));
pOtaClientInfo->port = pMsgInfo->mPeerPort;
pOtaClientInfo->dataLen = dataLen;
pOtaClientInfo->timeStamp = otPlatAlarmMilliGetNow();
OtaServer_ClientProcess(pOtaClientInfo);
}
}
(void) pCtx;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_SendImageNotifty(otaServer_ImageNotify_t *pImgNtf, otIp6Address *pAddr)
\brief This function is used for the transmission of Image Notification commands.
\param [in] pImgNtf Pointer to the otaServer_ImageNotify_t structure
\param [in] pAddr Pointer to peer address
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_SendImageNotifty
(
otaServer_ImageNotify_t *pImgNtf,
otIp6Address *pAddr
)
{
bool_t status = false;
FILE *pFile = fopen(binary_file_path,"rb");
if(pFile != NULL)
{
otaServer_ImageNotify_t imageNotify;
otaFileSubElement_t imageTag;
/* transfer completed */
mOtaServerImageList[mOtaServerTempImageIdx].isValidEntry = true;
mOtaServerSetup.isActive = true;
/* Set position after file header */
fseek(pFile, mOtaServerImageList[mOtaServerTempImageIdx].imageAddr + sizeof(otaFileHeader_t), SEEK_SET);
if(fread((void *)&imageTag, sizeof(otaFileSubElement_t), 1, pFile))
{
// Inform clients that a new image is available
memset(&imageNotify, 0, sizeof(otaServer_ImageNotify_t));
memcpy(&imageNotify.fileVersion, &mOtaServerImageList[mOtaServerTempImageIdx].fileVersion, sizeof(uint32_t));
memcpy(&imageNotify.imageType, &mOtaServerImageList[mOtaServerTempImageIdx].imageType, sizeof(uint16_t));
memcpy(&imageNotify.manufacturerCode, &mOtaServerImageList[mOtaServerTempImageIdx].manufCode, sizeof(uint16_t));
memcpy(&imageNotify.imageSize, &imageTag.length, sizeof(imageNotify.imageSize));
memcpy(&imageNotify.fileSize, &mOtaServerImageList[mOtaServerTempImageIdx].fileSize, sizeof(uint32_t));
}
/* return image notify data for multicast usage */
if(pImgNtf != NULL)
{
memcpy(pImgNtf, &imageNotify, sizeof(otaServer_ImageNotify_t));
}
status = (OtaServer_CoapSendImageNotify(&imageNotify, pAddr) == gOtaStatus_Success_c) ? true : false;
}
if (pFile != NULL)
{
fclose(pFile);
}
return (status == true) ? gOtaStatus_Success_c : gOtaStatus_Failed_c;
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_ClientProcess(uint8_t *param)
\brief This function is used to process ota client commands.
\param [in] pOtaClientInfo Pointer to pOtaClientInfo structure
***************************************************************************************************/
static void OtaServer_ClientProcess
(
otaClientInfo_t *pOtaClientInfo
)
{
bool_t isServerBusy = false;
uint16_t clientId;
if (false == mOtaServerSetup.isActive)
{
/* Server is not active - send back a Rsp command with status no image available */
OtaServer_CoapSendRspWaitAbortData(pOtaClientInfo, gOtaFileStatus_NoImageAvailable_c, 0);
}
else
{
if ((gOtaStatus_Success_c == OtaServer_CheckClientSessionTable(pOtaClientInfo)) &&
(gOtaStatus_Success_c == OtaServer_HandleBlockSocket(true)))
{
if (mOtaServerSetup.transferType == gOtaUnicast_c)
{
clientId = (pOtaClientInfo->remoteAddr.mFields.m8[14] << 8) + pOtaClientInfo->remoteAddr.mFields.m8[15];
if((clientId != gOtaServer_InvalidClientId_c) && OtaServer_IsClientValid(clientId))
{
OtaServer_CmdProcess(pOtaClientInfo);
}
else
{
isServerBusy = true;
}
}
else if (mOtaServerSetup.transferType == gOtaMulticast_c)
{
OtaServer_CmdProcess(pOtaClientInfo);
}
}
else
{
isServerBusy = true;
}
if (isServerBusy)
{
OtaServer_CoapSendRspWaitAbortData(pOtaClientInfo,
gOtaFileStatus_ServerBusy_c,
gOtaServer_DelayForNextRequestMs_c);
}
}
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_CheckClientSessionTable(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to check if the server can process this new client request.
\param [in] pOtaClientInfo Pointer to client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_CheckClientSessionTable
(
otaClientInfo_t *pOtaClientInfo
)
{
otaStatus_t status = gOtaStatus_Failed_c;
uint8_t idxInfoTable;
uint8_t firstExpiredEntry = gOtaServer_MaxSimultaneousClients_c;
for (idxInfoTable = 0; idxInfoTable < gOtaServer_MaxSimultaneousClients_c; idxInfoTable++)
{
if (memcmp(&pOtaClientInfo->remoteAddr, &mOtaClientSessionInfoTable[idxInfoTable].remoteAddr, sizeof(otIp6Address)))
{
mOtaClientSessionInfoTable[idxInfoTable].timeStamp = pOtaClientInfo->timeStamp;
return gOtaStatus_Success_c;
}
if (((mOtaClientSessionInfoTable[idxInfoTable].timeStamp == 0) ||
(mOtaClientSessionInfoTable[idxInfoTable].timeStamp + (gOtaServer_ClientSessionExpirationMs_c) < pOtaClientInfo->timeStamp)) &&
(firstExpiredEntry == gOtaServer_MaxSimultaneousClients_c))
{
firstExpiredEntry = idxInfoTable;
}
}
if (firstExpiredEntry < gOtaServer_MaxSimultaneousClients_c)
{
memcpy(&mOtaClientSessionInfoTable[firstExpiredEntry].remoteAddr, &pOtaClientInfo->remoteAddr, sizeof(otIp6Address));
mOtaClientSessionInfoTable[firstExpiredEntry].timeStamp = pOtaClientInfo->timeStamp;
status = gOtaStatus_Success_c;
}
return status;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_CmdCheck(void *pData, uint32_t dataLen)
\brief This function is used to check if ota client command is valid.
\param [in] pData OTA cmd data
\param [in] dataLen OTA cmd length
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_CmdCheck
(
uint8_t otaCommand,
uint32_t dataLen
)
{
otaStatus_t status = gOtaStatus_Failed_c;
switch (otaCommand)
{
case gOtaCmd_QueryImageReq_c:
if (dataLen == sizeof(otaCmd_QueryImageReq_t))
{
status = gOtaStatus_Success_c;
}
break;
case gOtaCmd_BlockReq_c:
if (dataLen == (sizeof(otaCmd_BlockReq_t)))
{
status = gOtaStatus_Success_c;
}
break;
case gOtaCmd_UpgradeEndReq_c:
if (dataLen == sizeof(otaCmd_UpgradeEndReq_t))
{
status = gOtaStatus_Success_c;
}
break;
case gOtaCmd_ServerDiscovery_c:
if (dataLen == sizeof(otaCmd_ServerDiscovery_t))
{
status = gOtaStatus_Success_c;
}
break;
default:
break;
}
return status;
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_CmdProcess(void *param)
\brief This function is used to process ota client commands.
\param [in] pOtaClientInfo Pointer to pOtaClientInfo structure
***************************************************************************************************/
static void OtaServer_CmdProcess
(
otaClientInfo_t *pOtaClientInfo
)
{
uint8_t otaCommand = *pOtaClientInfo->pData;
switch (otaCommand)
{
case gOtaCmd_QueryImageReq_c:
(void)OtaServer_QueryImageReqHandler(pOtaClientInfo);
break;
case gOtaCmd_BlockReq_c:
(void)OtaServer_BlockReqHandler(pOtaClientInfo);
break;
case gOtaCmd_UpgradeEndReq_c:
(void)OtaServer_UpgradeEndReqHandler(pOtaClientInfo);
break;
case gOtaCmd_ServerDiscovery_c:
(void)OtaServer_ServerDiscoveryHandler(pOtaClientInfo);
break;
default:
free((void *)pOtaClientInfo);
break;
}
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_QueryImageReqHandler(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to process a QueryImageReq command.
\param [in] pOtaClientInfo Pointer to client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_QueryImageReqHandler
(
otaClientInfo_t *pOtaClientInfo
)
{
return OtaServerStandalone_QueryImageReqHandler(pOtaClientInfo);
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_BlockReqHandler(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to process a BlockRequest command.
\param [in] pOtaClientInfo Pointer to client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_BlockReqHandler
(
otaClientInfo_t *pOtaClientInfo
)
{
return OtaServerStandalone_BlockReqHandler(pOtaClientInfo);
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_UpgradeEndReqHandler(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to process an UpdateEndRequest command.
\param [in] pOtaClientInfo Pointer to client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_UpgradeEndReqHandler
(
otaClientInfo_t *pOtaClientInfo
)
{
otaCmd_UpgradeEndRsp_t upgradeRsp = {0};
uint32_t timeInMs = otPlatAlarmMilliGetNow();
/* Get the client status from the received packet */
otaFileStatus_t client_status = (otaFileStatus_t) (*(pOtaClientInfo->pData + 1));
uint16_t clientId = 0;
upgradeRsp.commandId = gOtaCmd_UpgradeEndRsp_c;
upgradeRsp.status = gOtaStatus_Success_c;
memcpy(&upgradeRsp.data.success.currentTime, &timeInMs, sizeof(uint32_t));
timeInMs += otRandomNonCryptoGetUint32InRange(gOtaServer_MinDelayForEndRequestMs_c, gOtaServer_MaxDelayForEndRequestMs_c);
memcpy(&upgradeRsp.data.success.upgradeTime, &timeInMs, sizeof(uint32_t));
if (client_status == gOtaFileStatus_Success_c)
{
(void)OtaServer_CoapSendRsp(pOtaClientInfo, (uint8_t *)&upgradeRsp, sizeof(otaCmd_UpgradeEndRsp_t));
}
NWKU_MemCpyReverseOrder(&clientId, &pOtaClientInfo->remoteAddr.mFields.m8[14], sizeof(uint16_t));
OtaServer_RemoveClientFromPercentageInfo(clientId);
if (client_status != gOtaFileStatus_Success_c)
{
free(pOtaClientInfo);
}
return gOtaStatus_Success_c;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_ServerDiscoveryHandler(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to process a ServerDiscovery command.
\param [in] pOtaClientInfo Pointer to client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_ServerDiscoveryHandler
(
otaClientInfo_t *pOtaClientInfo
)
{
return OtaServerStandalone_ServerDiscoveryHandler(pOtaClientInfo);
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_CoapSendImageNotify(otaServer_ImageNotify_t *pThciImageNotify,
otIp6Address *pDestAddr)
\brief This function is used to send multicast a coap Image notify command to all thread nodes
\param [in] pThciImageNotify Pointer to image notify message
\param [in] pDestAddr Pointer to IPv6 address
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_CoapSendImageNotify
(
otaServer_ImageNotify_t *pImageNotify,
otIp6Address *pDestAddr
)
{
otaStatus_t status = gOtaStatus_Failed_c;
uint16_t fragmentSize = gOtaMaxBlockDataSize_c;
otCoapType coapType = OT_COAP_TYPE_NON_CONFIRMABLE;
otCoapCode coapCode = OT_COAP_CODE_POST;
otError error = OT_ERROR_NONE;
otMessage *pMsg = otCoapNewMessage(mOtaServerSetup.pOtInstance, NULL);
if(pMsg)
{
otMessageInfo messageInfo = {0};
otaServerCmd_ImageNotify_t imageNotify;
otCoapMessageInit(pMsg, coapType, coapCode);
otCoapMessageGenerateToken(pMsg, 4);
/* Complete command */
imageNotify.commandId = gOtaCmd_ImageNotify_c;
imageNotify.transferType = mOtaServerSetup.transferType;
memcpy(&imageNotify.manufacturerCode, &pImageNotify->manufacturerCode,
sizeof(otaServer_ImageNotify_t) - 2);
memcpy(&imageNotify.serverDownloadPort[0], (void *)&mOtaServerSetup.downloadPort, sizeof(uint16_t));
memcpy(&imageNotify.fragmentSize[0], &fragmentSize, sizeof(imageNotify.fragmentSize));
error = otCoapMessageAppendUriPathOptions(pMsg, OTA_SERVER_URI_PATH);
error = otCoapMessageSetPayloadMarker(pMsg);
error = otMessageAppend(pMsg, (const void *)&imageNotify, sizeof(imageNotify));
memset(&messageInfo, 0, sizeof(messageInfo));
messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
memcpy(&messageInfo.mSockAddr, GET_OTA_ADDRESS(mOtaServerSetup.pOtInstance), sizeof(otIp6Address));
memcpy(&messageInfo.mPeerAddr, pDestAddr, sizeof(otIp6Address));
error = otCoapSendRequest(mOtaServerSetup.pOtInstance, pMsg, &messageInfo, NULL, NULL);
if ((error != OT_ERROR_NONE) && (pMsg != NULL))
{
otMessageFree(pMsg);
}
else
{
status = gOtaStatus_Success_c;
}
}
return status;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_CoapSendRsp(otaClientInfo_t *pOtaClientInfo, uint8_t *pData, uint32_t dataLen)
\brief This function is used to send a coap response to a OTA client node.
\param [in] pOtaClientInfo Pointer to client info
\param [in] pData Pointer to data
\param [in] dataLen Payload length
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_CoapSendRsp
(
otaClientInfo_t *pOtaClientInfo,
uint8_t *pData,
uint32_t dataLen
)
{
otaStatus_t status = gOtaStatus_Failed_c;
otCoapType coapType = OT_COAP_TYPE_NON_CONFIRMABLE;
otCoapCode coapCode = OT_COAP_CODE_POST;
otError error = OT_ERROR_NONE;
otMessage *pMsg = otCoapNewMessage(mOtaServerSetup.pOtInstance, NULL);
if(pMsg)
{
otMessageInfo messageInfo = {0};
otCoapMessageInit(pMsg, coapType, coapCode);
otCoapMessageGenerateToken(pMsg, 4);
/* Complete command */
error = otCoapMessageAppendUriPathOptions(pMsg, OTA_SERVER_URI_PATH);
error = otCoapMessageSetPayloadMarker(pMsg);
error = otMessageAppend(pMsg, (const void *)pData, dataLen);
memset(&messageInfo, 0, sizeof(messageInfo));
messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
memcpy(&messageInfo.mSockAddr, GET_OTA_ADDRESS(mOtaServerSetup.pOtInstance), sizeof(otIp6Address));
memcpy(&messageInfo.mPeerAddr, &pOtaClientInfo->remoteAddr, sizeof(otIp6Address));
error = otCoapSendRequest(mOtaServerSetup.pOtInstance, pMsg, &messageInfo, NULL, NULL);
if ((error != OT_ERROR_NONE) && (pMsg != NULL))
{
otMessageFree(pMsg);
}
else
{
status = gOtaStatus_Success_c;
}
}
free((void *)pOtaClientInfo);
return status;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_SocketSendRsp(otaClientInfo_t *pOtaClientInfo, uint8_t *pData, uint32_t dataLen)
\brief This function is used to send a socket response to a OTA client node.
\param [in] pOtaClientInfo Pointer to client info
\param [in] pData Pointer to data
\param [in] dataLen Payload length
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_SocketSendRsp
(
otaClientInfo_t *pOtaClientInfo,
uint8_t *pData,
uint32_t dataLen
)
{
otMessageInfo messageInfo;
otMessage *message = NULL;
otError error = OT_ERROR_NONE;
/* Set remote address and local port */
memset(&messageInfo, 0, sizeof(messageInfo));
messageInfo.mPeerPort = pOtaClientInfo->port;
memcpy(&messageInfo.mPeerAddr, &pOtaClientInfo->remoteAddr, sizeof(otIp6Address));
message = otUdpNewMessage(mOtaServerSetup.pOtInstance, NULL);
error = otMessageAppend(message, (const void *)pData, dataLen);
error = otUdpSend(mOtaServerSetup.pOtaUdpSrvSocket, message, &messageInfo);
if (error != OT_ERROR_NONE && message != NULL)
{
otMessageFree(message);
}
free((void *)pOtaClientInfo);
return gOtaStatus_Success_c;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_CoapSendRspWaitAbortData(otaClientInfo_t *pOtaClientInfo, uint8_t status,
uint32_t delayInMs)
\brief This function is used to send a Query Image Rsp command to the OTA client node using
a status != Success
\param [in] pOtaClientInfo Pointer to client info
\param [in] status Query image response status != Success
\param [in] delayInMs Delay in milliseconds
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_CoapSendRspWaitAbortData
(
otaClientInfo_t *pOtaClientInfo,
uint8_t status,
uint32_t delayInMs
)
{
otaStatus_t result = gOtaStatus_Failed_c;
if (status != gOtaFileStatus_Success_c)
{
/* All busy / abort responses have the same structure as queryRsp*/
otaCmd_QueryImageRsp_t queryRsp = {0};
uint8_t len = 2 + sizeof(otaCmd_QueryImageRspWait_t);
uint8_t otaCommand = *pOtaClientInfo->pData;
uint32_t timeInMs = otPlatAlarmMilliGetNow();
queryRsp.commandId = gOtaCmd_QueryImageRsp_c;
switch (otaCommand)
{
case gOtaCmd_BlockReq_c:
queryRsp.commandId = gOtaCmd_BlockRsp_c;
break;
case gOtaCmd_QueryImageReq_c:
queryRsp.commandId = gOtaCmd_QueryImageRsp_c;
break;
case gOtaCmd_UpgradeEndReq_c:
queryRsp.commandId = gOtaCmd_UpgradeEndRsp_c;
break;
default:
free((void *)pOtaClientInfo);
return gOtaStatus_InvalidParam_c;
}
queryRsp.status = status;
memcpy(&queryRsp.data.wait.currentTime, &timeInMs, sizeof(uint32_t));
timeInMs = timeInMs + delayInMs;
memcpy(&queryRsp.data.wait.requestTime, &timeInMs, sizeof(uint32_t));
if (gOtaStatus_Success_c == OtaServer_CoapSendRsp(pOtaClientInfo, (uint8_t *)&queryRsp, len))
{
result = gOtaStatus_Success_c;
}
}
else
{
free((void *)pOtaClientInfo);
}
return result;
}
/*!*************************************************************************************************
\private
\fn static bool_t OtaServer_IsClientValid(uint16_t clientId)
\brief This function is used to validate client ID and add it to percentage list.
\param [in] clientId Client ID
\return bool_t TRUE - client valid, FALSE - client is in the abort list
***************************************************************************************************/
static bool_t OtaServer_IsClientValid
(
uint16_t clientId
)
{
bool_t result = false;
uint8_t i;
for (i = 0; i < gOtaServer_MaxSimultaneousClients_c; i++)
{
if (mOtaServerPercentageInformation.unicastEntry[i].clientId == clientId)
{
result = true;
break;
}
}
if (result == false)
{
for (i = 0; i < gOtaServer_MaxSimultaneousClients_c; i++)
{
if (mOtaServerPercentageInformation.unicastEntry[i].clientId == gOtaServer_InvalidClientId_c)
{
mOtaServerPercentageInformation.unicastEntry[i].clientId = clientId;
mOtaServerPercentageInformation.unicastEntry[i].percentage = 0;
result = true;
break;
}
}
}
return result;
}
/*!*************************************************************************************************
\private
\fn static bool_t OtaServer_RemoveClientFromPercentageInfo(uint16_t clientId)
\brief This function is used to remove a client ID from percentage information list.
\param [in] clientId Client ID
\return bool_t TRUE - client removed, FALSE - otherwise
***************************************************************************************************/
static bool_t OtaServer_RemoveClientFromPercentageInfo
(
uint16_t clientId
)
{
bool_t result = false;
for (uint8_t i = 0; i < gOtaServer_MaxSimultaneousClients_c; i++)
{
if (mOtaServerPercentageInformation.unicastEntry[i].clientId == clientId)
{
mOtaServerPercentageInformation.unicastEntry[i].clientId = gOtaServer_InvalidClientId_c;
mOtaServerPercentageInformation.unicastEntry[i].percentage = 0;
result = true;
break;
}
}
return result;
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_ResetPercentageInfo(void)
\brief This function is resets the percentage information.
***************************************************************************************************/
static void OtaServer_ResetPercentageInfo
(
void
)
{
mOtaServerPercentageInformation.multicastPercentage = 0;
mOtaServerPercentageInformation.otaType = 0xFF;
for (uint8_t i = 0; i < gOtaServer_MaxSimultaneousClients_c; i++)
{
mOtaServerPercentageInformation.unicastEntry[i].clientId = gOtaServer_InvalidClientId_c;
mOtaServerPercentageInformation.unicastEntry[i].percentage = 0;
}
}
/*************************************************************************************************
*
* OTA Server Standalone functions
*
**************************************************************************************************/
/*!*************************************************************************************************
\private
\fn static void OtaServer_InitStandaloneOpMode(void)
\brief Initialize ota server standalone operation mode.
***************************************************************************************************/
static void OtaServer_InitStandaloneOpMode
(
void
)
{
bool_t imageAvailable = false;
uint8_t index = 0;
otaFileHeader_t otaHeader;
FILE *pFile = NULL;
/* init external memory */
pFile = fopen(binary_file_path,"rb");
/* process OTA header information */
while ((index < gOtaServer_MaxOtaImages_c) && (pFile != NULL))
{
uint32_t fileIdentifier;
/* Read OTA Header */
if (fread((void *)&otaHeader, sizeof(otaFileHeader_t), 1, pFile))
{
memcpy(&fileIdentifier, otaHeader.fileIdentifier, sizeof(fileIdentifier));
if (fileIdentifier == gOtaFileIdentifierNo_c)
{
uint16_t manufCode, imageType;
uint32_t fileVersion, fileSize;
memcpy(&fileVersion, &otaHeader.fileVersion, sizeof(uint32_t));
memcpy(&imageType, &otaHeader.imageType, sizeof(uint16_t));
memcpy(&manufCode, &otaHeader.manufacturerCode, sizeof(uint16_t));
memcpy(&fileSize, &otaHeader.totalImageSize, sizeof(uint32_t));
index = OtaServerStandalone_KeepImageInfo(manufCode, imageType, fileVersion, fileSize);
if (index < gOtaServer_MaxOtaImages_c)
{
mOtaServerTempImageIdx = index;
mOtaServerImageList[index].isValidEntry = true;
imageAvailable = true;
}
}
else
{
break;
}
}
else
{
/* ignore other data */
break;
}
}
if (pFile != NULL)
{
fclose(pFile);
}
mOtaServerSetup.isActive = imageAvailable;
}
/*!*************************************************************************************************
\private
\fn static uint8_t OtaServerStandalone_ValidateImage(uint16_t manufCode, uint16_t imageType,
uint32_t fileVersion, bool_t serialProtocol)
\brief Validate image by checking internal table.
\param [in] manufCode Manufacturer code
[in] imageType Image type
[in] fileVersion File version
[in] serialProtocol Image validation is required for the ota Server serial protocol
\return uint8_t Image index if success, otherwise returns gOtaServer_MaxOtaImages_c
***************************************************************************************************/
static uint8_t OtaServerStandalone_ValidateImage
(
uint16_t manufCode,
uint16_t imageType,
uint32_t fileVersion,
bool_t serialProtocol
)
{
uint8_t result = gOtaServer_MaxOtaImages_c;
/* validate internal list */
for (uint8_t i = 0; i < gOtaServer_MaxOtaImages_c; i++)
{
if ((manufCode == mOtaServerImageList[i].manufCode) &&
(imageType == mOtaServerImageList[i].imageType) &&
(mOtaServerImageList[i].isValidEntry))
{
if (((fileVersion == mOtaServerImageList[i].fileVersion) && (mOtaServerSetup.fileVersionPolicy & gOtaFileVersionPolicies_Reinstall_c)) ||
((fileVersion < mOtaServerImageList[i].fileVersion) && (mOtaServerSetup.fileVersionPolicy & gOtaFileVersionPolicies_Upgrade_c)) ||
((fileVersion > mOtaServerImageList[i].fileVersion) && (mOtaServerSetup.fileVersionPolicy & gOtaFileVersionPolicies_Downgrade_c)) ||
((serialProtocol) && (fileVersion == mOtaServerImageList[i].fileVersion)) || (fileVersion == 0xFFFFFFFF))
{
result = i;
break;
}
}
}
return result;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServerStandalone_QueryImageReqHandler(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to process a QueryImageReq command.
\param [in] pOtaClientInfo Pointer to client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServerStandalone_QueryImageReqHandler
(
otaClientInfo_t *pOtaClientInfo
)
{
otaStatus_t status = gOtaStatus_Failed_c;
uint8_t index = 0;
otaCmd_QueryImageReq_t *pQueryImgReq = (otaCmd_QueryImageReq_t *)pOtaClientInfo->pData;
index = OtaServerStandalone_ValidateImage((uint16_t)(pQueryImgReq->manufacturerCode[0] + (pQueryImgReq->manufacturerCode[1] << 8)),
(uint16_t)(pQueryImgReq->imageType[0] + (pQueryImgReq->imageType[1] << 8)),
(uint32_t)(pQueryImgReq->fileVersion[0] + (pQueryImgReq->fileVersion[1] << 8) +
(pQueryImgReq->fileVersion[2] << 16) + (pQueryImgReq->fileVersion[3] << 24)), false);
if (index < gOtaServer_MaxOtaImages_c)
{
otaCmd_QueryImageRsp_t queryImgRsp = {0};
/* image size */
uint8_t len = sizeof(otaCmd_QueryImageRsp_t) - sizeof(queryImgRsp.data);
queryImgRsp.commandId = gOtaCmd_QueryImageRsp_c;
queryImgRsp.status = gOtaFileStatus_Success_c;
memcpy(&queryImgRsp.data.success.manufacturerCode, &mOtaServerImageList[index].manufCode, sizeof(uint16_t));
memcpy(&queryImgRsp.data.success.fileVersion, &mOtaServerImageList[index].fileVersion, sizeof(uint32_t));
memcpy(&queryImgRsp.data.success.imageType, &mOtaServerImageList[index].imageType, sizeof(uint16_t));
memcpy(&queryImgRsp.data.success.fileSize, &mOtaServerImageList[index].fileSize, sizeof(uint32_t));
memcpy(&queryImgRsp.data.success.serverDownloadPort, &mOtaServerSetup.downloadPort, sizeof(uint16_t));
len += sizeof(otaCmd_QueryImageRspSuccess_t);
(void)OtaServer_CoapSendRsp(pOtaClientInfo, (uint8_t *)&queryImgRsp, len);
status = gOtaStatus_Success_c;
}
else
{
/* packet in progress - send back a Query Image Rsp command with status no image available */
OtaServer_CoapSendRspWaitAbortData(pOtaClientInfo, gOtaFileStatus_NoImageAvailable_c, 0);
status = gOtaStatus_Success_c;
}
return status;
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_AddPercentageInfoPerClient(uint16_t clientId, uint8_t percent)
\brief This function is used to process a BlockRequest command.
\param [in] clientId Client ID
\param [in] percent Unicast OTAP percent done
***************************************************************************************************/
static void OtaServer_AddPercentageInfoPerClient
(
uint16_t clientId,
uint8_t percent
)
{
for (int i = 0; i < gOtaServer_MaxSimultaneousClients_c; i++)
{
if (mOtaServerPercentageInformation.unicastEntry[i].clientId == clientId)
{
mOtaServerPercentageInformation.unicastEntry[i].percentage = percent;
break;
}
}
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServerStandalone_BlockReqHandler(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to process a BlockRequest command.
\param [in] pOtaClientInfo Pointer to client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServerStandalone_BlockReqHandler
(
otaClientInfo_t *pOtaClientInfo
)
{
otaStatus_t status = gOtaStatus_Failed_c;
uint8_t index = gOtaServer_MaxOtaImages_c;
otaCmd_BlockReq_t *pBlockReq = (otaCmd_BlockReq_t *)pOtaClientInfo->pData;
index = OtaServerStandalone_ValidateImage((uint16_t)(pBlockReq->manufacturerCode[0] + (pBlockReq->manufacturerCode[1] << 8)),
(uint16_t)(pBlockReq->imageType[0] + (pBlockReq->imageType[1] << 8)),
(uint32_t)(pBlockReq->fileVersion[0] + (pBlockReq->fileVersion[1] << 8) +
(pBlockReq->fileVersion[2] << 16) + (pBlockReq->fileVersion[3] << 24)), false);
if (index < gOtaServer_MaxOtaImages_c)
{
otaCmd_BlockRsp_t *pBlockRsp = NULL;
uint32_t respLength = 0;
uint32_t len = pBlockReq->maxDataSize;
uint32_t imageOffset = (uint32_t)(pBlockReq->fileOffset[0] + (pBlockReq->fileOffset[1] << 8) +
(pBlockReq->fileOffset[2] << 16) + (pBlockReq->fileOffset[3] << 24));
if ((mOtaServerImageList[index].fileSize - imageOffset) < len)
{
len = mOtaServerImageList[index].fileSize - imageOffset;
}
respLength = 2 + sizeof(otaCmd_BlockRspSuccess_t) - 1 + len;
pBlockRsp = (otaCmd_BlockRsp_t *)calloc(1, respLength);
if (pBlockRsp)
{
uint32_t addr = imageOffset + mOtaServerImageList[index].imageAddr;
FILE *pFile = fopen(binary_file_path,"rb");
fseek(pFile, addr, SEEK_SET);
status = gOtaStatus_Success_c;
pBlockRsp->commandId = gOtaCmd_BlockRsp_c;
pBlockRsp->status = gOtaFileStatus_Success_c;
memcpy(pBlockRsp->data.success.fileVersion, pBlockReq->fileVersion, sizeof(uint32_t));
memcpy(pBlockRsp->data.success.fileOffset, &imageOffset, sizeof(uint32_t));
pBlockRsp->data.success.dataSize = len;
if(fread((void *)&pBlockRsp->data.success.pData[0], len, 1, pFile) == 0)
{
(void)OtaServer_CoapSendRspWaitAbortData(pOtaClientInfo, gOtaFileStatus_Abort_c, 0);
}
else
{
uint16_t clientId = (pOtaClientInfo->remoteAddr.mFields.m8[14] << 8) + pOtaClientInfo->remoteAddr.mFields.m8[15];
uint8_t percent = 0;
OtaServer_SocketSendRsp(pOtaClientInfo, (uint8_t *)pBlockRsp, respLength);
//Calculate percentage of image already sent
percent = (uint8_t)(((imageOffset + len) * 100) / mOtaServerImageList[index].fileSize);
mOtaServerPercentageInformation.otaType = mOtaServerSetup.transferType;
if (mOtaServerSetup.transferType == gOtaMulticast_c)
{
//Percentage of sent packages over multicast OTA
mOtaServerPercentageInformation.multicastPercentage = percent;
}
else
{
//Percentage of sent packages for a unicast client
OtaServer_AddPercentageInfoPerClient(clientId, percent);
}
}
free(pBlockRsp);
if(pFile != NULL)
{
fclose(pFile);
}
}
}
else
{
/* image is not available, abort current session */
status = gOtaStatus_Success_c;
(void)OtaServer_CoapSendRspWaitAbortData(pOtaClientInfo, gOtaFileStatus_Abort_c, 0);
}
return status;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServerStandalone_ServerDiscoveryHandler(otaClientInfo_t *pOtaClientInfo)
\brief This function is used to process a ServerDiscovery command.
\param [in] pOtaClientInfo Pointer to OTA client info
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServerStandalone_ServerDiscoveryHandler
(
otaClientInfo_t *pOtaClientInfo
)
{
otaStatus_t status = gOtaStatus_Success_c;
uint8_t index = gOtaServer_MaxOtaImages_c;
otaCmd_ServerDiscovery_t *pCmdData = (otaCmd_ServerDiscovery_t *)pOtaClientInfo->pData;
index = OtaServerStandalone_ValidateImage((uint16_t)(pCmdData->manufacturerCode[0] + (pCmdData->manufacturerCode[1] << 8)),
(uint16_t)(pCmdData->imageType[0] + (pCmdData->imageType[1] << 8)),
0xFFFFFFFF, false);
if (index < gOtaServer_MaxOtaImages_c)
{
/* send back a unicast image notify command */
status = OtaServer_SendImageNotifty(NULL, &pOtaClientInfo->remoteAddr);
}
free(pOtaClientInfo);
return status;
}
/*!*************************************************************************************************
\private
\fn static uint8_t OtaServerStandalone_KeepImageInfo(uint16_t manufCode, uint16_t imageType,
uint32_t fileVersion, uint32_t fileSize)
\brief This function is used to store image information in local table.
\param [in] manufCode Manufacturer code
[in] imageType Image type
[in] fileVersion File version
[in] imageSize Image size
\return uint8_t Image index if success, otherwise returns gOtaServer_MaxOtaImages_c
***************************************************************************************************/
static uint8_t OtaServerStandalone_KeepImageInfo
(
uint16_t manufCode,
uint16_t imageType,
uint32_t fileVersion,
uint32_t fileSize
)
{
uint8_t result = gOtaServer_MaxOtaImages_c;
uint32_t imageAddrOffset = 0;
for (uint8_t i = 0; i < gOtaServer_MaxOtaImages_c; i++)
{
if (mOtaServerImageList[i].isValidEntry)
{
/* entry is valid, update image offset */
imageAddrOffset += mOtaServerImageList[i].fileSize;
}
else
{
/* entry is free */
mOtaServerImageList[i].fileVersion = fileVersion;
mOtaServerImageList[i].imageAddr = imageAddrOffset;
mOtaServerImageList[i].imageType = imageType;
mOtaServerImageList[i].manufCode = manufCode;
mOtaServerImageList[i].fileSize = fileSize;
mOtaServerImageList[i].isValidEntry = false; /* True when the download is completed */
result = i;
break;
}
}
return result;
}
/*************************************************************************************************
*
* OTA Server Serial Protocol callbacks
*
**************************************************************************************************/
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_HandleBlockSocket(bool_t onOff)
\brief This function is used to handle block sockets.
\param [in] onOff If TRUE, create and bind and if FALSE, close socket
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_HandleBlockSocket
(
bool_t onOff
)
{
otaStatus_t status = gOtaStatus_Success_c;
if (true == onOff)
{
if (mOtaServerSetup.pOtaUdpSrvSocket == NULL)
{
otError error;
otSockAddr portAddr;
/* Set Ota Server local address and local port */
memset(&portAddr, 0, sizeof(portAddr));
portAddr.mPort = OTA_SERVER_DEFAULT_PORT;
mOtaServerSetup.pOtaUdpSrvSocket = &mOtaUdpSrvSocket;
/* Open Ota Server UDP socket */
error = otUdpOpen(mOtaServerSetup.pOtInstance, mOtaServerSetup.pOtaUdpSrvSocket, OtaClient_UdpServerService, NULL);
if (error != OT_ERROR_NONE)
{
status = gOtaStatus_NoUdpSocket_c;
}
if(status == gOtaStatus_Success_c)
{
error = otUdpBind(mOtaServerSetup.pOtaUdpSrvSocket, &portAddr);
if (error != OT_ERROR_NONE)
{
otUdpClose(mOtaServerSetup.pOtaUdpSrvSocket);
mOtaServerSetup.pOtaUdpSrvSocket = NULL;
status = gOtaStatus_Failed_c;
}
}
}
else
{
status = gOtaStatus_Success_c;
}
}
else
{
if (mOtaServerSetup.pOtaUdpSrvSocket != NULL)
{
otUdpClose(mOtaServerSetup.pOtaUdpSrvSocket);
mOtaServerSetup.pOtaUdpSrvSocket = NULL;
}
}
return status;
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_MulticastTimeoutCb(void *pParam)
\brief This function is used for the ota server multicast timer callback.
\param [in] pParam Generic pointer containing information dependent on the multicast state.
***************************************************************************************************/
static void OtaServer_MulticastTimeoutCb
(
void *pParam
)
{
OtaServer_MulticastMngr(pParam);
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_InitMulticast(void *pParam)
\brief This function is used for the initialization of the OTA Multicast mechanism.
\param [in] pParam Pointer to the otaServer_ImageNotify_t structure containing
the Image Notification information required for initialization.
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_InitMulticast
(
void *pParam
)
{
otaServer_ImageNotify_t *pImageNotify;
otaStatus_t status = gOtaStatus_Failed_c;
if (pParam)
{
uint32_t delay;
pImageNotify = (otaServer_ImageNotify_t *)pParam;
memcpy(&mOtaServerSetup.multicastManufacturerCode, pImageNotify->manufacturerCode, sizeof(uint16_t));
memcpy(&mOtaServerSetup.multicastImageType, pImageNotify->imageType, sizeof(uint16_t));
memcpy(&mOtaServerSetup.multicastFileVersion, pImageNotify->fileVersion, sizeof(uint32_t));
memset(&mOtaServerSetup.ackBitmask, 0xFF, sizeof(mOtaServerSetup.ackBitmask));
mOtaServerSetup.currentWindowOffset = 0;
memcpy(&mOtaServerSetup.multicastImageSize, pImageNotify->fileSize, sizeof(mOtaServerSetup.multicastImageSize));
mOtaServerSetup.multicastNoOfImgNtf = gOtaServer_MulticastImgNtfRetransmissions_c;
mOtaServerSetup.multicastNoOfBlockRsp = gOtaServer_MulticastNoOfBlockRsps_c;
mOtaServerSetup.multicastNoOfWindowRetries = gOtaServer_MulticastWindowRetries_c;
if (mOtaServerSetup.multicastNoOfImgNtf)
{
mOtaServerSetup.multicastState = gOtaServerMulticastState_SendImgNtf_c;
delay = gOtaServer_MulticastImgNtfInterval_c;
}
else
{
mOtaServerSetup.multicastState = gOtaServerMulticastState_GenBlockReq_c;
delay = gOtaServer_MulticastInterval_c;
free(pParam);
pParam = NULL;
}
OtaServer_SetTimeCallback(OtaServer_MulticastTimeoutCb, pParam, delay);
status = gOtaStatus_Success_c;
}
return status;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_SendImgNtf(void *pParam)
\brief This function is used for the retransmission of Image Notification commands.
\param [in] pParam Pointer to the otaServer_ImageNotify_t structure containing
the Image Notification information used for the OTA command.
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_SendImgNtf
(
void *pParam
)
{
otaStatus_t status = gOtaStatus_Failed_c;
if (pParam)
{
uint32_t delay;
otaServer_ImageNotify_t *pImageNotify = pParam;
status = OtaServer_CoapSendImageNotify(pImageNotify, &in6addr_realmlocal_allthreadnodes);
if (status == gOtaStatus_Success_c)
{
mOtaServerSetup.multicastNoOfImgNtf--;
}
if (mOtaServerSetup.multicastNoOfImgNtf)
{
delay = gOtaServer_MulticastImgNtfInterval_c;
}
else
{
mOtaServerSetup.multicastState = gOtaServerMulticastState_GenBlockReq_c;
delay = gOtaServer_MulticastInterval_c;
free(pParam);
pParam = NULL;
}
OtaServer_SetTimeCallback(OtaServer_MulticastTimeoutCb, pParam, delay);
status = gOtaStatus_Success_c;
}
return status;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_GenerateBlockReq(void *pParam)
\brief This function is used for generating a Block Request to the local OTA Server
that will result in a OTA Block Response sent as a multicast.
\param [in] pParam Pointer to additional data. Reserved for future enhancements. Not used.
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_GenerateBlockReq
(
void *pParam
)
{
otaStatus_t status = gOtaStatus_Failed_c;
otaClientInfo_t *pOtaClientInfo;
otaCmd_BlockReq_t *pBlockReq;
/* Calculate the size of the Block Req taking into acount the variable length ACK bitfield. */
uint8_t size = sizeof(otaClientInfo_t) - 1 + sizeof(otaCmd_BlockReq_t);
(void)pParam;
pOtaClientInfo = (otaClientInfo_t *)calloc(1, size);
if (NULL == pOtaClientInfo)
{
status = gOtaStatus_NoMem_c;
}
else
{
uint32_t fragIdx, imageOffset;
uint32_t delay = gOtaServer_MulticastBlockRspInterval_c;
memcpy(&pOtaClientInfo->remoteAddr, &in6addr_realmlocal_allthreadnodes, sizeof(otIp6Address));
memcpy(&pOtaClientInfo->sourceAddr, GET_OTA_ADDRESS(mOtaServerSetup.pOtInstance), sizeof(otIp6Address));
pOtaClientInfo->port = OTA_SERVER_DEFAULT_PORT;
pOtaClientInfo->dataLen = size - sizeof(otaClientInfo_t) + 1;
pOtaClientInfo->timeStamp = otPlatAlarmMilliGetNow();
pBlockReq = (otaCmd_BlockReq_t *)pOtaClientInfo->pData;
pBlockReq->commandId = gOtaCmd_BlockReq_c;
memcpy(pBlockReq->manufacturerCode, &mOtaServerSetup.multicastManufacturerCode, sizeof(pBlockReq->manufacturerCode));
memcpy(pBlockReq->imageType, &mOtaServerSetup.multicastImageType, sizeof(pBlockReq->imageType));
memcpy(pBlockReq->fileVersion, &mOtaServerSetup.multicastFileVersion, sizeof(pBlockReq->fileVersion));
fragIdx = NWKU_GetFirstBitValue(mOtaServerSetup.ackBitmask, gOtaServer_MulticastWindowSize_c / 8, true);
imageOffset = mOtaServerSetup.currentWindowOffset + (fragIdx * gOtaMaxBlockDataSize_c);
memcpy(pBlockReq->fileOffset, &imageOffset, sizeof(pBlockReq->fileOffset));
pBlockReq->maxDataSize = gOtaMaxBlockDataSize_c;
/* Send the Block Req to the OTA Server. */
OtaServer_ClientProcess(pOtaClientInfo);
if (mOtaServerSetup.multicastNoOfBlockRsp == 0)
{
if (fragIdx < gOtaServer_MulticastWindowSize_c)
{
/* Last retry. */
NWKU_ClearBit(fragIdx, mOtaServerSetup.ackBitmask);
mOtaServerSetup.multicastNoOfBlockRsp = gOtaServer_MulticastNoOfBlockRsps_c;
/* Get next fragment index */
fragIdx = NWKU_GetFirstBitValue(mOtaServerSetup.ackBitmask, gOtaServer_MulticastWindowSize_c / 8, true);
if (fragIdx > (gOtaServer_MulticastWindowSize_c - 1))
{
/* Last retry of last fragment from current window. */
mOtaServerSetup.multicastState = gOtaServerMulticastState_WaitForAck_c;
delay = gOtaServer_MulticastAckTimeout_c;
}
}
}
else
{
mOtaServerSetup.multicastNoOfBlockRsp--;
}
OtaServer_SetTimeCallback(OtaServer_MulticastTimeoutCb, NULL, delay);
}
return status;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_MulticastUpgradeEnd(void *pParam)
\brief This function is used for sending the Upgrade End Response command at the end of
the OTA multicast process.
\param [in] pParam Pointer to additional data. Reserved for future enhancements. Not used.
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_MulticastUpgradeEnd
(
void *pParam
)
{
(void)pParam;
OtaServer_ResetMulticastModule(NULL);
return gOtaStatus_Success_c;
}
/*!*************************************************************************************************
\private
\fn static otaStatus_t OtaServer_ProcessAckTimeout(void *pParam)
\brief This function is used for determining the next steps after the ACK window closes.
\param [in] pParam Pointer to additional data. Reserved for future enhancements. Not used.
\return otaStatus_t Status of the operation
***************************************************************************************************/
static otaStatus_t OtaServer_ProcessAckTimeout
(
void *pParam
)
{
otaStatus_t status = gOtaStatus_Success_c;
uint32_t fragIdx;
uint32_t delay = gOtaServer_MulticastBlockRspInterval_c;
(void)pParam;
fragIdx = NWKU_GetFirstBitValue(mOtaServerSetup.ackBitmask, gOtaServer_MulticastWindowSize_c / 8, true);
/* Check if there are fragments that require retransmission. */
if ((fragIdx < gOtaServer_MulticastWindowSize_c - 1) && (mOtaServerSetup.multicastNoOfWindowRetries))
{
/* Retransmit current window. */
mOtaServerSetup.multicastNoOfWindowRetries--;
}
else
{
/* Last window? */
if (mOtaServerSetup.currentWindowOffset + (gOtaServer_MulticastWindowSize_c * gOtaMaxBlockDataSize_c) >= mOtaServerSetup.multicastImageSize)
{
/* Move to the next state */
delay = gOtaServer_MulticastUpgradeEndDelay_c;
mOtaServerSetup.multicastState = gOtaServerMulticastState_SendUpgradeEnd_c;
}
else
{
/* Window completed. Move to the next window. */
mOtaServerSetup.currentWindowOffset += (gOtaServer_MulticastWindowSize_c * gOtaMaxBlockDataSize_c);
if (mOtaServerSetup.currentWindowOffset + (gOtaServer_MulticastWindowSize_c * gOtaMaxBlockDataSize_c) <= mOtaServerSetup.multicastImageSize)
{
/* Current window is full */
memset(mOtaServerSetup.ackBitmask, 0xFF, sizeof(mOtaServerSetup.ackBitmask));
}
else
{
/* Calculate the number of fragments remaining */
uint8_t fragsInWindow = (mOtaServerSetup.multicastImageSize - mOtaServerSetup.currentWindowOffset) / gOtaMaxBlockDataSize_c;
if ((mOtaServerSetup.multicastImageSize - mOtaServerSetup.currentWindowOffset) % gOtaMaxBlockDataSize_c != 0)
{
fragsInWindow++;
}
/* Set corresponding bits in ack bitmask. */
for (int i = 0; i < fragsInWindow; i++)
{
NWKU_SetBit(i, mOtaServerSetup.ackBitmask);
}
}
mOtaServerSetup.multicastState = gOtaServerMulticastState_GenBlockReq_c;
/* Debug only */
/* Get fragment index. Should be 0, should not fail. */
fragIdx = NWKU_GetFirstBitValue(mOtaServerSetup.ackBitmask, gOtaServer_MulticastWindowSize_c / 8, true);
}
}
OtaServer_SetTimeCallback(OtaServer_MulticastTimeoutCb, NULL, delay);
return status;
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_ResetMulticastModule(void *pParam)
\brief Reset the OTA Multicast mechanism.
\param [in] pParam Pointer to data
***************************************************************************************************/
static void OtaServer_ResetMulticastModule
(
void *pParam
)
{
if (callbackIsSet == true)
{
mOtaServerSetup.multicastState = gOtaServerMulticastState_ResetMulticast_c;
}
else
{
mOtaServerSetup.multicastState = gOtaServerMulticastState_NotInit_c;
mOtaServerSetup.transferType = gOtaUnicast_c;
OtaServer_ResetPercentageInfo();
OtaServer_StopTimeCallback();
if (pParam)
{
free(pParam);
}
}
}
/*!*************************************************************************************************
\private
\fn static void OtaServer_MulticastMngr(void *pParam)
\brief Ota Multicast state machine.
\param [in] pParam Generic pointer containing state depended information
***************************************************************************************************/
static void OtaServer_MulticastMngr
(
void *pParam
)
{
switch (mOtaServerSetup.multicastState)
{
case gOtaServerMulticastState_NotInit_c:
(void)OtaServer_InitMulticast(pParam);
break;
case gOtaServerMulticastState_SendImgNtf_c:
(void)OtaServer_SendImgNtf(pParam);
break;
case gOtaServerMulticastState_GenBlockReq_c:
(void)OtaServer_GenerateBlockReq(pParam);
break;
case gOtaServerMulticastState_WaitForAck_c:
(void)OtaServer_ProcessAckTimeout(pParam);
break;
case gOtaServerMulticastState_SendUpgradeEnd_c:
(void)OtaServer_MulticastUpgradeEnd(pParam);
break;
case gOtaServerMulticastState_ResetMulticast_c:
OtaServer_ResetMulticastModule(pParam);
break;
case gOtaServerMulticastState_Idle_c:
break;
}
}
/*==================================================================================================
Private debug functions
==================================================================================================*/