2025-02-15 11:05:28 -05:00

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