355 lines
13 KiB
C
355 lines
13 KiB
C
/*******************************************************************************
|
|
File Name:
|
|
m2m_flash.c
|
|
|
|
Summary:
|
|
This module contains the WINC flash interface.
|
|
|
|
Description:
|
|
This module contains the WINC flash interface.
|
|
*******************************************************************************/
|
|
|
|
//DOM-IGNORE-BEGIN
|
|
/*******************************************************************************
|
|
* Copyright (C) 2021 Microchip Technology Inc. and its subsidiaries.
|
|
*
|
|
* Subject to your compliance with these terms, you may use Microchip software
|
|
* and any derivatives exclusively with Microchip products. It is your
|
|
* responsibility to comply with third party license terms applicable to your
|
|
* use of third party software (including open source software) that may
|
|
* accompany Microchip software.
|
|
*
|
|
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
|
|
* EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
|
|
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE.
|
|
*
|
|
* IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
|
|
* INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
|
|
* WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
|
|
* BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
|
|
* FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
|
|
* ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
|
|
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
|
|
*******************************************************************************/
|
|
|
|
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
|
INCLUDES
|
|
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
|
|
#include "m2m_flash.h"
|
|
#include "spi_flash.h"
|
|
#include "spi_flash_map.h"
|
|
#include "nmdrv.h"
|
|
|
|
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
|
TYPEDEFS
|
|
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t address;
|
|
uint32_t size;
|
|
} tstrFlashMapEntry;
|
|
|
|
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
|
GLOBALS
|
|
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
|
|
|
|
static const tstrFlashMapEntry flashMap[] =
|
|
{
|
|
{0 , 0 }, // WINC_FLASH_REGION_RAW,
|
|
{0 , OTA_IMAGE_SIZE }, // WINC_FLASH_REGION_FIRMWARE_ACTIVE,
|
|
{0 , OTA_IMAGE_SIZE }, // WINC_FLASH_REGION_FIRMWARE_INACTIVE,
|
|
{M2M_PLL_FLASH_OFFSET , M2M_PLL_FLASH_SZ }, // WINC_FLASH_REGION_PLL_TABLE,
|
|
{M2M_GAIN_FLASH_OFFSET , M2M_GAIN_FLASH_SZ }, // WINC_FLASH_REGION_GAIN_TABLE,
|
|
{M2M_PLL_FLASH_OFFSET , M2M_PLL_FLASH_SZ+M2M_GAIN_FLASH_SZ}, // WINC_FLASH_REGION_PLL_AND_GAIN_TABLES,
|
|
{M2M_TLS_ROOTCER_FLASH_OFFSET , M2M_TLS_ROOTCER_FLASH_SZ }, // WINC_FLASH_REGION_ROOT_CERTS,
|
|
{M2M_TLS_SERVER_FLASH_OFFSET , M2M_TLS_SERVER_FLASH_SZ }, // WINC_FLASH_REGION_LOCAL_CERTS,
|
|
{M2M_CACHED_CONNS_FLASH_OFFSET , M2M_CACHED_CONNS_FLASH_SZ }, // WINC_FLASH_REGION_CONN_PARAM,
|
|
{0 , M2M_HTTP_MEM_FLASH_SZ }, // WINC_FLASH_REGION_HTTP_FILES,
|
|
};
|
|
|
|
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
|
FUNCTIONS
|
|
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
|
|
|
|
static int8_t winc_flash_compare(uint8_t *pu8Buf, uint32_t u32Offset, uint32_t u32Size)
|
|
{
|
|
int8_t ret = M2M_SUCCESS;
|
|
uint8_t buf[128];
|
|
uint32_t offset = 0;
|
|
|
|
while(offset < u32Size)
|
|
{
|
|
uint32_t chunk_sz = sizeof(buf);
|
|
if(chunk_sz > u32Size - offset)
|
|
chunk_sz = u32Size - offset;
|
|
ret = spi_flash_read(buf, u32Offset + offset, chunk_sz);
|
|
if(ret != M2M_SUCCESS)
|
|
break;
|
|
ret = memcmp(buf, pu8Buf + offset, chunk_sz);
|
|
if(ret != 0)
|
|
break;
|
|
offset += chunk_sz;
|
|
}
|
|
return ret;
|
|
}
|
|
static int8_t winc_flash_write_verify(uint8_t *pu8Buf, uint32_t u32Offset, uint32_t u32Size)
|
|
{
|
|
int8_t ret = M2M_ERR_FAIL;
|
|
uint8_t count = 20;
|
|
|
|
while((ret != M2M_SUCCESS) && (count-- > 0))
|
|
{
|
|
ret = spi_flash_write(pu8Buf, u32Offset, u32Size);
|
|
if(ret == M2M_SUCCESS)
|
|
ret = winc_flash_compare(pu8Buf, u32Offset, u32Size);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Some internal functions for accessing the control structure. */
|
|
static uint8_t crc7(uint8_t crc, const uint8_t *buff, uint16_t len)
|
|
{
|
|
uint8_t reg = crc;
|
|
uint16_t i;
|
|
for(i = 0; i < len; i++)
|
|
{
|
|
uint16_t g;
|
|
for(g = 0; g < 8; g++)
|
|
{
|
|
uint8_t inv = (((buff[i] << g) & 0x80) >> 7) ^ ((reg >> 6) & 1);
|
|
reg = ((reg << 1) & 0x7f) ^ (9 * inv);
|
|
}
|
|
}
|
|
return reg;
|
|
}
|
|
static int8_t verify_control_structure(tstrOtaControlSec *pstrControlSec)
|
|
{
|
|
int8_t s8Ret = M2M_SUCCESS;
|
|
|
|
if(pstrControlSec->u32OtaMagicValue != OTA_MAGIC_VALUE)
|
|
s8Ret = M2M_ERR_FAIL;
|
|
if(pstrControlSec->u32OtaControlSecCrc != crc7(0x7f, (uint8_t *)pstrControlSec, sizeof(tstrOtaControlSec) - 4))
|
|
s8Ret = M2M_ERR_FAIL;
|
|
|
|
return s8Ret;
|
|
}
|
|
static int8_t read_control_structure(tstrOtaControlSec *pstrControlSec)
|
|
{
|
|
int8_t s8Ret;
|
|
|
|
s8Ret = spi_flash_read((uint8_t *)pstrControlSec, M2M_CONTROL_FLASH_OFFSET, sizeof(tstrOtaControlSec));
|
|
if(s8Ret == M2M_SUCCESS)
|
|
s8Ret = verify_control_structure(pstrControlSec);
|
|
|
|
if(s8Ret != M2M_SUCCESS)
|
|
{
|
|
s8Ret = spi_flash_read((uint8_t *)pstrControlSec, M2M_BACKUP_FLASH_OFFSET, sizeof(tstrOtaControlSec));
|
|
if(s8Ret == M2M_SUCCESS)
|
|
s8Ret = verify_control_structure(pstrControlSec);
|
|
}
|
|
|
|
return s8Ret;
|
|
}
|
|
static int8_t update_control_structure(tstrOtaControlSec *pstrControlSec)
|
|
{
|
|
int8_t ret = M2M_ERR_FAIL;
|
|
|
|
ret = spi_flash_erase(M2M_BACKUP_FLASH_OFFSET, M2M_BACKUP_FLASH_SZ);
|
|
if(ret == M2M_SUCCESS)
|
|
{
|
|
pstrControlSec->u32OtaSequenceNumber++;
|
|
pstrControlSec->u32OtaControlSecCrc = crc7(0x7f, (uint8_t *)pstrControlSec, sizeof(tstrOtaControlSec) - 4);
|
|
ret = winc_flash_write_verify((uint8_t *)pstrControlSec, M2M_BACKUP_FLASH_OFFSET, sizeof(tstrOtaControlSec));
|
|
if(ret == M2M_SUCCESS)
|
|
{
|
|
ret = spi_flash_erase(M2M_CONTROL_FLASH_OFFSET, M2M_CONTROL_FLASH_SZ);
|
|
if(ret == M2M_SUCCESS)
|
|
{
|
|
pstrControlSec->u32OtaSequenceNumber++;
|
|
pstrControlSec->u32OtaControlSecCrc = crc7(0x7f, (uint8_t *)pstrControlSec, sizeof(tstrOtaControlSec) - 4);
|
|
ret = winc_flash_write_verify((uint8_t *)pstrControlSec, M2M_CONTROL_FLASH_OFFSET, sizeof(tstrOtaControlSec));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static bool find_flash_section(tenuWincFlashRegion enuRegion, uint32_t *pu32StartAddr, uint32_t *pu32Size)
|
|
{
|
|
/* Ensure the pointers and region are valid. */
|
|
if((NULL == pu32StartAddr) || (NULL == pu32Size) || (enuRegion >= WINC_FLASH_NUM_REGIONS))
|
|
return false;
|
|
|
|
/* For the raw region resolve the full flash space, otherwise lookup
|
|
the region location and size from the flexible flash map. */
|
|
switch(enuRegion)
|
|
{
|
|
case WINC_FLASH_REGION_RAW:
|
|
*pu32StartAddr = 0;
|
|
*pu32Size = spi_flash_get_size() << 17;
|
|
break;
|
|
case WINC_FLASH_REGION_FIRMWARE_ACTIVE:
|
|
case WINC_FLASH_REGION_FIRMWARE_INACTIVE:
|
|
case WINC_FLASH_REGION_HTTP_FILES:
|
|
{
|
|
/* In these cases we need to read the control structure to find the appropriate flash address. */
|
|
tstrOtaControlSec strControl;
|
|
|
|
/* Check the WINC is initialised and not running. */
|
|
if (NM_STATE_INIT != nm_get_state())
|
|
return false;
|
|
|
|
/* Read control structure from flash. */
|
|
if (M2M_SUCCESS != read_control_structure(&strControl))
|
|
return false;
|
|
|
|
if (WINC_FLASH_REGION_FIRMWARE_INACTIVE == enuRegion)
|
|
*pu32StartAddr = strControl.u32OtaRollbackImageOffset;
|
|
else if (WINC_FLASH_REGION_FIRMWARE_ACTIVE == enuRegion)
|
|
*pu32StartAddr = strControl.u32OtaCurrentWorkingImagOffset;
|
|
else if (WINC_FLASH_REGION_HTTP_FILES == enuRegion)
|
|
*pu32StartAddr = strControl.u32OtaCurrentWorkingImagOffset + (M2M_HTTP_MEM_FLASH_OFFSET - M2M_OTA_IMAGE1_OFFSET);
|
|
|
|
*pu32Size = flashMap[enuRegion].size;
|
|
break;
|
|
}
|
|
default:
|
|
*pu32StartAddr = flashMap[enuRegion].address;
|
|
*pu32Size = flashMap[enuRegion].size;
|
|
break;
|
|
}
|
|
|
|
M2M_INFO("Flash lookup %2d: 0x%06" PRIx32 " %0" PRId32 "\r\n", enuRegion, *pu32StartAddr, *pu32Size);
|
|
|
|
return true;
|
|
}
|
|
|
|
int8_t m2m_flash_erase_sector(tenuWincFlashRegion enuRegion, uint8_t u8StartSector, uint8_t u8NumSectors)
|
|
{
|
|
uint32_t flashAddress;
|
|
uint32_t flashRegionSize;
|
|
|
|
/* Check the WINC is initialised and not running. */
|
|
if(NM_STATE_INIT != nm_get_state())
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Check the region is valid. */
|
|
if(enuRegion >= WINC_FLASH_NUM_REGIONS)
|
|
return M2M_ERR_INVALID_ARG;
|
|
|
|
/* Find region address and size. */
|
|
if(false == find_flash_section(enuRegion, &flashAddress, &flashRegionSize))
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Erase is only supported for regions which begin on a sector boundary. */
|
|
if(flashAddress & (FLASH_SECTOR_SZ-1))
|
|
return M2M_ERR_INVALID_ARG;
|
|
|
|
/* Check requested size fits within region size. */
|
|
if((((uint32_t)u8StartSector + u8NumSectors) * FLASH_SECTOR_SZ) > flashRegionSize)
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Find start address of area within requested region. */
|
|
flashAddress += (u8StartSector * FLASH_SECTOR_SZ);
|
|
|
|
/* Erase the requested sectors. */
|
|
if(M2M_SUCCESS != spi_flash_erase(flashAddress, u8NumSectors * FLASH_SECTOR_SZ))
|
|
return M2M_ERR_FAIL;
|
|
|
|
return M2M_SUCCESS;
|
|
}
|
|
|
|
int8_t m2m_flash_write(tenuWincFlashRegion enuRegion, void *pvBuffer, uint32_t u32Offset, uint32_t u32Size)
|
|
{
|
|
uint32_t flashAddress;
|
|
uint32_t flashRegionSize;
|
|
|
|
/* Check the WINC is initialised and not running. */
|
|
if(NM_STATE_INIT != nm_get_state())
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Check the buffer pointer and region are valid. */
|
|
if((NULL == pvBuffer) || (enuRegion >= WINC_FLASH_NUM_REGIONS))
|
|
return M2M_ERR_INVALID_ARG;
|
|
|
|
/* Find region address and size. */
|
|
if(false == find_flash_section(enuRegion, &flashAddress, &flashRegionSize))
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Check requested size fits within region size. Also check for wraparound. */
|
|
if(((u32Offset + u32Size) > flashRegionSize) || ((uint32_t)(u32Offset + u32Size) < u32Offset))
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Find start address of area within requested region. */
|
|
flashAddress += u32Offset;
|
|
|
|
/* Write data to flash. */
|
|
if(M2M_SUCCESS != spi_flash_write(pvBuffer, flashAddress, u32Size))
|
|
return M2M_ERR_FAIL;
|
|
|
|
return M2M_SUCCESS;
|
|
}
|
|
|
|
int8_t m2m_flash_read(tenuWincFlashRegion enuRegion, void *pvBuffer, uint32_t u32Offset, uint32_t u32Size)
|
|
{
|
|
uint32_t flashAddress;
|
|
uint32_t flashRegionSize;
|
|
|
|
/* Check the WINC is initialised and not running. */
|
|
if(NM_STATE_INIT != nm_get_state())
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Check the buffer pointer and region are valid. */
|
|
if((NULL == pvBuffer) || (enuRegion >= WINC_FLASH_NUM_REGIONS))
|
|
return M2M_ERR_INVALID_ARG;
|
|
|
|
/* Find region address and size. */
|
|
if(false == find_flash_section(enuRegion, &flashAddress, &flashRegionSize))
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Check requested size fits within region size. Also check for wraparound. */
|
|
if(((u32Offset + u32Size) > flashRegionSize) || ((uint32_t)(u32Offset + u32Size) < u32Offset))
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Find start address of area within requested region. */
|
|
flashAddress += u32Offset;
|
|
|
|
/* Read data from flash. */
|
|
if(M2M_SUCCESS != spi_flash_read(pvBuffer, flashAddress, u32Size))
|
|
return M2M_ERR_FAIL;
|
|
|
|
return M2M_SUCCESS;
|
|
}
|
|
|
|
int8_t m2m_flash_switch_firmware(void)
|
|
{
|
|
tstrOtaControlSec strControl;
|
|
uint32_t u32Tmp;
|
|
|
|
/* Check the WINC is initialised and not running. */
|
|
if(NM_STATE_INIT != nm_get_state())
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Read control structure from flash. */
|
|
if(M2M_SUCCESS != read_control_structure(&strControl))
|
|
return M2M_ERR_FAIL;
|
|
|
|
/* Switch active and inactive. */
|
|
u32Tmp = strControl.u32OtaRollbackImageOffset;
|
|
strControl.u32OtaRollbackImageOffset = strControl.u32OtaCurrentWorkingImagOffset;
|
|
strControl.u32OtaCurrentWorkingImagOffset = u32Tmp;
|
|
|
|
/* Ensure the inactive image is marked as invalid. This protects m2m_ota_switch_firmware from
|
|
switching to an image whose validity is unknown. Switching remains possible via this API. */
|
|
strControl.u32OtaRollbackImageValidStatus = OTA_STATUS_INVALID;
|
|
|
|
if(M2M_SUCCESS != update_control_structure(&strControl))
|
|
return M2M_ERR_FAIL;
|
|
|
|
return M2M_SUCCESS;
|
|
}
|
|
|
|
//DOM-IGNORE-END
|