376 lines
16 KiB
C
376 lines
16 KiB
C
/**
|
|
******************************************************************************
|
|
* @file LmhpFirmwareManagement.c
|
|
* @author MCD Application Team
|
|
* @brief Implements the LoRa-Alliance Firmware Management protocol
|
|
* Specification: https://resources.lora-alliance.org/technical-specifications/ts006-1-0-0-firmware-management-protocol
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* Copyright (c) 2020(-2021) STMicroelectronics.
|
|
* All rights reserved.
|
|
*
|
|
* This software is licensed under terms that can be found in the LICENSE file
|
|
* in the root directory of this software component.
|
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "platform.h"
|
|
#include "LoRaMac.h"
|
|
#include "LmHandler.h"
|
|
#include "LmhpFirmwareManagement.h"
|
|
#include "fw_update_agent.h"
|
|
#include "mw_log_conf.h" /* needed for MW_LOG */
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
/*!
|
|
* Package current context
|
|
*/
|
|
typedef struct LmhpFirmwareManagementState_s
|
|
{
|
|
bool Initialized;
|
|
bool IsTxPending;
|
|
uint8_t DataBufferMaxSize;
|
|
uint8_t *DataBuffer;
|
|
Version_t fwVersion;
|
|
} LmhpFirmwareManagementState_t;
|
|
|
|
typedef enum LmhpFirmwareManagementMoteCmd_e
|
|
{
|
|
FW_MANAGEMENT_PKG_VERSION_ANS = 0x00,
|
|
FW_MANAGEMENT_DEV_VERSION_ANS = 0x01,
|
|
FW_MANAGEMENT_DEV_REBOOT_TIME_ANS = 0x02,
|
|
FW_MANAGEMENT_DEV_REBOOT_COUNTDOWN_ANS = 0x03,
|
|
FW_MANAGEMENT_DEV_UPGRADE_IMAGE_ANS = 0x04,
|
|
FW_MANAGEMENT_DEV_DELETE_IMAGE_ANS = 0x05,
|
|
} LmhpFirmwareManagementMoteCmd_t;
|
|
|
|
typedef enum LmhpFirmwareManagementSrvCmd_e
|
|
{
|
|
FW_MANAGEMENT_PKG_VERSION_REQ = 0x00,
|
|
FW_MANAGEMENT_DEV_VERSION_REQ = 0x01,
|
|
FW_MANAGEMENT_DEV_REBOOT_TIME_REQ = 0x02,
|
|
FW_MANAGEMENT_DEV_REBOOT_COUNTDOWN_REQ = 0x03,
|
|
FW_MANAGEMENT_DEV_UPGRADE_IMAGE_REQ = 0x04,
|
|
FW_MANAGEMENT_DEV_DELETE_IMAGE_REQ = 0x05,
|
|
} LmhpFirmwareManagementSrvCmd_t;
|
|
|
|
typedef enum LmhpFirmwareManagementUpImageStatus_e
|
|
{
|
|
FW_MANAGEMENT_NO_PRESENT_IMAGE = 0x00,
|
|
FW_MANAGEMENT_CORRUPTED_IMAGE = 0x01,
|
|
FW_MANAGEMENT_INCOMPATIBLE_IMAGE = 0x02,
|
|
FW_MANAGEMENT_VALID_IMAGE = 0x03,
|
|
} LmhpFirmwareManagementUpImageStatus_t;
|
|
/* Private define ------------------------------------------------------------*/
|
|
/*!
|
|
* LoRaWAN Application Layer Remote multicast setup Specification
|
|
*/
|
|
#define FW_MANAGEMENT_PORT 203
|
|
#define FW_MANAGEMENT_ID 4
|
|
#define FW_MANAGEMENT_VERSION 1
|
|
#define HW_VERSION 0x00000000 /* Not yet managed */
|
|
|
|
/* Private macro -------------------------------------------------------------*/
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
/*!
|
|
* Initializes the package with provided parameters
|
|
*
|
|
* \param [in] params Pointer to the package parameters
|
|
* \param [in] dataBuffer Pointer to main application buffer
|
|
* \param [in] dataBufferMaxSize Main application buffer maximum size
|
|
*/
|
|
static void LmhpFirmwareManagementInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
|
|
|
|
/*!
|
|
* Returns the current package initialization status.
|
|
*
|
|
* \retval status Package initialization status
|
|
* [true: Initialized, false: Not initialized]
|
|
*/
|
|
static bool LmhpFirmwareManagementIsInitialized( void );
|
|
|
|
/*!
|
|
* Returns if a package transmission is pending or not.
|
|
*
|
|
* \retval status Package transmission status
|
|
* [true: pending, false: Not pending]
|
|
*/
|
|
static bool LmhpFirmwareManagementIsTxPending( void );
|
|
|
|
/*!
|
|
* Processes the internal package events.
|
|
*/
|
|
static void LmhpFirmwareManagementProcess( void );
|
|
|
|
/*!
|
|
* Processes the MCPS Indication
|
|
*
|
|
* \param [in] mcpsIndication MCPS indication primitive data
|
|
*/
|
|
static void LmhpFirmwareManagementOnMcpsIndication( McpsIndication_t *mcpsIndication );
|
|
|
|
static void OnRebootTimer( void *context );
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
static LmhpFirmwareManagementState_t LmhpFirmwareManagementState =
|
|
{
|
|
.Initialized = false,
|
|
.IsTxPending = false,
|
|
};
|
|
|
|
static LmhPackage_t LmhpFirmwareManagementPackage =
|
|
{
|
|
.Port = FW_MANAGEMENT_PORT,
|
|
.Init = LmhpFirmwareManagementInit,
|
|
.IsInitialized = LmhpFirmwareManagementIsInitialized,
|
|
.IsTxPending = LmhpFirmwareManagementIsTxPending,
|
|
.Process = LmhpFirmwareManagementProcess,
|
|
.OnPackageProcessEvent = NULL, /* To be initialized by LmHandler */
|
|
.OnMcpsConfirmProcess = NULL, /* Not used in this package */
|
|
.OnMcpsIndicationProcess = LmhpFirmwareManagementOnMcpsIndication,
|
|
.OnMlmeConfirmProcess = NULL, /* Not used in this package */
|
|
.OnMlmeIndicationProcess = NULL, /* Not used in this package */
|
|
.OnJoinRequest = NULL, /* To be initialized by LmHandler */
|
|
.OnDeviceTimeRequest = NULL, /* To be initialized by LmHandler */
|
|
.OnSysTimeUpdate = NULL, /* To be initialized by LmHandler */
|
|
#if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 )))
|
|
.OnSystemReset = NULL, /* To be initialized by LmHandler */
|
|
#endif /* LORAMAC_VERSION */
|
|
};
|
|
|
|
/*!
|
|
* Reboot timer
|
|
*/
|
|
static TimerEvent_t RebootTimer;
|
|
|
|
/* Exported functions ---------------------------------------------------------*/
|
|
LmhPackage_t *LmhpFirmwareManagementPackageFactory( void )
|
|
{
|
|
return &LmhpFirmwareManagementPackage;
|
|
}
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
static void LmhpFirmwareManagementInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
|
|
{
|
|
if( dataBuffer != NULL )
|
|
{
|
|
LmhpFirmwareManagementState.DataBuffer = dataBuffer;
|
|
LmhpFirmwareManagementState.DataBufferMaxSize = dataBufferMaxSize;
|
|
LmhpFirmwareManagementState.Initialized = true;
|
|
TimerInit( &RebootTimer, OnRebootTimer );
|
|
}
|
|
else
|
|
{
|
|
LmhpFirmwareManagementState.Initialized = false;
|
|
}
|
|
LmhpFirmwareManagementState.IsTxPending = false;
|
|
LmhpFirmwareManagementState.fwVersion.Value = ( ( Version_t * )params )->Value;
|
|
}
|
|
|
|
static bool LmhpFirmwareManagementIsInitialized( void )
|
|
{
|
|
return LmhpFirmwareManagementState.Initialized;
|
|
}
|
|
|
|
static bool LmhpFirmwareManagementIsTxPending( void )
|
|
{
|
|
return LmhpFirmwareManagementState.IsTxPending;
|
|
}
|
|
|
|
static void LmhpFirmwareManagementProcess( void )
|
|
{
|
|
/* Not yet implemented */
|
|
}
|
|
|
|
static void LmhpFirmwareManagementOnMcpsIndication( McpsIndication_t *mcpsIndication )
|
|
{
|
|
uint8_t cmdIndex = 0;
|
|
uint8_t dataBufferIndex = 0;
|
|
|
|
if( mcpsIndication->Port != FW_MANAGEMENT_PORT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
while( cmdIndex < mcpsIndication->BufferSize )
|
|
{
|
|
switch( mcpsIndication->Buffer[cmdIndex++] )
|
|
{
|
|
case FW_MANAGEMENT_PKG_VERSION_REQ:
|
|
{
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_PKG_VERSION_ANS;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_ID;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_VERSION;
|
|
break;
|
|
}
|
|
case FW_MANAGEMENT_DEV_VERSION_REQ:
|
|
{
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_DEV_VERSION_ANS;
|
|
/* FW Version */
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = LmhpFirmwareManagementState.fwVersion.Fields.Major;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = LmhpFirmwareManagementState.fwVersion.Fields.Minor;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = LmhpFirmwareManagementState.fwVersion.Fields.Patch;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = LmhpFirmwareManagementState.fwVersion.Fields.Revision;
|
|
/* HW Version */
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( HW_VERSION >> 0 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( HW_VERSION >> 8 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( HW_VERSION >> 16 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( HW_VERSION >> 24 ) & 0xFF;
|
|
break;
|
|
}
|
|
case FW_MANAGEMENT_DEV_REBOOT_TIME_REQ:
|
|
{
|
|
uint32_t rebootTimeReq = 0;
|
|
uint32_t rebootTimeAns = 0;
|
|
rebootTimeReq = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
|
|
rebootTimeReq += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
|
|
rebootTimeReq += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
|
|
rebootTimeReq += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
|
|
|
|
if( rebootTimeReq == 0 )
|
|
{
|
|
OnRebootTimer( NULL );
|
|
}
|
|
else if( rebootTimeReq == 0xFFFFFFFF )
|
|
{
|
|
rebootTimeAns = rebootTimeReq;
|
|
TimerStop( &RebootTimer );
|
|
}
|
|
else
|
|
{
|
|
SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 };
|
|
curTime = SysTimeGet();
|
|
|
|
rebootTimeAns = rebootTimeReq - curTime.Seconds;
|
|
if( rebootTimeAns > 0 )
|
|
{
|
|
/* Start session start timer */
|
|
TimerSetValue( &RebootTimer, rebootTimeAns * 1000 );
|
|
TimerStart( &RebootTimer );
|
|
}
|
|
}
|
|
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_DEV_REBOOT_TIME_ANS;
|
|
/* FW Version */
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( rebootTimeAns >> 0 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( rebootTimeAns >> 8 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( rebootTimeAns >> 16 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( rebootTimeAns >> 24 ) & 0xFF;
|
|
|
|
break;
|
|
}
|
|
case FW_MANAGEMENT_DEV_REBOOT_COUNTDOWN_REQ:
|
|
{
|
|
uint32_t rebootCountdown = 0;
|
|
rebootCountdown = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
|
|
rebootCountdown += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
|
|
rebootCountdown += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
|
|
|
|
if( rebootCountdown == 0 )
|
|
{
|
|
OnRebootTimer( NULL );
|
|
}
|
|
else if( rebootCountdown == 0xFFFFFF )
|
|
{
|
|
TimerStop( &RebootTimer );
|
|
}
|
|
else
|
|
{
|
|
if( rebootCountdown > 0 )
|
|
{
|
|
/* Start session start timer */
|
|
TimerSetValue( &RebootTimer, rebootCountdown * 1000 );
|
|
TimerStart( &RebootTimer );
|
|
}
|
|
}
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_DEV_REBOOT_COUNTDOWN_ANS;
|
|
/* FW Version */
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( rebootCountdown >> 0 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( rebootCountdown >> 8 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( rebootCountdown >> 16 ) & 0xFF;
|
|
break;
|
|
}
|
|
case FW_MANAGEMENT_DEV_UPGRADE_IMAGE_REQ:
|
|
{
|
|
uint32_t imageVersion = 0;
|
|
uint8_t imageStatus = FW_MANAGEMENT_NO_PRESENT_IMAGE;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_DEV_UPGRADE_IMAGE_ANS;
|
|
/* No FW present */
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = imageStatus & 0x03;
|
|
|
|
if( imageStatus == FW_MANAGEMENT_VALID_IMAGE )
|
|
{
|
|
/* Next FW version (opt) */
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( imageVersion >> 0 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( imageVersion >> 8 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( imageVersion >> 16 ) & 0xFF;
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = ( imageVersion >> 24 ) & 0xFF;
|
|
}
|
|
break;
|
|
}
|
|
case FW_MANAGEMENT_DEV_DELETE_IMAGE_REQ:
|
|
{
|
|
uint32_t firmwareVersion = 0;
|
|
firmwareVersion = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
|
|
firmwareVersion += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
|
|
firmwareVersion += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
|
|
firmwareVersion += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
|
|
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = FW_MANAGEMENT_DEV_DELETE_IMAGE_ANS;
|
|
/* No valid image present */
|
|
LmhpFirmwareManagementState.DataBuffer[dataBufferIndex++] = 0x01;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( dataBufferIndex != 0 )
|
|
{
|
|
/* Answer commands */
|
|
LmHandlerAppData_t appData =
|
|
{
|
|
.Buffer = LmhpFirmwareManagementState.DataBuffer,
|
|
.BufferSize = dataBufferIndex,
|
|
.Port = FW_MANAGEMENT_PORT
|
|
};
|
|
|
|
bool current_dutycycle;
|
|
LmHandlerGetDutyCycleEnable( ¤t_dutycycle );
|
|
|
|
/* force Duty Cycle OFF to this Send */
|
|
LmHandlerSetDutyCycleEnable( false );
|
|
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, true );
|
|
|
|
/* restore initial Duty Cycle */
|
|
LmHandlerSetDutyCycleEnable( current_dutycycle );
|
|
}
|
|
}
|
|
|
|
static void OnRebootTimer( void *context )
|
|
{
|
|
#if (INTEROP_TEST_MODE == 0)
|
|
/* Do a request to Run the Secure boot - The file is already in flash */
|
|
#if (LORAWAN_PACKAGES_VERSION == 2)
|
|
FwUpdateAgent_Run();
|
|
#endif /* LORAWAN_PACKAGES_VERSION */
|
|
#endif /* INTEROP_TEST_MODE */
|
|
#if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 )))
|
|
if( LmhpFirmwareManagementPackage.OnSystemReset != NULL )
|
|
{
|
|
LmhpFirmwareManagementPackage.OnSystemReset();
|
|
}
|
|
else
|
|
#endif /* LORAMAC_VERSION */
|
|
{
|
|
NVIC_SystemReset();
|
|
}
|
|
}
|